90

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

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

不过,我无法以编程方式完成这项工作;使列表视图无效,甚至整个视图寻呼机无效。

这与兼容性-v4_r7 库有关。我也尝试使用最新版本,因为它声称修复了许多与视图寻呼机相关的问题,但它使事情变得更糟(例如,手势被破坏,因此有时它不会让我再翻阅所有页面。)

是否还有其他人也遇到这些问题,或者您是否知道可能导致此问题的原因?

4

13 回答 13

57

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

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

代替

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
于 2015-11-24T08:25:23.620 回答
46

我们终于设法找到了解决方案。显然我们的实现遇到了两个问题:

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

我对源代码的研究并没有太深入ViewPager- 而且你必须这样做并不完全明确 - 但文档说:

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

和:

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

所以我的结论是,它ViewPager依赖于它的底层适配器在 / 中显式地添加/删除它的instantiateItem()孩子destroyItem()。也就是说,如果您的适配器是 的子类PagerAdapter,则您的子类必须实现此逻辑。

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

于 2012-08-23T11:14:25.890 回答
23

我遇到了完全相同的问题,但实际上我破坏了 destroyItem 中的视图(我想)。然而问题是我使用viewPager.removeViewAt(index);insted of销毁了它viewPager.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);
}
于 2012-10-24T08:01:41.647 回答
11

ViewPager 试图围绕重复使用项目做一些聪明的事情,但它要求您在事情发生变化时返回新的项目位置。尝试将此添加到您的 PagerAdapter:

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

它基本上告诉 ViewPager 一切都发生了变化(并迫使它重新实例化一切)。这是我唯一能想到的事情。

于 2012-07-31T09:31:35.723 回答
3

尝试了太多解决方案但意外viewPager.post()奏效

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });
于 2019-01-06T18:23:21.400 回答
1

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

在 Eclipse 中(使用 Android 开发工具 r20):

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

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

于 2012-07-31T09:44:19.457 回答
0

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

对我来说,在我认为的父级的父LinearLayout级中,有人requestLayout()在没有调用super.requestLayout(). 这阻止onMeasureonLayout在我的 ViewPager 上被调用(尽管 hierarchyviewer 手动调用这些)。在没有被测量的情况下,它们将在 ViewPager 中显示为空白。

因此,请检查您的包含视图。确保它们是 View 的子类,并且不要盲目地覆盖 requestLayout 或类似的东西。

于 2012-07-31T17:31:08.130 回答
0

有同样的问题,这与ListView(因为如果列表为空,我的空视图显示得很好)。我只是打电话requestLayout()给有问题的ListView。现在画得很好!

于 2012-12-19T22:42:38.540 回答
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();
     }
于 2015-08-30T16:57:24.960 回答
0

我遇到了相同症状的问题,但原因不同,结果证明这是我的一个愚蠢的错误。以为我会在这里添加它,以防它帮助任何人。

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

于 2017-02-02T03:40:09.097 回答
-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];
于 2013-05-24T14:38:40.377 回答
-1

对我来说,问题是在应用程序进程被杀死后回到活动。我正在使用从 Android 源修改的自定义视图寻呼机适配器。视图寻呼机直接嵌入在活动中。

打电话viewPager.setCurrentItem(position, true);

(带动画)设置数据后 notifyDataSetChanged() 似乎工作,但如果参数设置为 false 它不会,并且片段是空白的。这是一个边缘案例,可能对某人有帮助。

于 2017-01-03T18:19:46.503 回答
-1

对于 Kotlin 用户:

在你的碎片中;使用childFragmentManager代替viewPagerAdapter

于 2020-11-27T15:59:59.617 回答