Android-FragmentPagerAdapter刷新无效的解决方案

按照通常使用ListView的习惯做法,如果你只是更新保存Fragment的List数据,然后调用adapter的notifyDataSetChanged()是不会起作用的.

搜索了下发现此问题普遍存在,多数是说先移除Fragment再notifyDataSetChanged(),因为FragmentPagerAdapter内部会缓存Fragment,但是经测试发现仅仅这样干是不行的。

这可能是Android一个BUG, 与此问题相关的主要有两个方法:

  • getItemPosition()
  • instantiateItem()

具体的分析内容参见原文,那么重写后的adapter如下:

  1 /**
  2  * 加载显示Fragment的ViewPagerAdapter基类
  3  * 提供可以刷新的方法
  4  *
  5  * @author Fly
  6  * @e-mail 1285760616@qq.com
  7  * @time 2018/3/22
  8  */
  9 public class BaseFragmentPagerAdapter extends FragmentPagerAdapter {
 10     private List<BaseFragment> mFragmentList;
 11     private FragmentManager mFragmentManager;
 12     /**下面两个值用来保存Fragment的位置信息,用以判断该位置是否需要更新*/
 13     private SparseArray<String> mFragmentPositionMap;
 14     private SparseArray<String> mFragmentPositionMapAfterUpdate;
 15 
 16     public BaseFragmentPagerAdapter(FragmentManager fm, List<BaseFragment> fragments) {
 17         super(fm);
 18         mFragmentList = fragments;
 19         mFragmentManager = fm;
 20         mFragmentList = fragments;
 21         mFragmentPositionMap = new SparseArray<>();
 22         mFragmentPositionMapAfterUpdate = new SparseArray<>();
 23         setFragmentPositionMap();
 24         setFragmentPositionMapForUpdate();
 25     }
 26 
 27     /**
 28      * 保存更新之前的位置信息,用<hashCode, position>的键值对结构来保存
 29      */
 30     private void setFragmentPositionMap() {
 31         mFragmentPositionMap.clear();
 32         for (int i = 0; i < mFragmentList.size(); i++) {
 33             mFragmentPositionMap.put(Long.valueOf(getItemId(i)).intValue(), String.valueOf(i));
 34         }
 35     }
 36 
 37     /**
 38      * 保存更新之后的位置信息,用<hashCode, position>的键值对结构来保存
 39      */
 40     private void setFragmentPositionMapForUpdate() {
 41         mFragmentPositionMapAfterUpdate.clear();
 42         for (int i = 0; i < mFragmentList.size(); i++) {
 43             mFragmentPositionMapAfterUpdate.put(Long.valueOf(getItemId(i)).intValue(),  String.valueOf(i));
 44         }
 45     }
 46 
 47    /**
 48     * 在此方法中找到需要更新的位置返回POSITION_NONE,否则返回POSITION_UNCHANGED即可
 49     */
 50     @Override
 51     public int getItemPosition(Object object) {
 52         int hashCode = object.hashCode();
 53         //查找object在更新后的列表中的位置
 54         String position = mFragmentPositionMapAfterUpdate.get(hashCode);
 55         //更新后的列表中不存在该object的位置了
 56         if (position == null) {
 57             return POSITION_NONE;
 58         } else {
 59             //如果更新后的列表中存在该object的位置, 查找该object之前的位置并判断位置是否发生了变化
 60             int size = mFragmentPositionMap.size();
 61             for (int i = 0; i < size ; i++) {
 62                 int key = mFragmentPositionMap.keyAt(i);
 63                 if (key == hashCode) {
 64                     String index = mFragmentPositionMap.get(key);
 65                     if (position.equals(index)) {
 66                         //位置没变依然返回POSITION_UNCHANGED
 67                         return POSITION_UNCHANGED;
 68                     } else {
 69                         //位置变了
 70                         return POSITION_NONE;
 71                     }
 72                 }
 73             }
 74         }
 75         return POSITION_UNCHANGED;
 76     }
 77 
 78     /**
 79      * 将指定的Fragment替换/更新为新的Fragment
 80      * @param oldFragment 旧Fragment
 81      * @param newFragment 新Fragment
 82      */
 83     public void replaceFragment(BaseFragment oldFragment, BaseFragment newFragment) {
 84         int position = mFragmentList.indexOf(oldFragment);
 85         if (position == -1) {
 86             return;
 87         }
 88         //从Transaction移除旧的Fragment
 89         removeFragmentInternal(oldFragment);
 90         //替换List中对应的Fragment
 91         mFragmentList.set(position, newFragment);
 92         //刷新Adapter
 93         notifyItemChanged();
 94     }
 95 
 96     /**
 97      * 将指定位置的Fragment替换/更新为新的Fragment,同{@link #replaceFragment(BaseFragment oldFragment, BaseFragment newFragment)}
 98      * @param position    旧Fragment的位置
 99      * @param newFragment 新Fragment
100      */
101     public void replaceFragment(int position, BaseFragment newFragment) {
102         BaseFragment oldFragment = mFragmentList.get(position);
103         removeFragmentInternal(oldFragment);
104         mFragmentList.set(position, newFragment);
105         notifyItemChanged();
106     }
107 
108     /**
109      * 移除指定的Fragment
110      * @param fragment 目标Fragment
111      */
112     public void removeFragment(BaseFragment fragment) {
113         //先从List中移除
114         mFragmentList.remove(fragment);
115         //然后从Transaction移除
116         removeFragmentInternal(fragment);
117         //最后刷新Adapter
118         notifyItemChanged();
119     }
120 
121     /**
122      * 移除指定位置的Fragment,同 {@link #removeFragment(BaseFragment fragment)}
123      * @param position
124      */
125     public void removeFragment(int position) {
126         BaseFragment fragment = mFragmentList.get(position);
127         //然后从List中移除
128         mFragmentList.remove(fragment);
129         //先从Transaction移除
130         removeFragmentInternal(fragment);
131         //最后刷新Adapter
132         notifyItemChanged();
133     }
134 
135     /**
136      * 添加Fragment
137      * @param fragment 目标Fragment
138      */
139     public void addFragment(BaseFragment fragment) {
140         mFragmentList.add(fragment);
141         notifyItemChanged();
142     }
143 
144     /**
145      * 在指定位置插入一个Fragment
146      * @param position 插入位置
147      * @param fragment 目标Fragment
148      */
149     public void insertFragment(int position, BaseFragment fragment) {
150         mFragmentList.add(position, fragment);
151         notifyItemChanged();
152     }
153 
154     private void notifyItemChanged() {
155         //刷新之前重新收集位置信息
156         setFragmentPositionMapForUpdate();
157         notifyDataSetChanged();
158         setFragmentPositionMap();
159     }
160 
161     /**
162      * 从Transaction移除Fragment
163      * @param fragment 目标Fragment
164      */
165     private void removeFragmentInternal(BaseFragment fragment) {
166         FragmentTransaction transaction = mFragmentManager.beginTransaction();
167         transaction.remove(fragment);
168         transaction.commitNow();
169     }
170 
171     /**
172      * 此方法不用position做返回值即可破解fragment tag异常的错误
173      */
174     @Override
175     public long getItemId(int position) {
176         // 获取当前数据的hashCode,其实这里不用hashCode用自定义的可以关联当前Item对象的唯一值也可以,只要不是直接返回position
177         return mFragmentList.get(position).hashCode();
178     }
179 
180     @Override
181     public Fragment getItem(int position) {
182         return mFragmentList.get(position);
183     }
184 
185     @Override
186     public int getCount() {
187         return mFragmentList.size();
188     }
189 
190     public List<BaseFragment> getFragments() {
191         return mFragmentList;
192     }
193 }

测试代码:

 1 public class TestActivity extends FragmentActivity implements View.OnClickListener {
 2     List<Fragment> mFragmentList;
 3     ViewPager mViewPager;
 4     public BaseFragmentPagerAdapter mAdapter;
 5 
 6     @Override
 7     protected void onCreate(Bundle savedInstanceState) {
 8         super.onCreate(savedInstanceState);
 9         setContentView(R.layout.activity_test);
10         mViewPager = findViewById(R.id.vp);
11         findViewById(R.id.btn_change).setOnClickListener(this);
12 
13         mFragmentList = new ArrayList<>();
14         mFragmentList.add(getFg("AAA"));
15         mFragmentList.add(getFg("BBB"));
16         mFragmentList.add(getFg("CCC"));
17         mFragmentList.add(getFg("DDD"));
18         mAdapter = new BaseFragmentPagerAdapter(getSupportFragmentManager(), mFragmentList);
19         mViewPager.setAdapter(mAdapter);
20     }
21 
22     private TestFragment getFg(String a){
23         TestFragment fragment = new TestFragment();
24         fragment.setTest(a);
25         return fragment;
26     }
27 
28     @Override
29     public void onClick(View view) {
30         TestFragment eee = getFg("EEE");
31 
32         //新增
33         mAdapter.addFragment(eee);
34         //插入
35         mAdapter.insertFragment(1, eee);
36 
37         //删除
38         mAdapter.removeFragment(1);
39         //删除
40         mAdapter.removeFragment(mFragmentList.get(1));
41 
42         //替换
43         mAdapter.replaceFragment(1, eee);
44         //替换
45         mAdapter.replaceFragment(mFragmentList.get(0), eee);
46     }
47 }

内容取自

https://blog.csdn.net/lyabc123456/article/details/79797552

posted @ 2018-05-30 13:45  Sharley  阅读(3042)  评论(0编辑  收藏  举报