ViewPager是个好东西,但往往有些业务需要是android无法满足的,比如要更新ViewPager的特定view
本帖其实就是StackOverflow的总结帖
原帖见http://stackoverflow.com/questions/7263291/viewpager-pageradapter-not-updating-the-view
一种简单快速的办法:
Override getItemPosition in your PagerAdapter like this: public int getItemPosition(Object object) { return POSITION_NONE; } This way, when you call notifyDataSetChanged(), the view pager will be updated.
I think that there is no any kind of bug in the PagerAdapter, the problem is that its understanding is a little complex. Looking the solutions explained here, there is a misunderstanding and then a poor usage of instantiated views from my point of view. I have been working the last few days with PagerAdapter and ViewPager, and i found the following: The notifyDataSetChanged() method on the PagerAdapter will only notify the ViewPager that the underlying pages have changed. For example, if you have create/deleted dynamically pages (adding or removing items from your list) the ViewPager should take care about that. In this case i think that the ViewPager determines if a new view should be deleted or instantiated using the getItemPosition() and getCount() methods. I think that ViewPager after a notifyDataSetChanged() call, takes its child views, and check their position with the getItemPosition(). If for a child view this method returns POSITION_NONE, the ViewPager understand that the view has been deleted, calling the destroyItem(), and removing this view. In this way, overriding the getItemPosition() to return always POSITION_NONE is completely wrong only if you want to update the content of the pages. Because the previously created views will be destroyed, and a new ones will be created every time you call notifyDatasetChanged(). It may seem to be not so wrong just for a few TextViews, but when you have complex views, like ListViews populated with databases, this may be a real problem and a waste of resources. So there are several approaches to efficiently change the content of a view without having to remove and instantiate again the view. It the depends on the problem you want to solve. My approach is to use the setTag() method for any instantiated view in the instantiateItem() method. So when you want to change the data or invalidate the view that you need, you can call the findViewWithTag() method on the viewPager to retrieve the previously instantiated view and modify/use it as you want without having to delete create any new view each time you want to update some value. Imagine for example that you have 100 pages with 100 TextViews and you want to update only one value periodically.. with the approaches explained before it will means to remove and instantiates 100 TextView in each update. It does not make sense...
文中提到在 instantiateItem(ViewGroup container, int position) 时,进行setTag(),然后在需要更新的view的地方,使用findViewWithTag (Object tag)获取到要操作的view,直接操作这个view即可。
但是PagerAdapter的instantiateItem方法写到
Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).所以覆盖 instantiateItem是不保险的,因为这时候view可能并未被创建,而在finishUpdate()时是确保view可用的。
大致的顺序
1.PagerAdapter.instantiateItem()
2.FragmentPagerAdapter.getItem() -- 这时并没有创建fragment的view
3.FragmentManager.moveToState() -- 这里调用了fragment的onCreateView()
4.PagerAdapter.finishUpdate()