Android-ParallaxHeaderViewPager 코드공개


구글의 뉴스스텐드 앱을 보면 상단의 프로필을 나타내는영역과 하단에 리스트로 레이아웃이 배치되어 있다. 중요한점은 뷰페이저를 이용해 리스트뷰가 좌우로 제스쳐가 된다. 디자인도 그렇고 써보면 상당히 편리 하다는것을 느낄 수있다. 하지만 페이저의 좌우 위치가 맞지 않는 경우 제스쳐를 하게 되는 경우 화면이 튀는 경우를 볼 수 있는데 편리는 하지만 UI가 논리적이지 않다는 것을 볼 수 있다.

예를 들어보면 2개의페이지가 있는데 한쪽은 가장 상단에 반대쪽은 가장 하단으로 이동해있는 경우 좌우페이지를 이동하게 되면 프로필영역의 크기와 위치가 애매하게 된다. 현재로서는 다른 대안이 없기때문에 아직도 수정을 보류중인것 같다. 

이러한 문제점은 Airbnb에서도 어떻게 할까 많은 고민을 한것 같고, 블로그에도 잘 적혀 있다. 나도 이러한 문제점을 해결 하기위해 고민한 결과와 좀더 나은 방법을 고안해낸 코드를 공개한다.

모든것은 준비되어 있다. 연결되어 있지 않을 뿐이다. 

이미 훌륭하게 만들어진 뷰페이저의 인디케이터 역할을 하는 PagerSlidingTabStrip, 이미지를 자연스럽게 애니메이션하는 것과 스크롤시 효과에 대한 NoBoringActionBar는 익히 알려져있다. 이 두개의 조합을 통해 ParallaxHeaderViewPager를 만들어 보았다.

PagerSlidingTabStrip + NoBoringActionBar → ParallaxHeaderViewPager


구현 방법은 다음과 같다.

1. NoBoringActionBar의 ListView를 ViewPager로 교체한다. 

2. ViewPager에 아이템별로 생성된 Fragment에 ListView 또는 ScrollView를 만든다. 

3. 모든 Fragment의 ListView또는 ScrollView의 OnScroll() Listener을 Activity에서 받을 수 있는 Interface를 만든다. 

4. ListView또는 ScrollView에 특정상황에 스크롤을 하는 기능을 가진  Interface를 만든다. 

5. ViewPger에서 페이지 전환시 Interface를 통해 Fragment의 각각의 ListView에 페이지 전환전의 ListView의 높이를 스크롤 하게 함으로 페이지 전환시 높이는 고정된다. 

레이아웃 구조

https://github.com/kmshack/Android-ParallaxHeaderViewPager/blob/master/res/layout-xhdpi/activity_main.xml



구글의 뉴스스텐드와 동일하게 작동되는 것을 볼 수 있다. 

소스코드는 https://github.com/kmshack/Android-ParallaxHeaderViewPager 





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등의 효과를 주어 애니메이션을 생성 할 수 있다.