開門見山地說 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
十分感謝你能看到這裡。我們的目标是星辰大海,加油!