在我们的项目开发中会经常使用到ViewPager+Fragment来实现特定的UI效果,但是当Fragment需要动态增加,删除过内容变更时,会出现Fragment页面刷新失效的情况
如下:
@BindView(R.id.viewpager) ViewPager viewpager;
List fragments = new ArrayList<>();
private void setupViewPager(List list_key) {
fragments.clear();
for (int i = 0; i < list_key.size(); i++) {
fragments.add(MyFragment.newInstance(list_key.get(i)));
}
viewpager.setAdapter(new MyPagerAdapter(getChildFragmentManager(), fragments));
viewpager.setOffscreenPageLimit(2);
}
class MyPagerAdapter extends FragmentPagerAdapter {
private List fragments;
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
//创建 主页tab
public static MyFragment newInstance(String key) {
FragmentHomeUser fragment = new FragmentHomeUser();
Bundle args = new Bundle();
args.putString("key", key);
fragment.setArguments(args);
return fragment;
}
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_homeuser, container, false);
String key = getArguments().getString("key", "");
return view;
}
当我们需要变更fragments中的Fragment时,即便
Bundle args = new Bundle();
args.putString(“key”, key);
fragment.setArguments(args);
中传入新的key
String key = getArguments().getString(“key”, “”);
获取到的值依然是就的key值
后来发现是因为FragmentPagerAdapter 没有刷新导致
要解决这个问题要针对FragmentPagerAdapter 做一些优化处理
1、清除FragmentPagerAdapter缓存,重新添加
private void clearViewPager() {
//先保证ViewPager之前已设置过Adapter,这样才有可能存在缓存
if (viewpager.getAdapter() != null) {
//获取FragmentManager实现类的class对象,这里指的就是FragmentManagerImpl
Class extends FragmentManager> aClass = getChildFragmentManager().getClass();
try {
//1.获取其mAdded字段
Field field = aClass.getDeclaredField("mAdded");
field.setAccessible(true);
//强转成ArrayList
ArrayList list = (ArrayList) field.get(getChildFragmentManager());
//清空缓存
list.clear();
//2.获取mActive字段
field = aClass.getDeclaredField("mActive");
field.setAccessible(true);
//强转成SparseArray
SparseArray array = (SparseArray) field.get(getChildFragmentManager());
//清空缓存
array.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void setupViewPager(List list_key) {
fragments.clear();
for (int i = 0; i < list_key.size(); i++) {
fragments.add(MyFragment.newInstance(list_key.get(i)));
}
clearViewPager();
viewpager.setAdapter(new MyPagerAdapter(getChildFragmentManager(), fragments));
viewpager.setOffscreenPageLimit(2);
}
2、重写FragmentPagerAdapter的getItemPosition方法
class MyPagerAdapter extends FragmentPagerAdapter {
private List fragments;
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragments = fragments;
notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public long getItemId(int position) {
return fragments.get(position).hashCode();
}
@Override
public int getItemPosition(@NonNull Object object) {
// 否则返回状态值 POSITION_NONE
return POSITION_NONE;
}
}
以上可以解决Fragment的动态刷新问题,但新的问题出现了,在刷新时,由于Fragment要清除后重新载入,在UI效果上会出现闪屏现象,下面我们在来解决这个问题
在初始化时,对比fragments和list_key需要生成的Fragment列表,已经添加到FragmentPagerAdapter 中的Fragment,只变更key值,根据key值刷新Fragment,需要添加的Fragment则使用fragments.add(MyFragment.newInstance(list_key.get(i)));添加新的Fragment,最后通过remove方法移出多余的Fragment
private void setupViewPager(List list_key) {
if (fragments.size() > 0) {
for (int i = 0; i < list_key.size(); i++) {
if (fragments.size() > i) {
if (fragments.get(i).getKey().equals(list_key.get(i))) {
Bundle args = new Bundle();
args.putString("key", list_key.get(i));
fragments.get(i).setArguments(args);
}
} else {
fragments.add(MyFragment.newInstance(list_key.get(i)));
}
}
} else {
for (int i = 0; i < list_key.size(); i++) {
fragments.add(MyFragment.newInstance(list_key.get(i)));
}
}
remove(fragments, list_key);
viewpager.setAdapter(new MyPagerAdapter(getChildFragmentManager(), fragments));
viewpager.setOffscreenPageLimit(2);
}
/**
* 移除
*/
private void remove(List fragments, List array) {
for (Iterator it = fragments.iterator(); it.hasNext(); ) {
MayFragment entry = it.next();
String key = entry.getArguments().getString("key");
Logs.e("entry.key = " + key + ", isInArray = " + isInArray(array, key));
if (!isInArray(array, key)) {
Logs.e("删除-" + key);
it.remove();
}
}
}
private boolean isInArray(List array, String key) {
for (int i = 0; i < array.size(); i++) {
if (array.get(i).equals(key)) {
return true;
}
}
return false;
}
然后重写FragmentPagerAdapter 的getItemPosition方法,key值一致测不需要刷新,不一致的需要通过return POSITION_NONE;刷新Fragment
class MyPagerAdapter extends FragmentPagerAdapter {
private List fragments;
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragments = fragments;
notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public long getItemId(int position) {
return fragments.get(position).hashCode();
}
@Override
public int getItemPosition(@NonNull Object object) {
MyFragment info = (MyFragment) object;
int position = getFragmentPosition(fragments, info.getKey());
if (position != -1) {
// 如果当前 item 未被 remove,则返回 item 的真实 position
return position;
} else {
// 否则返回状态值 POSITION_NONE
return POSITION_NONE;
}
}
}
public int getFragmentPosition(List fragments, String key) {
for (int i = 0; i < fragments.size(); i++) {
if (key.equals(fragments.get(i).getKey())) {
return i;
}
}
return -1;
}
以上既能刷新Fragment有可避免已有Fragment刷新时闪屏问题