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 }
内容取自