아래 그림처럼 ViewPager 형태인데 다음 페이지가 살짝 보이는 UI를 만드는 경우가 종종 있다. 이번 포스트에서는 ViewPager2를 이용해 이 화면을 만드는 방법을 다뤄보려고 한다.

 

val currentVisibleItemPx = DimensionUtils.dp2px(requireContext(), 40f).toInt()

margin_pager.addItemDecoration(object: RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        outRect.right = currentVisibleItemPx
        outRect.left = currentVisibleItemPx
    }
})

ViewPager에서 inflate 된 페이지는 부모의 width를 따라가게 되므로 우선 각 페이지가 전체 영역을 잡지 않게 여백을 만들어둔다. 현재 보여진 위치로부터 양 옆에 margin을 추가한다. 그러면 아래 그림과 같은 상태가 된다.

 

 

val nextVisibleItemPx = DimensionUtils.dp2px(requireContext(), 20f).toInt()
val pageTranslationX = nextVisibleItemPx + currentVisibleItemPx

margin_pager.offscreenPageLimit = 1

margin_pager.setPageTransformer { page, position ->
    page.translationX = -pageTranslationX * ( position)
}

 

다음은 이전 페이지와 다음 페이지에 이동 효과를 줘야한다. 먼저 첫번째는 offscreenPageLimit 값을 설정는데 이 속성은 ViewPager2가 스크린에 현재 페이지로부터 얼만큼 떨어져 있는 페이지를 미리 생성 할 것인지 설정하는 함수다. offscreenPageLimit 값이 1이고, 5번 페이지가 현재 위치라면 ViewPager2는 4, 6번 페이지도 미리 생성 해둔다.

 

그 다음은 setPageTransformer 함수를 사용하는 것이다. 이 콜백은 현재 포커싱된 page 뷰 객체를 받을 수 있고 각 page 별로 포커싱 된 페이지로부터 얼마만큼 떨어져 있는지 비율 정보를 position으로 받을 수 있다. 아래 그림에선 현재 페이지가 1번에 포커싱돼 있어서 이전 페이지인 0번 페이지는 -1f만큼 떨어져 있게 되고, 다음 페이지인 2번 페이지는 1f만큼 떨어져 있게 된다. 

 

 

이 정보 값을 이용하면 0번과 2번 페이지를 필요한 만큼 이동시킬 수 있다. translationX 값을 변경하면 0번과 2번 페이지가 움직여서 미리보기 형태로 볼 수 있게 된다.

 

728x90

브런치나 틱톡처럼 수직으로 스와이프 해서 화면을 넘길 수 있는 ViewPager를 만들려고 인터넷에 검색해보면 ViewPager 클래스에서 상속받는 일부 함수들을 변경하는 답변이 많다. 그런데 대부분의 답변으로 시도해본 결과 아래 이미지처럼 스무스하게 스와이프가 되지 않는 문제가 있었다



0. 기존 ViewPager 클래스의 문제점


원인은 화면을 넘기는 부분을 담당하는 코드가 안드로이드 내부 라이브러리인 ViewPager 클래스 내에 있고 이쪽 코드는 수직으로 넘기는 걸 고려하지 않게 구현되어 있기 때문이다. ViewPager에서 일부 변수들을 오버라이드 할 수 있도록 하지 않았을 까 하는 일말의 기대가 있었지만 스와이프시 넘기는 부분의 threshold 값의 역할을 하는 변수는 오버라이드 할 수 없는 private 으로 구현이되어 있었다. 몇몇 답변에서는 super class의 변수를 바꾸는 방법을 제안했는데 이 방식은 구조적으로 좋은 방법도 아니거니와 최신 디바이스에선 통하지도 않는다.


1. ViewPager2 클래스


구글에서는 ViewPager 코드를 바꾸는게 귀찮았는지 ViewPager2라는 새로운 라이브러리를 내놓았다. ViewPager2는 기존 ViewPager 클래스가 가지고 있던 버그들을 잡고 신규 기능을 추가한 새로운 클래스인데 주요 기능으로 세로모드 지원이 있었다. 코드로 적용을 해보니 아래 그림처럼 간단한 스와이프로도 화면이 슉슉 넘어갈 수 있게 됐다. 



2. 구현방법


2.1 ViewPager2 라이브러리 다운 받기 

implementation 'androidx.viewpager2:viewpager2:1.0.0'

build.gradle에서 소스를 추가한다. 예전에는 alpha 태그를 붙였는데 지금은 정식 버전인 1.0.0이 나왔다.


2.2 ViewPager2용 adapter 구현 

public class ViewPager2Adapter extends FragmentStateAdapter {
    private List<string> stringList = new ArrayList<>();

    public ViewPager2Adapter(@NonNull FragmentActivity fragmentActivity, List<string> stringList) {
        super(fragmentActivity);
        this.stringList = stringList;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return MyFragment.newInstance(stringList.get(position));
    }

    @Override
    public int getItemCount() {
        return stringList.size();
    }
}

기존 ViewPager Adapter와 차이가 있는 부분은 상속 클래스인 FragmentStateAdapter 일 것이다. ViewPager2를 만들면서 구글에서 추가한 새로운 어댑터인데 정확히 어떤 차이점이 있는지는 아직 모르겠다.


2.3 XML에서 ViewPager2 소스 추가하기 

<androidx.viewpager2.widget.viewpager2 
android:id="@+id/viewPager" 
android:background="#000000" 
android:layout_width="match_parent" 
android:layout_height="match_parent"
</androidx.viewpager2.widget.viewpager2>

원래 ViewPager가 있던 자리에 ViewPager2로 바꿔준다.


2.4 Adapter 초기화 하고 Viewer에 붙이기

viewPager2 = findViewById(R.id.viewPager);
ViewPager2Adapter adapter = new ViewPager2Adapter(this, stringList);
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
viewPager2.setAdapter(adapter);

만들어둔 어댑터를 ViewPager2 Viewer에 붙여준다. 여기선 수직으로 스와이프 하려고 setOrientation 함수에 수직 상수값을 넣어줬다. 수평으로 스와이프 하고 싶으면 값을 변경하면 된다.


3. 소스코드


https://github.com/kwony/ViewPager2-Vertically


728x90

'개발 > 안드로이드' 카테고리의 다른 글

RxJava: defer, fromCallable  (0) 2020.02.15
코틀린 apply, also, let, run, with  (0) 2020.02.09
수직으로 스와이프 가능한 ViewPager 만들기  (0) 2019.12.07
RxJava - flatMap  (0) 2019.11.30
RxJava - Create 함수  (0) 2019.08.11
RxJava - Observable, Observer  (1) 2019.08.10