ViewPager+Fragment使用中的几个常见问题总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/songmingzhan/article/details/84452921

1.实现循环切换

思路一:在ViewPager的Adapter中返回count的值为 Integer.MAX_VALUE ,进行初始化的时候讲ViewPager的 setCurrentItem(int item) 的方法中传入Integer.MAX_VALUE的一个中间值,因为Int的最大值是2147483647 如果设它的中间值用户是很难滑到两端的,但是并意味着不能滑到两端。

思路二:在item的两端各增加一个Item,当ViewPager滑动到第一个Item的时候跳转到倒数第二Item,当滑动到最后一个Item时候跳转到第二个Item,设置跳转的方法一定要用

public void setCurrentItem(int item, boolean smoothScroll)

传递参数为false ,这样跳转的就会很流畅。

部分实现代码

@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        removeCheck();
        Log.i(TAG, "onPageScrolled: positionOffset = "+positionOffset+ ", positionOffsetPixels = "+positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        RceLog.i(TAG, "onPageSelected() position  : " + position);
        if (position == 0) {
            isChange = true;
            tabIndex = moduleList.size() - 2;
        } else if (position == moduleList.size() - 1) {
            isChange = true;
            tabIndex = 1;
        } else {
            tabIndex = position;
          
        }

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        Log.i(TAG, "onPageScrollStateChanged: state = "+state);
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            if (isChange) {
                isChange = false;
                viewPager.setCurrentItem(tabIndex, false);
            }
        }
    }

2.禁止预加载问题

看了一下网上的解决方案,不少人都建议使用ViewPager的setOffscreenPageLimit(int limit),但是通过查看源码发现要禁止预加载使用该方法是无效的。

 private static final int DEFAULT_OFFSCREEN_PAGES = 1;
 public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                    + DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

从上面的源码可以明显的看出,使用该方法根本行不通,通过他设置预加载界面至少为一个界面,所以怎么设置都会预加载的。

方案二:
在Fagment可见的时候在进行加载数据,重写setUserVisibleHint(boolean isVisibleToUser)方法 进行判断当前Fragment是否可见

public abstract class BaseFragment extends Fragment {
    protected boolean isUserVisible;
    protected boolean isPrepared;//标志已经初始化完成

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            isUserVisible = true;
            onUserVisible();
        } else {
            isUserVisible = false;
            onUserInvisible();
        }
    }

    /**
     * fragment 可见
     */
    protected void onUserVisible() {
        if (isPrepared && isUserVisible && isAdded()) {
            update();
        }
    }

    /**
     * fragment 不可见
     */
    protected void onUserInvisible() {

    }

    /**
     * 子类重写此方法,在fragment 可见的时候更新 UI
     */
    protected abstract void update();


}


子类在重写update方法,在update中进行数据更新

3.FragmentPagerAdapter与FragmentStatePagerAdapter

FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。再次加载的时候时调用attach方法

destroyItem 的处理

public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        this.mCurTransaction.detach((Fragment)object);
    }

再次加载时instantiateItem 中的部分代码

Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            this.mCurTransaction.attach(fragment);
        } else {
            fragment = this.getItem(position);
            this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
        }

FragmentStatePagerAdapter:会调用remove方法销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。

destroyItem 的处理:调用FragmentTransaction的remove方法从FragmentManager中移除Fragment,同时在mSaveState中保存了Fragment的状态。

 public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        while(this.mSavedState.size() <= position) {
            this.mSavedState.add((Object)null);
        }

        this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);
        this.mFragments.set(position, (Object)null);
        this.mCurTransaction.remove(fragment);
    }

再次加载的时候回从mSavedState获取状态,设置给Fragment

instantiateItem(@NonNull ViewGroup container, int position) 中的部分代码

 fragment = this.getItem(position);
        if (this.mSavedState.size() > position) {
            SavedState fss = (SavedState)this.mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }

通过两个Adapter的对比FragmentPagerAdapter没一个Fragment都会保存在内存中,因此使用一些页面较少的情况,如果界面比较多的情况应该使用FragmentStatePagerAdapter。

猜你喜欢

转载自blog.csdn.net/songmingzhan/article/details/84452921