ViewPager不会重新绘制内容,而是保持空白


86

我们在这里遇到了ViewPager的一个非常奇怪的问题。我们在每个ViewPager页面上嵌入列表,并在更新列表数据时在列表适配器和视图分页器适配器上触发notifyDataSetChanged。

我们观察到的是,有时页面不会更新其视图树,即保持空白,有时甚至在分页时消失。当来回翻动几次时,内容将突然重新出现。似乎Android在这里缺少视图更新。我还注意到,在使用层次结构查看器进行调试时,选择视图将始终使其重新出现,这显然是因为层次结构查看器会强制所选视图重新绘制自身。

但是我无法以编程方式进行这项工作;使列表视图无效,甚至使整个视图分页器无效,都无效。

这是与compatible-v4_r7库一起使用的。我还尝试使用最新的修订版,因为它声称可以解决与视图分页器有关的许多问题,但是却使事情变得更糟(例如,手势被打断了,以至于有时它不再让我翻阅所有页面。)

是否还有其他人也遇到了这些问题,或者您是否知道可能是什么原因造成的?

Answers:


44

我们终于设法找到了解决方案。显然,我们的实施存在两个问题:

  1. 我们的适配器未删除中的视图destroyItem()
  2. 我们正在缓存视图,因此我们只需要对布局进行一次膨胀,并且由于我们没有在中删除视图destroyItem(),因此我们没有在其中添加视图,instantiateItem()而只是返回与当前位置相对应的缓存视图。

我没有对ViewPager-的源代码进行过深入的研究-并不一定要明确指出-但是文档说:

destroyItem()
删除给定位置的页面。适配器负责从其容器中删除视图,尽管它仅必须确保在从finishUpdate(ViewGroup)返回时完成此操作。

和:

一个非常简单的PagerAdapter可以选择将页面Views本身用作关键对象,在创建后从InstantiateItem(ViewGroup,int)返回它们,并将其添加到父ViewGroup。匹配的destroyItem(ViewGroup,int,Object)实现将从父ViewGroup中删除该View,并且isViewFromObject(View,Object)可以实现为return view == object;。

因此,我的结论是,ViewPager依靠其基础适配器在instantiateItem()/中显式添加/删除其子级destroyItem()。也就是说,如果您的适配器是的子类PagerAdapter,则您的子类必须实现此逻辑。

注意:如果您在中使用列表,请注意这一点ViewPager


2
我遇到了同样的问题,但是使用FragmentPagerAdapter,它为我处理了onDestroy。这里的其他解决方案也不起作用。
格雷格·恩尼斯

14
也可能是问题所在,是有人正在使用getFragmentManger而不是GetChildFragmentManager
Boy

@Boy什么是GetChildFragmentManager?
在洞中射击

1
@Boy Wooooow ....我整整整整两天想弄清楚为什么我什么都没更新。谢谢!(他们应该确实在Google的文档中确实发出了使用childfragmentmanager的通知,这完全不是您需要其他经理)。
user0721090601

@futtetennista我在做同样的..面临这个问题..你可以检查.. stackoverflow.com/questions/61727835/...
AskQ

55

如果将ViewPager设置为带有的Fragment内的FragmentPagerAdapter,请使用getChildFragmentManager()而不是getSupportFragmentManager()作为参数来初始化FragmentPagerAdapter

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

代替

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

2
很好,当使用FragmentPagerAdapter时,似乎可以解决我的问题
Tobliug

1
谢谢-这工作。我试图迫使getItem()FragmentPagerAdapter其嵌套在一个重建的片段。
kosiara-Bartosz Kosarzycki '16

哇,这部作品像魅力一样。我想问题是由于片段中的viewpager膨胀所致
Thecarisma

我希望我们有这样的拍手,比如中,所以我可以给你1000多个拍手。干得好,这应该是公认的答案
Codelicious

21

我遇到了完全相同的问题,但实际上是在destroyItem中破坏了视图(我认为)。但是问题是我用viewPager.removeViewAt(index);insted ofviewPager.removeView((View) object);

错误:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

对:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}

感谢您的提示。一直在调查这个问题很长时间。
莫里茨

2
这对我有用,但是您知道为什么第一个是问题吗?
HannahMitt 2014年

@HannahMitt removeViewAt将删除ViewPager当前子级列表中特定位置的任何视图,并且可以按任何顺序将页面添加到ViewPager(因此页面0实际上可能位于ViewPager中索引为1的位置或其他位置)。removeView将遍历子级列表,并完全删除指定的对象。

2
不适合我:无法投射视图。这里的对象是一个片段。
FRK

viewpager必须是静态的吗?
卡纳加林加姆'19

10

ViewPager尝试对重复使用的项目进行巧妙的处理,但是它要求您在更改后返回新的项目位置。尝试将其添加到您的PagerAdapter中:

public int getItemPosition (Object object) { return POSITION_NONE; }

它基本上告诉ViewPager一切都已更改(并强制其重新实例化所有内容)。这是我唯一想到的事情。


1
是的,我已经在以下线程中阅读了有关此选项的信息:stackoverflow.com/questions/7263291/…-但是,这似乎像大锤一样。当然必须有一种更优雅的方式吗?我想知道是否与将列表用作传呼页面有关?
马赛厄斯

这有点大刀阔斧,但是您可以从那做起。即从该方法返回正确的值。
克里斯·巴内斯

@ChrisBanes正如您所说的return POSITION_NONE;forces it to re-instantiate everything但是就我而言,我不想重新实例化所有内容,因此可以在view不清除现有内容的情况下将其删除吗?请让我知道
Ritesh Adulkar

您是生活中的味蕾
mask8

2

试了太多的解决方案,却不料viewPager.post()工作

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });

1
天哪。为什么没人支持这个?当我重新输入一个片段并且里面的viewpager不呈现自身时,我得到了奇怪的行为,并且像这样延迟它实际上解决了这个问题!
Fugogugo

0

Android支持库有一个演示活动,其中包括一个ViewPager,每个页面上都有一个ListView。您可能应该看看并了解它的作用。

在Eclipse中(使用Android Dev Tools r20):

  1. 选择 New > Android Sample Project
  2. 选择您的目标API级别(我建议提供最新的API级别)
  3. 选择 Support4Demos
  4. 右键单击该项目,然后选择 Android Tools > Add Support Library
  5. 运行该应用程序Fragment,然后选择Pager

的代码在中src/com.example.android.supportv4.app/FragmentPagerSupport.java。祝好运!


谢谢-我来看一下!也许我会发现我们犯错的地方。
马赛厄斯

0

我遇到了这个问题,并且遇到了非常相似的问题。我什至在堆栈溢出时问了它

对我而言,在我认为的父项的父项中,有人LinearLayout继承了子类并推翻,requestLayout()而没有调用super.requestLayout()。这阻止onMeasureonLayout被调用我的ViewPager(虽然hierarchyviewer手动调用这些)。如果不进行测量,它们将在ViewPager中显示为空白。

因此,请检查您的包含视图。确保它们是View的子类,并且不要盲目重写requestLayout或类似内容。



0

使用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();
     }

0

我有同样症状的问题,但原因不同,结果却是我的一个愚蠢的错误。以为我会在这里添加它,以防它对任何人有帮助。

我有一个使用FragmentStatePagerAdapter的ViewPager,它曾经有两个片段,但后来又添加了第三个片段。但是,我忘记了默认的屏幕外页面限制是1-因此,当我切换到新的第三个片段时,第一个片段将被销毁,然后在切回后重新创建。问题是我的活动是负责通知这些片段以初始化其UI状态。当活动和片段的生命周期相同时,这确实起作用了,但是要解决此问题,我必须更改片段以在其启动生命周期中初始化自己的UI。最后,我还结束了将setOffscreenPageLimit更改为2的操作,以便所有三个片段始终保持活动状态(在这种情况下是安全的,因为它们占用的内存不多)。


-1

我有类似的问题。我缓存视图,因为我只需要3个视图ViewPager。当我向前滑动时一切正常,但是当我开始向后滑动时发生错误,它表示“我的视图已经有一个父视图”。解决方案是手动删除不需要的项目。

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];

-1

对我来说,问题是在应用程序进程被终止后又回到了活动。我正在使用从Android来源修改而来的自定义视图分页器适配器。视图分页器直接嵌入到活动中。

呼唤 viewPager.setCurrentItem(position, true);

(带有动画)在设置数据和notifyDataSetChanged()之后似乎可以工作,但是如果参数设置为false,那么它将无效,并且片段为空。这是一个边缘案例,可能对某人有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.