我正在使用兼容性库中的ViewPager。我已经成功地让它显示几个视图,我可以通过页面。

但是,我很难弄清楚如何用一组新的视图更新ViewPager。

我已经尝试了各种各样的事情,比如调用mAdapter.notifyDataSetChanged(), mviewpage .invalidate(),甚至在每次我想使用新的数据列表时创建一个全新的适配器。

没有任何帮助,textviews保持不变,从原始数据。

更新: 我做了一个小测试项目,我几乎能够更新视图。我将在下面粘贴这个类。

然而,似乎没有更新的是第二个视图,“B”仍然存在,它应该在按下更新按钮后显示“Y”。

public class ViewPagerBugActivity extends Activity {

    private ViewPager myViewPager;
    private List<String> data;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        data = new ArrayList<String>();
        data.add("A");
        data.add("B");
        data.add("C");

        myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
        myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

        Button updateButton = (Button) findViewById(R.id.update_button);
        updateButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                updateViewPager();
            }
        });
    }

    private void updateViewPager() {
        data.clear();
        data.add("X");
        data.add("Y");
        data.add("Z");
        myViewPager.getAdapter().notifyDataSetChanged();
    }

    private class MyViewPagerAdapter extends PagerAdapter {

        private List<String> data;
        private Context ctx;

        public MyViewPagerAdapter(Context ctx, List<String> data) {
            this.ctx = ctx;
            this.data = data;
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public Object instantiateItem(View collection, int position) {
            TextView view = new TextView(ctx);
            view.setText(data.get(position));
            ((ViewPager)collection).addView(view);
            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
             ((ViewPager) collection).removeView((View) view);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }

        @Override
        public void startUpdate(View arg0) {
        }

        @Override
        public void finishUpdate(View arg0) {
        }
    }
}

当前回答

我有同样的问题,我的解决方案是使用FragmentPagerAdapter覆盖FragmentPagerAdapter#getItemId(int位置):

@Override
public long getItemId(int position) {
    return mPages.get(position).getId();
}

默认情况下,此方法返回项目的位置。我认为ViewPager检查itemId是否被更改,只有当它被更改时才重新创建页面。但是未覆盖的版本返回与itemId相同的位置,即使页面实际上不同,ViewPager没有定义页面被替换并需要重新创建。

要使用此功能,每个页面都需要长id。通常,它应该是唯一的,但我建议,对于这种情况,它应该不同于相同页面的前一个值。因此,在适配器或随机整数(具有广泛的分布)中使用连续计数器是可能的。

我认为这是更一致的方式,而不是使用标签的视图作为解决方案,在这个主题中提到。但可能不是所有情况都适用。

其他回答

有几种方法可以实现这一点。

第一种方法更简单,但效率更低。

重写PagerAdapter中的getItemPosition,如下所示:

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

这样,当您调用notifyDataSetChanged()时,视图分页器将删除所有视图并重新加载它们。这样就得到了装填效果。

第二个选项是Alvaro Luis Bustamante(以前是alvarolb)建议的,在实例化一个新视图时使用instantiateItem()中的setTag()方法。然后,您可以使用findViewWithTag()来查找要更新的视图,而不是使用notifyDataSetChanged()。

结论

如果您有很多视图,或者希望支持修改任何特定的项和/或视图(在任何时候快速),那么第二种方法(标记)是非常灵活和高性能的,因为它可以防止重新创建所有未修改的视图。 (alvarolb的原创研究值得称赞。)

但如果你的应用只有“刷新”功能(甚至不允许单个条目的更改),或者只有很少的条目,请使用第一种方法,因为它节省了开发时间。

对我有用的是重写finishUpdate方法,它在转换结束时被调用,并在那里执行notifyDataSetChanged():

public class MyPagerAdapter extends FragmentPagerAdapter {
    ...
    @Override
    public void finishUpdate(ViewGroup container) {
        super.finishUpdate(container);
        this.notifyDataSetChanged();
    }
    ...
}

我认为我已经创建了一种简单的方法来通知数据集的更改:

首先,稍微改变一下instantiateItem函数的工作方式:

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View rootView = mInflater.inflate(...,container, false);
        rootView.setTag(position);
        updateView(rootView, position);
        container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mViewPager.setObjectForPosition(rootView, position);
        return rootView;
    }

对于“updateView”,用你想要填充的所有数据填充视图(setText,setBitmapImage,…)

验证destroyView是这样工作的:

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object obj) {
        final View viewToRemove = (View) obj;
        mViewPager.removeView(viewToRemove);
    }

现在,假设你需要更改数据,然后调用PagerAdapter上的下一个函数:

    public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
            final NotifyLocation toPos) {
        final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
        final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        if (fromPosInt <= toPosInt) {
            notifyDataSetChanged();
            for (int i = fromPosInt; i <= toPosInt; ++i) {
                final View pageView = viewPager.findViewWithTag(i);
                mPagerAdapter.updateView(pageView, i);
            }
        }
    }

public enum NotifyLocation {
    MOST_LEFT, CENTER, MOST_RIGHT
}

例如,如果你希望通知viewPager所显示的所有视图,有一些东西发生了变化,你可以调用:

notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);

就是这样。

而不是返回POSITION_NONE并再次创建所有片段,您可以按照我在这里建议的那样做:动态更新ViewPager ?

This is for all those like me, which need to update the Viewpager from a service (or other background thread) and none of the proposals have worked: After a bit of logchecking i realized, that the notifyDataSetChanged() method never returns. getItemPosition(Object object) is called an all ends there without further processing. Then i found in the docs of the parent PagerAdapter class (is not in the docs of the subclasses), "Data set changes must occur on the main thread and must end with a call to notifyDataSetChanged() ". So, the working solution in this case was (using FragmentStatePagerAdapter and getItemPosition(Object object) set to return POSITION_NONE) :

然后调用notifyDataSetChanged():

runOnUiThread(new Runnable() {
         @Override
         public void run() {
             pager.getAdapter().notifyDataSetChanged();
         }
     });