[컴][안드로이드] ViewPager 와 FragmentPagerAdapter 를 사용할 때 주의할 점



Fragment 와 ViewPager
Drawer 와 ViewPager


ViewPager 와 FragmentPagerAdapter 를 사용할 때 주의할 점



아래처럼 drawer 와 view pager 를 함께 쓰고 있다. 그리고 이 viewpager 는 fragment 안에 들어가 있다. 그리고 이 viewpager 를 FragmentPagerAdapter 를 사용하고 있다.


이 view hierarchy 를 그려보면 아래와 같다.

Activity
  |
  +---- Drawer
          |
          +----- Fragment
          |         |
          |         +---- ViewPager
          |                   |
          |                   +---- Fragment
          |                   |
          |                   +---- Fragment
          |
          +----- Fragment
          |
          |
          ...


fragment 안에 FragmentPagerAdapter 를 이용하는 viewpager 를 사용할 때 문제점

Drawer 메뉴에서 item 을 선택하면 해당하는 Fragment 를 새로 생성하게 되어 있다. 근데 이때 문제가 발생했다. viewpager 가 fragment 를 새로 생성하지 않았다. 이유를 알기 위해 debugging 을 해봤다.

보통 ViewPager 는 아래와 같은 경로로 자신의 adapter (FragmentPagerAdapter)에 있는 fragment 를 가져오게 된다.

이 때 findFragmentByTag를 이용한다. 그래서 문제가 좀 발생한다.


onMeasure():1545, ViewPager (android.support.v4.view)
populate():1025, ViewPager (android.support.v4.view)
populate():1091, ViewPager (android.support.v4.view)
addNewItem():943, ViewPager (android.support.v4.view)
instantiateItem():97, FragmentPagerAdapter (android.support.v4.app)
getItem():86, FragPagerAdapter (com.hbwhale.seedmoney)


@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        ...
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    ...

    return fragment;
}

이 상황에서 문제가 되는 것은 먼저 FragmentPagerAdapter 가 fragment 를 만들기 전에 특정 tag name 으로 된 fragment 가 있는지 확인을 해서 있다면 그녀석을 사용하게 된다. 그런데 문제는 tag name 으로 container.getId() + getItemId() 를 사용한다는 것이다.

해결책

결론적으로 이야기 하면 이렇게 같은 Activity 내에서 같은 layout xml을 이용해서 viewpager 를 계속해서 새로 만드는 상황이라면 getItemId() 를 override 해줄 필요가 있다.

container.getId() 는 같은 xml 을 이용하니 viewpager 를 생성한다 해도 같은 id 값을 갖게 된다. 그러므로 우리는 getItemId() 를 viewpager 가 새로 생성될 때마다 다른 값을 return 할 수 있도록 해줘야 한다.

그렇게 하지 않으면, 과거의 viewpager 가 가지고 있던 fragment 를 찾아서(mFragmentManager.findFragmentByTag) 이용하려고 하기 때문이다.
아마 이 부분을 확실하게 null 을 return 하게 할 수 있다면 굳이 getItemId() 를 override 할 필요는 없다.



<android.support.design.widget.CoordinatorLayout 
 ... >

    <android.support.design.widget.AppBarLayout
        ...>

        <android.support.v7.widget.Toolbar
           .../>

        <android.support.design.widget.TabLayout
            ... />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_main_tabs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        ... />

</android.support.design.widget.CoordinatorLayout>



댓글 없음:

댓글 쓰기