뷰페이저(ViewPager)

ViewPager는 좌우 스와이핑 제스처를 통해 뷰를 전환하는 레이아웃관리자로 볼 수 있다. 뷰페이저의 페이지는 전환될 레이아웃을 말하기 때문에 당연히 뷰도 될 수 있고 뷰그룹도 될 수 있다. 보통 가장많이 쓰이는건 프래그먼트와 함께 많이 쓰이게 된다.

특징

뷰페이저는 항상 현재 위치의 좌우 아이템을 미리 로드한다. 즉 현재 3번 페이지를 보여주고 있다면 이미 2, 4번 페이지에 대한 getItem이 호출된다. 0번에 있다면 우측의 1번 페이지까지 해서 2개가 로드된다. 그리고 현재 위치를 기준으로 좌우에 있는 페이지까지만 유지하고 그 밖에 있는 것들은 버린다.

뷰페이저의 현재 아이템 가져오기

뷰페이저는 페이지들의 전환을 쉽게 해준다. 당연히 뷰페이저를 통해 UX를 구성할때는 현재 페이지를 가져와서 원하는 작업을 하고 싶은 경우가 많다. 그래서 당연히 뷰페이저나 뷰페이저어댑터를 통해 현재 페이지를 가져올 수 있는 API가 있을 것 같다. 그…그런데…없다..그런거..-_-; 만들어야한다.

참고자료 stackOverFlow..

어떻게 할까 한 10초 고민하다가 바로 android viewpager get current fragment로 구글링을 해보니 바로 어떤 똘똘한 아저씨가 친절하게 해결책을 올려주셨다. 요새는 검색하면 어지간한 문제는 다 해결이 되는데 그만큼 내 머리를 쓸일은 적어지는 것 같다. 쩝..

아이디어는 간단하다.

  1. 프래그먼트가 생성되면 리스트에 담는다
  2. 프래그먼트가 소멸되면 리스트에서 버린다
  3. 필요할때마다 리스트에서 꺼낸다

주의점은 아까 특징에서 언급했듯이 뷰페이저는 현재 위치를 기준으로 좌우의 페이지까지만 가지고 있는다. 즉 현재 위치나 내 좌우 페이지가 아닌 위치의 페이지를 가져오려고 하면 오류가 발생한다.

이정도 개념을 머리에 두고 아래 코드를 보면 바로 이해가 갈 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

public MyPagerAdapter(FragmentManager fm) {
super(fm);
}

@Override
public int getCount() {
return ...;
}

@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(...);
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}

public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}

위 코드는 아까 위에서 링크를 걸어뒀던 스택오버플로우에 있는 코드이다. 코드를 보면 아래와 같다.

  1. 프래그먼트가 생성되면 리스트에 담는다
    • instantiateItem()

  2. 프래그먼트가 소멸되면 리스트에서 버린다
    • destroyItem()
  3. 필요할때마다 리스트에서 꺼낸다
    • getRegisteredFragment()

실제 사용할때는 아래와 같이 사용하게 될 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Example {
int mCurrentPosition;
ViewPager mViewPager;
MyPagerAdapter mAdapter;

......

private void initViewPager() {
mViewPager = ...;
mAdapter = new MyPagerAdapter(......);

mViewPager.setAdapter(mAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }

@Override
public void onPageSelected(int position) {
mCurrentPosition = position;
}

@Override
public void onPageScrollStateChanged(int state) {

}
});
}

private void doSomethingWithCurrentFragment() {
Fragment fragment = mAdapter.getRegisteredFragment(mCurrentPosition);
if (fragment == null) {
return;
}

// do something with current fragment..
}
}

먼저 뷰페이저의 현재 위치를 알기 위해 onPageChangeListener를 뷰페이저에 달아준다. 이 리스너의 onPageSelected는 뷰페이저의 페이지가 바뀔때마다 호출된다. 이걸 통해 현재 뷰패이저의 위치를 가지고 있다가 필요할 때 뷰페이저어댑터를 통해 현재 프래그먼트를 가져온다. 끝!!!