구글플러스 안드로이드 앱 프로필 화면 구성

요즘 안드로이드 UI를 보면 섹션바가 중간쯤 있다가 위로 올라가면 위쪽에 걸쳐지는 경우가 많다. 구글에서 만들 앱을 보면 더더욱 많이 쓰고 있다. 구글 플러스의 프로필 페이지와 얼마전에 업데이트된 뉴스 스텐드 앱이다.

하지만 자세히보면 모두 구현 방식이 다르다.

구글 플러스

구글 뉴스 스텐드, Airbnb

  • ViewPager를 사용하여 좌우 스와이프가 됨.
  • ViewPager뒤에 배경을 주고 페이지별로 상단에 여백을 주어 배경이 비치게한뒤, 스크롤시 여백을 줄이는지 리스트뷰 터치이벤트를 넘겨 스크롤될지 판단하는 로직.

구글 플러스 프로필 화면 뷰 구조

프로필 화면은 리스트뷰로 이루어 져있으며, 초록색은 Adapter의 아이템이고, 주황색과 빨강색은 헤더뷰로 구성되었다. OnScrollListener을 통해서 스크를을 콜백 받을 수 있는데, 섹션뷰의 위치가 최상단에 오게 되면 섹션뷰를 리스트뷰위에 그리는 방식이다.

스크롤 도중에 프로필이미지의 위치도 Translation Animation을 이용해서 변경한다.

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
    int totalItemCount) {
  if(visibleItemCount == 0) return;
  if(firstVisibleItem != 0) return;

  mImageView.setTranslationY(-mListView.getChildAt(0).getTop() / 2);

}

섹션뷰를 리스트뷰에 그리기위해서는 리스트뷰를 상속받아서 dispatchDraw()에서 canvas에 뷰를 그려주면 된다.

mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
    //...

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (visibleItemCount == 0) return;

        boolean shouldStick = false;
        if (view.getChildAt(0) != mHeader) shouldStick = true;
        else {
            int top = mHeader.getTop();
            int tabsHeight = mHorizontalScrollView.getMeasuredHeight();
            int headerViewHeight = mHeader.getMeasuredHeight();
            int delta = headerViewHeight - tabsHeight;

            if (delta + top < 0) shouldStick = true;
        }

        //sticky header
        if (shouldStick)
            mListView.setViewToDraw(mHorizontalScrollViewContainer);
        else
            mListView.setViewToDraw(null);
    }
});

이렇게 그리는것 외에 터치했을때 뷰가 터치이벤트를 받아야 하기때문에 dispatchTouchEvent()에서 처리해준다. 리스트뷰위에 그리면 이처럼 터치이벤트에 대한 처리를 따로 해줘야 하지만, 리스트위에 레이아웃을 미리구성해놓고 섹션뷰가 최상단에 오면 Visibility속성을 이용해서 보이거나 숨기하면 따로 터치이벤트 처리는 하지 않아도 된다. 하지만 리스트뷰위에 필요 없이 레이아웃이 한번더 그려지는 불필요 한 상황이 발생된다.

구글 플러스 방식은 하나의 리스트뷰로 섹션변경시 Adapter을 변경하는 방식이라 어떻게 보면 뷰 구조가 가벼울 수 있다. 하지만 좌우 스와이프기능이 안되기 때문에 불편 할 수도 있다.

뉴스 스텐드 구현 방식을 보자.

뷰 구조를 보면 ViewPager와 섹션기능을 하는 탭 호스트로 나누어 진다. 정확하게 파악 할 수는 없지만 상단 이미지를 ViewPager뒤쪽에 깔아 놓고 리스트뷰의 터치이벤트를 통해서 빈공간의 높이를 변경 하는 방식인것 같다.

 

빈공간은 단순히 뷰의 페딩이나 마진값으로 높이는 조절 할 수도 있고, 스크롤뷰를 통해 터치이벤트를 계산하여 스크롤 되는 다양한 방식이 있을 것이다. Airbnb 앱을 보면 구글 뉴스스텐드와 동인한 UI가 있는데, 여기 개발자도 만들면서 다양한 경험을 공유(http://nerds.airbnb.com/host-experience-android)하고 있다.

 

이는 구글 플러스 방식 보다 구현이 더 힘들다. 그 이유는 리스트뷰가 스크롤되어야 할때와 섹션헤더가 줄어들어야 할때를 이벤트 처리 해야 하기 때문이다. 예를 들면 섹션헤더가 줄어들다가 완전히 줄어든 경우 이벤트가 리스트뷰로 들어가서 스크롤이 되어야 하는등의 처리 방식에서 이벤트가 꼬이는 문제가 생길가능성이 높기 때문이다.

 

 

Airbnb는 이렇게 해결했다고 한다. ViewPager와 섹션, 상단 이미지를 화면크기보다 더 크게 그려 놓고, 사용자가 스크롤 하는 시점에 상단이미지의 높이를 계산해서 0이 아니면 상단 이미지를 계속 줄여 나가게 된다. 0이 되면 ViewPager와 섹션이 화면크기게 정확하게 맞아지고, 이때 부터는 터치이벤트가 ViewPager에서 먹혀서 리스트뷰나 스크롤뷰가 스크롤가능하게 된다. 반대로 상단에서 아래로 끌때는 스크롤뷰나 리스트뷰가 최상단의 스크롤된 위치인가는 확인하고 최상단이면 상단이미지를 늘리게 되는 구조이다.

ViewPager이기때문에 페이지가 변경될때마다 상단 이미지의 높이를 이전페이지와 유지하기위한 작업도 수행한다.

정리하자면 구글 플러스 방식은 개발하기 훨씬 쉽고 사용하기 불편하다. 뉴스 스텐드 방식은 개발하기 훨씬 어렵고 사용하기 편하다. 여러분의 선택은?