AndroidAndroid经验谈

ViewPager中动态更换Fragment不刷新的问题

2016-09-02  本文已影响5656人  諸星団

是使用ViewPager+Fragment构建界面的时候,遇到了这样的需求:在ViewPager的第一个item中有一个Button,点击Button会将第一个item中的Fragment换成另外一个Fragment。之前想的是将adapter中List<Fragment>中的数据换掉,然后notifyDataSetChanged,发现无效。研究FragmentPagerAdapter的源码发现,

@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) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            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));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

其在instantiateItem方法中并不是直接去List里面拿到Fragment的,而是先从FragmentManager中通过Tag找对应的Fragment,如果可以找到就不会去List里面拿了,这样无论再怎么更新list都是没用的,解决思路如下:

1、在重写instantiateItem方法,手动从FragmentManager将需要更新的Fragment移除,代码如下:

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        // TODO Auto-generated method stub
        if (position == 0)
            removeFragment(container,position);
        return super.instantiateItem(container, position);
    }

    private void removeFragment(ViewGroup container,int index) {
        String tag = getFragmentTag(container.getId(), index);
        Fragment fragment = fm.findFragmentByTag(tag);
        if (fragment == null)
            return;
        FragmentTransaction ft = fm.beginTransaction();
        ft.remove(fragment);
        ft.commit();
        ft = null;
        fm.executePendingTransactions();
    }

由于我这里要动态更换的是第一个item的fragment,所以就直接做判断if (position == 0),其他情况可以按需修改。首先通过一个getFragmentTag方法得到当前position的fragment的tag,这个方法里面其实就是用反射的方式调用了FragmentPagerAdapter的makeFragmentName方法,因为这里的tag是在这个方法中计算出来的,此段代码如下:

private String getFragmentTag(int viewId, int index) {  
        try {  
            Class<FragmentPagerAdapter> cls = FragmentPagerAdapter.class;  
            Class<?>[] parameterTypes = { int.class, long.class };  
            Method method = cls.getDeclaredMethod("makeFragmentName",  
                    parameterTypes);  
            method.setAccessible(true);  
            String tag = (String) method.invoke(this, viewId, index);  
            return tag;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return "";  
        }  
    }  

得到Tag后就可以通过FragmentManager的findFragmentByTag方法得到fragment,如果存在,就通过FragmentTransaction 移除掉即可。

最后重写getItemPosition() 方法,返回 POSITION_NONE。代码如下:

@Override  
    public int getItemPosition(Object object) {  
        // TODO Auto-generated method stub  
        return POSITION_NONE;  
    }  

2、使用FragmentStatePagerAdapter,它不会对fragment做缓存,每次都会销毁重建,这样效率很低,只适合页面非常少访问不频繁等特殊情况。

上一篇下一篇

猜你喜欢

热点阅读