ViewPager PageTransformer


ViewPager의 PageTransformer 인터페이스를 이용하면 페이지 전환시 애니메이션을 다양하게 만들 수 있다. 대표적으로 구글의 플레이뮤직앱을 보면 트렉을 넘기는 경우 일반적으로 좌/우페이지 애니메이션이 아니라 아래에서 위쪽으로 올라오는 애니메이션을 볼 수있다. 


구현 방법은 PageTransformer를 implement해서 transformPage(View view, int position)를 구현 해서 ViewPager의 setPageTransformer (boolean reverseDrawingOrder, ViewPager.PageTransformer transformer) 메서드를 이용해서 설정하면 된다. 


ViewPager pager = (ViewPager) findViewById(R.id.pager);
...
pager.setPageTransformer(true, new DepthPageTransformer());

public class DepthPageTransformer implements ViewPager.PageTransformer {

    public void transformPage(View view, float position) {
        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page

        } else if (position <= 1) { // (0,1]
            // Fade the page out.

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.

        }
    }
}

view는 현재 화면에 보여지고 있는 View가 들어 오게 되며, postion은 -1~1사이의 값이 들어 온다. -1~0의 값이 들어 오는 경우는 페이지가 없어지고 있을때(왼쪽), 0~1의 값이 들어 오는 경우에는 페이지가 보여지고 있을때(오른쪽)이다. 

예를 들어 1페이지에서 2페이지로 전환 할때 1페이지는 0->-1값으로 수치가 줄어들고, 2페이지는 0->1로 수치가 늘어난다. 이렇기 때문에 4개의 경우로 분할 해서 작업을 하면된다. 

  • 페이지가 완전히 왼쪽으로 이동된 경우 position < -1
  • 페이지가 왼쪽으로 이동되고 있는 경우 position <= 0
  • 페이지가 새롭게 나타나고 있는 경우 position <= 1
  • 나머지 
이렇게 분할 해서 상황에 따라 View의 alpha, translate등의 효과를 주어 애니메이션을 생성 할 수 있다. 



Android ViewPager 성능향상 방법


안드로이드 초기에만 해도 Activity는 하나의 페이지로만 인식이 되었는데, 몇년전부터 하나의 Activity에서도 여러개의 화면을 배치할 수 있는 UI들이 생겨나고 있다. 또한 폰뿐만 아니라 테블릿에서도 다양한 UI를 위해 분할된 화면들로 배치하고 있다. 이런 UI중심에는 ViewPager라는 안드로이드에서 지원하는 View가 있다.

ViewPager은 화면전환 없이 좌우 스크롤을 통해 효율적으로 페이지 전환을 할 수 있는 UI이다. ViewPager은 기본적으로 좌/우 화면을 미리 로딩해두기 때문에 좌우 스크롤시 빠른화면전환을 보여준다. 스크롤을 양옆으로 조금 당여보면 미리로딩되어 있다는 것을 알 수 있다.

 

하지만 이러한 로딩방식으로 처음 진입시 성능이나 메모리적으로 문제가 된다. 한 화면에 하나를 표시 할 것을 좌우 페이지를 미리불러오기 때문이다. 그래서 이 화면을 그리는 것에 대해 성능을 향상 하는것이 ViewPager의 성능향상이 되는 것이다.

1. View계층 단순화 및 thread분리

ViewPager는 ListView 처럼 View를 재활용 하는 방식이 아니라 보이는 페이지, 좌, 우 페이지가 필요 한경우 그때그때 View를 그리는 방식이다. 이 View를 초기화 할때 mainThread를 사용하는데, 이외 의 작업들은 다른 Thread를 이용해서 작업 해야한다. 만일 View초기화시 파일을 읽는다거나, 네트워크 작업을 하는등 mainThread에서 작업하게 된다면 페이지 전환이 엄청 느릴것이다. View의 계층 구조또한 단순히 해서 View를 그릴때 시간을 조금이라도 줄여야 한다.

 

2. 화면 그리는 시점 변경

1) ViewPager 로딩 방식을 변경

ViewPager은 기본적으로 현재 보고있는 페이지, 좌/우페이지로 3개의 ChildView를 미리 로딩한다. 이렇게 미리로딩 하는 구조가 아닌 현재 화면에 보이고 있는 로딩하는 구조로 변경하면 성능을 조금더 향상된다.  ViewPager의 OnPageChangeListener를 통해서 페이지가 변경되었을때 onPageScrollStateChanged으로 변경된 페이지Position을 CallBack받을 수 있다. 성능은 좋아지겠지만 화면이 전환 되었을때 로딩 하는 구조이기 때문에 사용자는 로딩을 보면서 기다리게 만들 수 있다는 단점이 존재한다.

mPager.setOnPageChangeListener(new OnPageChangeListener() {
    
    @Override
    public void onPageSelected(int state) {
        
    }
    
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        
    }
    
    @Override
    public void onPageScrollStateChanged(int position) {
        
    }
});
2) PageLimit 갯수 변경

초반에 미리 로드해두는 방식이다. 예를 들어 페이지가 4개가 있을 경우 ViewPager는 초반에 1,2 페이지만 그리게 된다. 하지만 3페이지로 이동하면 3,4페이지가 로딩이 된다. 동시에 2개가 로딩되면서 버벅일 수 있다. 이렇게 페이지 이동시마다 많은 View들이 로딩을 하게 되면 무거워 질 수밖에 없다. 이렇게 페이지이동시마다 화면의 버벅임을 없애기 좋은 방법은 미리 모든 페이지를 로드해두는 것이다. 페이지가 많고 복잡한 구조이면 메모리문제가 생길 수 있으며, 4~5개의 간단한 페이지의 경우 미리 로딩해둔 다음 페이지만 이동하는 방식으로 바꾸는 것이 성능면에서 유리하다.

ViewPager의 setOffscreenPageLimit(int) 메서드를 이용하면 양쪽에 유지되는 페이지수를 설정 할 수가 있다. 예를 들어 4개의 페이지일 경우 3로 설정 하는 경우 4개의 페이지를 초반에 미리로딩한다. 페이지를 이동할때 마다 View를 지우고 새로만드는 작업은 하지않게 된다. 4개의 페이지가 미리로딩 되어 있기때문이다.

mPager.setOffscreenPageLimit(3);

참고: ViewPager View 강제로 다시그리기

위 3가지의 방법은 ViewPager가 어떤식으로 활용하는지에 따라 모두 성능이 다르다르기 때문에 적절하게 선택해서 쓰기바란다.