Android ViewPager 不重绘内容,保持/变为空白
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11736953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ViewPager does not redraw content, remains/turns blank
提问by Matthias
We're suffering from a very strange issue with ViewPager here. We embed lists on each ViewPager page, and trigger notifyDataSetChanged both on the list adapter and the view pager adapter when updating list data.
我们在这里遇到了一个非常奇怪的 ViewPager 问题。我们在每个 ViewPager 页面上嵌入列表,并在更新列表数据时在列表适配器和视图分页器适配器上触发 notifyDataSetChanged。
What we observe is that sometimes, the page does not update its view tree, i.e. remains blank, or sometimes even disappears when paging to it. When paging back and forth a few times, the content will suddenly reappear. It seems as if Android is missing a view update here. I also noticed that when debugging with hierarchy viewer, selecting a view will always make it reappear, apparently because hierarchy viewer forces the selected view to redraw itself.
我们观察到,有时页面不更新其视图树,即保持空白,有时甚至在分页时消失。来回翻页几次,内容会突然重新出现。似乎 Android 在这里缺少视图更新。我还注意到,在使用层次查看器进行调试时,选择视图总是会使其重新出现,这显然是因为层次查看器强制所选视图重新绘制自身。
I could not make this work programmatically though; invalidating the list view, or the entire view pager even, had no effect.
不过,我无法以编程方式完成这项工作;使列表视图,甚至整个视图寻呼机无效,都没有效果。
This is with the compatibility-v4_r7 library. I also tried to use the latest revision, since it claims to fix many issues related to view pager, but it made matters even worse (for instance, gestures were broken so that it wouldn't let me page through all pages anymore sometimes.)
这是与兼容性 v4_r7 库。我还尝试使用最新版本,因为它声称修复了许多与查看寻呼机相关的问题,但它使事情变得更糟(例如,手势被破坏,因此有时我无法再翻阅所有页面。)
Is anyone else running into these issues, too, or do you have an idea of what could be causing this?
有没有其他人也遇到过这些问题,或者您知道是什么原因造成的?
采纳答案by futtetennista
We finally managed to find a solution. Apparently our implementation suffered of two issues:
我们终于设法找到了解决方案。显然,我们的实施遇到了两个问题:
- our adapter did not remove the view in
destroyItem()
. - we were caching views so that we'd have to inflate our layout just once, and, since we were not removing the view in
destroyItem()
, we were not adding it ininstantiateItem()
but just returning the cached view corresponding to the current position.
- 我们的适配器没有删除
destroyItem()
. - 我们正在缓存视图,以便我们只需要膨胀一次我们的布局,并且由于我们没有删除 中的视图
destroyItem()
,我们没有添加它,instantiateItem()
而是返回与当前位置相对应的缓存视图。
I haven't looked too deeply in the source code of the ViewPager
- and it's not exactly explicit that you have to do that - but the docs says :
我还没有深入研究 - 的源代码ViewPager
- 你必须这样做并不完全明确 - 但文档说:
destroyItem()
Remove a page for the given position. The adapter is responsible for removing the view from its container, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).
destroyItem()
删除给定位置的页面。适配器负责从它的容器中删除视图,尽管它只需要确保在它从 finishUpdate(ViewGroup) 返回时完成。
and:
和:
A very simple PagerAdapter may choose to use the page Views themselves as key objects, returning them from instantiateItem(ViewGroup, int) after creation and adding them to the parent ViewGroup. A matching destroyItem(ViewGroup, int, Object) implementation would remove the View from the parent ViewGroup and isViewFromObject(View, Object) could be implemented as return view == object;.
一个非常简单的 PagerAdapter 可以选择使用页面 Views 本身作为关键对象,在创建后从 instantiateItem(ViewGroup, int) 返回它们并将它们添加到父 ViewGroup。匹配的 destroyItem(ViewGroup, int, Object) 实现将从父 ViewGroup 中删除 View,并且 isViewFromObject(View, Object) 可以实现为 return view == object;。
So my conclusion is that ViewPager
relies on its underlying adapter to explicitly add/remove its children in instantiateItem()
/destroyItem()
. That is, if your adapter is a subclass of PagerAdapter
, your subclass must implement this logic.
所以我的结论是ViewPager
依赖其底层适配器在instantiateItem()
/ 中显式添加/删除其子项destroyItem()
。也就是说,如果您的适配器是 的子类PagerAdapter
,则您的子类必须实现此逻辑。
Side note: be aware of thisif you use lists inside ViewPager
.
附注:知道这个,如果你使用的内部列表ViewPager
。
回答by eyeslave
If the ViewPager
is set inside a Fragment with a FragmentPagerAdapter
, use getChildFragmentManager()
instead of getSupportFragmentManager()
as the parameter to initialize your FragmentPagerAdapter
.
如果在ViewPager
带有 a 的 Fragment 中设置了FragmentPagerAdapter
,请使用getChildFragmentManager()
而不是getSupportFragmentManager()
作为参数来初始化您的FragmentPagerAdapter
.
mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());
Instead of
代替
mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
回答by Ringen
I had the exact same problem but I actually destroyed the view in destroyItem (I thought). The problem however was that I destroyed it using viewPager.removeViewAt(index);
insted of viewPager.removeView((View) object);
我有完全相同的问题,但我实际上破坏了 destroyItem 中的视图(我认为)。然而,问题是我使用viewPager.removeViewAt(index);
insted销毁了它viewPager.removeView((View) object);
Wrong:
错误的:
@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
viewPager.removeViewAt(position);
}
Right:
对:
@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
viewPager.removeView((View) object);
}
回答by Chris Banes
ViewPager tries to do clever stuff around re-using items, but it requires you to return new item positions when things have changed. Try adding this to your PagerAdapter:
ViewPager 尝试在重用项目方面做一些聪明的事情,但它要求您在事情发生变化时返回新的项目位置。尝试将其添加到您的 PagerAdapter:
public int getItemPosition (Object object) { return POSITION_NONE; }
It basically tells ViewPager that everything has changed (and forces it to re-instantiate everything). That's the only thing I can think of off the top of my head.
它基本上告诉 ViewPager 一切都改变了(并强制它重新实例化一切)。这是我唯一能想到的事情。
回答by Zeero0
Tried too many solutions but unexpectedly viewPager.post()
worked
尝试了太多的解决方案,但出乎意料地viewPager.post()
奏效
mAdapter = new NewsVPAdapter(getContext(), articles);
viewPager.post(new Runnable() {
@Override
public void run() {
viewPager.setAdapter(mAdapter);
}
});
回答by Chris Sprague
I ran into this same problem when using a ViewPager and FragmentStatePagerAdapter. I tried using a handler with a 3 second delay to call invalidate() and requestLayout() but it didn't work. What did work was resetting the viewPager's background color as follows:
我在使用 ViewPager 和 FragmentStatePagerAdapter 时遇到了同样的问题。我尝试使用延迟 3 秒的处理程序来调用 invalidate() 和 requestLayout() 但它没有用。什么工作是重置viewPager的背景颜色如下:
MyFragment.java
我的片段
private Handler mHandler;
private Runnable mBugUpdater;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = new ViewPager(getActivity());
//...Create your adapter and set it here...
mHandler = new Handler();
mBugUpdater = new Runnable(){
@Override
public void run() {
mVp.setBackgroundColor(mItem.getBackgroundColor());
mHandler = null;
mBugUpdater = null;
}
};
mHandler.postDelayed(mBugUpdater,50);
return rootView;
}
@Override
public void onPause() {
if(mHandler != null){
//Remove the callback if it hasn't triggered yet
mHandler.removeCallbacks(mBugUpdater);
mHandler = null;
mBugUpdater = null;
}
super.onPause();
}
回答by dfinn
I had a problem with the same symptoms, but a different cause that turned out to be a silly mistake on my part. Thought I'd add it here in case it helps anyone.
我遇到了相同症状的问题,但结果证明是我犯了一个愚蠢的错误。我想我会在这里添加它,以防它对任何人有帮助。
I had a ViewPager using FragmentStatePagerAdapter which used to have two fragments, but I later added a third. However, I forgot that the default off screen page limit is 1 -- so, when I'd switch to the new third fragment, the first one would get destroyed, then recreated after switching back. The problem was that my activity was in charge of notifying these fragments to initialize their UI state. This happened to work when the activity and fragment lifecycles were the same, but to fix it I had to change the fragments to initialize their own UI during their startup lifecycle. In the end I also wound up changing setOffscreenPageLimit to 2 so that all three fragments were kept alive at all times (safe in this case since they were not very memory intensive).
我有一个使用 FragmentStatePagerAdapter 的 ViewPager,它曾经有两个片段,但后来我添加了第三个片段。但是,我忘记了默认的屏幕外页面限制是 1——所以,当我切换到新的第三个片段时,第一个片段会被破坏,然后在切换回来后重新创建。问题是我的活动负责通知这些片段初始化它们的 UI 状态。当 Activity 和 Fragment 生命周期相同时,这会起作用,但要修复它,我必须更改 Fragment 以在它们的启动生命周期中初始化它们自己的 UI。最后,我还将 setOffscreenPageLimit 更改为 2,以便所有三个片段始终保持活动状态(在这种情况下是安全的,因为它们不是非常密集的内存)。
回答by Sparky
The Android Support Library has a demo Activity that includes a ViewPager with a ListView on every page. You should probably have a look and see what it does.
Android 支持库有一个演示 Activity,其中包含一个 ViewPager,每个页面上都有一个 ListView。您可能应该看看它的作用。
In Eclipse (with Android Dev Tools r20):
在 Eclipse 中(使用 Android Dev Tools r20):
- Select
New > Android Sample Project
- Select your target API level (I suggest the newest available)
- Select
Support4Demos
- Right-click the project and select
Android Tools > Add Support Library
- Run the app and select
Fragment
and thenPager
- 选择
New > Android Sample Project
- 选择您的目标 API 级别(我建议使用最新的可用)
- 选择
Support4Demos
- 右键单击项目并选择
Android Tools > Add Support Library
- 运行应用程序并选择
Fragment
然后Pager
The code for this is in src/com.example.android.supportv4.app/FragmentPagerSupport.java
. Good luck!
此代码在src/com.example.android.supportv4.app/FragmentPagerSupport.java
. 祝你好运!
回答by Tim O'Brien
I ran into this and had very similar issues. I even asked it on stack overflow.
我遇到了这个并且遇到了非常相似的问题。我什至在堆栈溢出时问过它。
For me, in the parent of the parentof my view someone subclassed LinearLayout
and overrode requestLayout()
without calling super.requestLayout()
. This prevented onMeasure
and onLayout
from being called on my ViewPager (although hierarchyviewer manually calls these). Without being measured they'll show up as blank in ViewPager.
对我来说,在我视图的父级的父级中,有人子类化LinearLayout
并覆盖requestLayout()
而不调用super.requestLayout()
. 这阻止onMeasure
并onLayout
在我的 ViewPager 上被调用(尽管hierarchyviewer 手动调用这些)。未经测量,它们将在 ViewPager 中显示为空白。
So check your containing views. Make sure they subclass from View and don't blindly override requestLayout or anything similar.
因此,请检查您的包含视图。确保它们从 View 子类化,不要盲目覆盖 requestLayout 或任何类似的东西。
回答by Oleg Vaskevich
Had the same issue, which is something to do with ListView
(because my empty view shows up fine if the list is empty). I just called requestLayout()
on the problematic ListView
. Now it draws fine!
有同样的问题,这与ListView
(因为如果列表为空,我的空视图显示良好)。我只是调用requestLayout()
了有问题的ListView
. 现在画得很好!