开门见山地说 ViewPager的动态更新分为以下两种情况:
1.只改变渲染的List中某些元素的某个值,不改变位置和数量的
2.改变位置和数量的
而Adapter的notifyDataSetChanged方法恰好就是在第一种情况中无效的。
先讨论第一种情况。
可能有一些跟我一样的初学者会有一样的疑问,为什么里面的元素都更改了,他的这个方法不触发呢,别急,导致notifyDataSetChanged方法哑火的情况有很多,我先只列出这种情况,你先看看是不是你的菜。
我使用ViewPager+List<View>+ViewPagerAdapter构建了一个简易的切换页面,点击上方按钮,就可以更新下面的viewpager里面的当前索引的子项的某个组件的值(背景色),这里只大概解释原理和运行规则,MainActivity的代码如下,其他部分的代码我就不贴出来了,想必各位读者都是真才实学的高手,这点代码难不倒你们的。
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private ViewPager mViewPager;
private MViewPagerAdapter mAdapter;
private List<View> itemList;
private LayoutInflater mInflater;
private int mIndex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = findViewById(R.id.viewPager);
mTextView = findViewById(R.id.textView);
itemList = new ArrayList<>();
mInflater = getLayoutInflater();
//添加两个初始子项
itemList.add(generateItem(R.color.pink));
itemList.add(generateItem(R.color.teal_200));
mAdapter = new MViewPagerAdapter(this,itemList);
mViewPager.setAdapter(mAdapter);
//给viewpager添加切换事件监听,滑动时可以切换索引
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mIndex = position;
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeCurrentColor(mIndex,R.color.red);
}
});
}
/**
* 用于插入一个ViewPager的子项
* @param id 颜色的id,可以指定生成什么颜色的子项
* @return 返回一个子项的view,直接插入itemList
*/
public View generateItem(int id){
View item = mInflater.inflate(R.layout.viewpager_item, null);
TextView vpTextView = item.findViewById(R.id.vp_textView);
vpTextView.setBackgroundColor(getResources().getColor(id));
return item;
}
/**
* 根据索引改变当前位置的子项中组件的背景色
* @param index 索引
* @param id 颜色的id
*/
public void changeCurrentColor(int index,int id){
//错误的做法
/*View view = generateItem(id);
itemList.set(index,view);
mAdapter.notifyDataSetChanged();*/
//正确的做法
View view = itemList.get(index);
TextView tv = view.findViewById(R.id.vp_textView);
tv.setBackgroundColor(getResources().getColor(id));
}
}
可以看到我标出了错误的做法和正确的做法两个地方。可能是因为java基础不好,我很多时候是拒绝查看源码的,这是一个不好的习惯,也是导致这个问题我也是查了资料才解决的。
源码中notifyDataSetChanged()的触发条件有二:
1). 当前对象发生过位置改变
2). 绑定的List的长度发生变化
具体源码的话我还是不太看得懂,详情见这篇
https://www.jianshu.com/p/863297e782c3?nomobile=yes
也就是说,在错误案例中的
View view = generateItem(id);
itemList.set(index,view);
mAdapter.notifyDataSetChanged();
这样的操作完全没有碰到notifyDataSetChanged()方法的触发条件,这样用是错误的。
在更新List时的,删除子项或插入子项,调用这个方法没问题,但如果只是维护某索引的某个子项中的某个值的话,建议直接去修改它就好。
因此notifyDataSetChanged的这种情况的结论简单来说就是: 删除或插入可以,简单的更新不行。
当然notifyDataSetChanged失效的原因还有一点,viewpager-adapter-list三者之一不指向绑定时的地址了,换句话说就是,有内鬼!
具体的原因这篇文章讲的还不错,嘻嘻。
https://blog.csdn.net/whitley_gong/article/details/50562634
十分感谢你能看到这里。我们的目标是星辰大海,加油!