ViewPager의 PagerAdapter POSITION_NONE의 비밀



ViewPager의 PagerAdapter POSITION_NONE의 비밀




요즘 안드로이드 UI는 페이지단위가 대세인듯 합니다. 마켓뿐안 아니라 내놓으라는 인기앱들을 보면 모두 페이징 개념의 UI로 구성되어 있습니다. 안드로이드에서 이런 페이징을 쉽게 구현하기 위해 ViewPager을 Support해주고 있습니다. 



ViewPage는 View또는 Fragment를 페이지 단위로 관리 할수 있는 커스텀 뷰입니다. 

PageAdapter와 ViewPager을 같이 쓰면서 자원낭비에 대한 문제점을 알아보고자 합니다.

PageAdapter의 notifyDataSetChanged는 데이터가 바꼈으니 Notify를 하는 메소드 입니다.

PageAdapter에서 Fragment를 새로 생성하여 View를 만들때 이런 데이터 작업을 하게 됩니다, 그래서 notifyDataSetChanged를 하게 되더라도 Fragment내부의 View들이 새로고침이 되지 않는 문제점이 발생합니다. 강제로 OnCreateView를 태워 View를 그리거나 View를 refresh하도록 구현 해야 합니다.

그래서 대안으로 PageAdapter에서 getItemPosition을 오버라이드 하여 포지션값을 POSITION_NONE으로 주는 방법입니다.

POSITION_NONE으로 인해 ViewPager는 destoryItem()이 호출되어 Fragment가 삭제 된것으로 판단하게 되어 onCreateView가 호출되어 다시 그리는 방식입니다.

        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

데이터를 바꾸기 위해 notifyDataSetChanged를 호출 하면 Fragment를 새로 생성한다? 

단지 간단한 View일 경우 그렇게 문제 될점은 없을지 모르지만 ListView같은 복잡한 View들로 구성되어 있을때 자원이 낭비되는 문제점을 발생 할 수 있습니다.

따라서 이런 불필요한 낭비를 막기위해서는 instantiateItem에서 View들에 대한 정보를 저장해놓은 다음 데이터업데이트시 View정보를 불러와 refresh하도록 구현하길 권합니다.

SparseArray< View > views = new SparseArray< View >();

@Override
public Object instantiateItem(View container, int position) {
    View root = //refresh할 뷰
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

@Override
public void destroyItem(View collection, int position, Object o) {
    View view = (View)o;
    ((ViewPager) collection).removeView(view);
    views.remove(position);
    view = null;
}

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       //refresh할 작업들 
    }
    super.notifyDataSetChanged();
}

이 처럼 작업하여 자원의 낭비를 막을 수 있습니다.

POSITION_NONE은 장점도 있다. 예를 들어 가로세로 전환시 ViewPager의 View들이 새로운레이아웃을불러 들이는 경우에는 이 방법을 써야 합니다.

어떻게 하라고 권고 할 말한 사항은 아닌만큼 앱의 특성에 맞게 사용하시길 바랍니다.