ViewPager不重绘内容,保持/变为空白

时间:2012-07-31 09:24:32

标签: android android-viewpager

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

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

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

这与compatibility-v4_r7库有关。我也尝试使用最新版本,因为它声称修复了与查看寻呼机相关的许多问题,但它更糟糕的是(例如,手势被打破,以至于它不会让我不再遍历所有页面。)

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

13 个答案:

答案 0 :(得分:53)

如果ViewPager设置在带FragmentPagerAdapter的片段内,请使用getChildFragmentManager()代替getSupportFragmentManager()作为初始化FragmentPagerAdapter的参数。

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

而不是

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

答案 1 :(得分:44)

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

  1. 我们的适配器未删除destroyItem()中的视图。
  2. 我们正在缓存视图,因此我们只需要对我们的布局进行一次通知,而且,由于我们没有删除destroyItem()中的视图,因此我们没有在instantiateItem()中添加它而只是返回与当前位置对应的缓存视图。
  3. 我没有深入研究ViewPager的源代码 - 而且你必须这么做是不明确的 - 但文档说:

      

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

    和:

      

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

    所以我的结论是ViewPager依赖于其基础适配器在instantiateItem() / destroyItem()中明确添加/删除其子项。也就是说,如果你的适配器是PagerAdapter的子类,你的子类必须实现这个逻辑。

    附注:如果您使用ViewPager内的列表,请注意this

答案 2 :(得分:21)

我遇到了完全相同的问题,但实际上我在destroyItem中破坏了视图(我想)。但问题是我使用viewPager.removeViewAt(index);

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);
}

答案 3 :(得分:8)

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

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

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

答案 4 :(得分:0)

Android支持库有一个演示Activity,其中包含一个ViewPager,每个页面都有一个ListView。你可能应该看看它的作用。

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

  1. 选择New > Android Sample Project
  2. 选择目标API级别(我建议使用最新版本)
  3. 选择Support4Demos
  4. 右键单击该项目,然后选择Android Tools > Add Support Library
  5. 运行该应用并选择Fragment,然后选择Pager
  6. 此代码位于src/com.example.android.supportv4.app/FragmentPagerSupport.java。祝你好运!

答案 5 :(得分:0)

我碰到了这个并且遇到了非常类似的问题。我甚至在堆栈溢出时asked it

对我来说,在我的观点的父的父中,有人将LinearLayout隐藏起来并覆盖requestLayout()而不调用super.requestLayout()。这阻止了我的ViewPager上调用onMeasureonLayout(尽管hierarchyviewer手动调用这些)。如果不进行测量,它们将在ViewPager中显示为空白。

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

答案 6 :(得分:0)

有同样的问题,这与ListView有关(因为如果列表为空,我的空视图显示正常)。我刚刚在有问题的requestLayout()上致电ListView。现在它画好了!

答案 7 :(得分: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();
     }

答案 8 :(得分:0)

我遇到了同样症状的问题,但是另一个原因是我自己犯了一个愚蠢的错误。我想在这里添加它以防万一它可以帮助任何人。

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

答案 9 :(得分:0)

尝试了太多解决方案,但viewPager.post()出乎意料地

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

答案 10 :(得分:-1)

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

@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];

答案 11 :(得分:-1)

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

致电viewPager.setCurrentItem(position, true);

设置数据后,

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

答案 12 :(得分:-1)

对于Kotlin用户:

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