https://stickode.tistory.com/1190
지난 게시글인 [Java][Android] 뷰페이저2 (viewPager2) 구현 + 이미지 순서 표시
에서는 뷰페이저2를 사용하여 이미지를 표시하는 방법에 대해 알아보았습니다.
이번 포스트에서는 뷰페이저2에 무한 스크롤 / 슬라이딩 기능을 적용하여 사용자가 마지막 아이템에 도달했을 때, 다시 첫 번째 페이지로 넘어가도록 만드는 방법을 알아보겠습니다.
일반적으로 뷰페이저는 사용자가 마지막 페이지에 도달하면 더 이상 넘어가지 않지만, 무한 스크롤 기능을 구현하면 페이지가 계속 순환하여 사용자에게 보다 편리한 경험을 제공할 수 있습니다.
우선, 전체 코드입니다. xml파일은 바뀌지 않고, MainActivity.java와 VpAdapter.java 는 바뀌었습니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_order"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:background="#88000000"
android:gravity="center"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@id/view_pager2"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager2"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_gravity="center"
android:layout_marginTop="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
vp_item.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/images"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter" />
</FrameLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
// 이미지 뷰페이저
private VpAdapter vpAdapter;
private ViewPager2 viewPager2;
private TextView tv_order;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager2 = findViewById(R.id.view_pager2);
tv_order = findViewById(R.id.tv_order);
List<String> images = new ArrayList<>();
// 더미 사진을 이용 했습니다. 각자 원하는 사진을 추가 해주시면 됩니다.
images.add(String.valueOf(R.drawable.cat1));
images.add(String.valueOf(R.drawable.cat2));
images.add(String.valueOf(R.drawable.cat3));
images.add(String.valueOf(R.drawable.cat4));
images.add(String.valueOf(R.drawable.cat5));
vpAdapter = new VpAdapter(MainActivity.this ,images);
viewPager2.setAdapter(vpAdapter);
tv_order.setText("1 / " + images.size());
int middle = Integer.MAX_VALUE / 2;
int extra = middle % images.size();
viewPager2.setCurrentItem(middle - extra, false);
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
int realPosition = position % images.size();
String order = (realPosition + 1) + " / " + images.size();
tv_order.setText(order);
}
});
}
}
VpAdapter.java
public class VpAdapter extends RecyclerView.Adapter<VpAdapter.ViewHolder> {
private Context context;
private List<String> items;
public VpAdapter(Context context, List<String> items) {
this.context = context;
this.items = items;
}
@NonNull
@Override
public VpAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.vp_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull VpAdapter.ViewHolder holder, int position) {
int realPosition = position % items.size();
holder.bind(items.get(realPosition));
}
@Override
public int getItemCount() {
if (null != items && !items.isEmpty()) {
if (1 >= items.size()) {
return 1;
} else {
return Integer.MAX_VALUE;
}
} else {
return 1;
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.images);
}
public void bind(String imageResource) {
imageView.setImageResource(Integer.parseInt(imageResource));
}
}
}
우선 VpAdapter.java 의
@Override
public void onBindViewHolder(@NonNull VpAdapter.ViewHolder holder, int position) {
int realPosition = position % items.size();
holder.bind(items.get(realPosition));
}
이부분을 살펴보도록 하겠습니다.
int realPosition = position % items.size() 를 사용하여 실제 데이터를 가져올 위치를 계산합니다.
예를 들어, items에 5장의 사진이 들어있다고 가정해보도록 하겠습니다.
그러면 position 값이 0~4까지는 realPosition에는 그대로 들어가게 됩니다.
만약 position이 5가 되면, ' 5 % 5 = 0 ' 이 되어서, realPosition 이 0이 되어 다시 첫번째 사진을 가리키게 됩니다.
이런 방식으로, position이 아무리 증가해도 realPosition은 실제 사진의 0에서 4까지의 값을 가지게 되어, 무한 스크롤이 구현됩니다.
그 다음으로
@Override
public int getItemCount() {
if (null != items && !items.isEmpty()) {
if (1 >= items.size()) {
return 1;
} else {
return Integer.MAX_VALUE;
}
} else {
return 1;
}
}
어댑터가 관리하는 아이템의 총 갯수를 살펴보도록 하겠습니다.
아이템의 크기가 1보다 큰 경우, Integer.MAX_VALUE 를 반환하도록 했습니다. 아이템의 갯수가 int의 최대값 약 21억의 숫자를 반환하도록 하여, 사실상 무한에 가까운 스크롤을 구현합니다.
다음으로는, MainActivity.java 를 살펴보도록 하겠습니다.
int middle = Integer.MAX_VALUE / 2;
int extra = middle % images.size();
viewPager2.setCurrentItem(middle - extra, false);
이부분을 살펴보면,
middle 값 계산 : Integer.MAX_VALUE / 2 를 계산하여, 중간 위치를 찾습니다. 이 값은 뷰페이저가 처음 시작할 위치를 설정하는데 사용됩니다.
extra 값 계산 : middle 값을 images.size() 로 나눈 나머지값을 계산하여, 중간위치가 실제 이미지 리스트의 시작 위치와 일치하도록 조정하는데 사용됩니다.
middle 에서 extra를 빼서, 뷰페이저의 현재 아이템을 중간 위치에서 시작되도록 설정합니다.
이후에,
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
int realPosition = position % images.size();
String order = (realPosition + 1) + " / " + images.size();
tv_order.setText(order);
}
});
뷰페이저의 위치를 나타내는 TextView를 설정해주면 끝입니다.
뷰페이저2는 내부적으로 리사이클러뷰를 기반으로 구현되어 있습니다.
따라서 뷰 홀더 패턴을 사용하여 뷰를 재사용하기 때문에,
Integer.MAX_VALUE 크기의 아이템을 사용해도 실제 메모리 낭비는 발생하지 않는다는 점을 참고하면 좋을것 같습니다.
작동 화면
'안드로이드 자바' 카테고리의 다른 글
[JAVA][Android] 알라딘 도서 검색 Open API 사용하기 (Retrofit 사용) (2) | 2024.07.17 |
---|---|
[JAVA][Android] BottomSheetDialog 사용하기 (0) | 2024.07.15 |
[JAVA][Android] 알람 앱 구현하기 - (3) AlarmManager 살펴보기 (0) | 2024.07.13 |
[JAVA][Android][PHP]json_encode 안드로이드 전송 / UTF-8 변환 (2) | 2024.07.12 |
[Java][Android] 뷰페이저2 (viewPager2) 구현 + 이미지 순서 표시 (0) | 2024.07.11 |