我正在使用兼容性库中的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) {
        }
    }
}

当前回答

我使用Tablayout与ViewPagerAdapter。为了在片段之间传递数据或在片段之间进行通信,使用下面的代码,它工作得非常好,并在片段出现时刷新它。第二段点击按钮里面写下面的代码。

b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            String text=e1.getText().toString(); // get the text from EditText

            // move from one fragment to another fragment on button click
            TabLayout tablayout = (TabLayout) getActivity().findViewById(R.id.tab_layout); // here tab_layout is the id of TabLayout which is there in parent Activity/Fragment
            if (tablayout.getTabAt(1).isSelected()) { // here 1 is the index number of second fragment i-e current Fragment

                LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
                Intent i = new Intent("EDIT_TAG_REFRESH");
                i.putExtra("MyTextValue",text);
                lbm.sendBroadcast(i);

            }
            tablayout.getTabAt(0).select(); // here 0 is the index number of first fragment i-e to which fragment it has to moeve

        }
    });

下面是必须在第一个片段(在我的情况下)i-e中接收片段的代码。

MyReceiver r;
Context context;
String newValue;
public void refresh() {
    //your code in refresh.
    Log.i("Refresh", "YES");
}
public void onPause() {
    super.onPause();

    LocalBroadcastManager.getInstance(context).unregisterReceiver(r);
}
public void onResume() {
    super.onResume();
    r = new MyReceiver();
    LocalBroadcastManager.getInstance(getActivity()).registerReceiver(r,
            new IntentFilter("EDIT_TAG_REFRESH"));
} // this code has to be written before onCreateview()


 // below code can be written any where in the fragment
 private class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    PostRequestFragment.this.refresh();
        String action = intent.getAction();
        newValue=intent.getStringExtra("MyTextValue");
        t1.setText(newValue); // upon Referesh set the text
    }
}

其他回答

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

在OP提出他的问题两年半之后,这个问题仍然,嗯,仍然是一个问题。显然谷歌在这方面的优先级不是特别高,所以我没有找到解决方案,而是找到了一个变通办法。对我来说,最大的突破是找到了问题的真正原因(见本文中公认的答案)。一旦问题明显是任何活动页面都没有正确刷新,我的解决方法就很明显了:

在我的片段(几页)中:

I took all the code which populates the form out of onCreateView and put it in a function called PopulateForm which may be called from anywhere, rather than by the framework. This function attempts to get the current View using getView, and if that is null, it just returns. It's important that PopulateForm contains only the code that displays - all the other code which creates FocusChange listeners and the like is still in OnCreate Create a boolean which can be used as a flag indicating the form must be reloaded. Mine is mbReloadForm Override OnResume() to call PopulateForm() if mbReloadForm is set.

在我的活动中,我做页面的加载:

Go to page 0 before changing anything. I'm using FragmentStatePagerAdapter, so I know that two or three pages are affected at most. Changing to page 0 ensures I only ever have the problem on pages 0, 1 and 2. Before clearing the old list, take it's size(). This way you know how many pages are affected by the bug. If > 3, reduce it to 3 - if you're using a a different PagerAdapter, you'll have to see how many pages you have to deal with (maybe all?) Reload the data and call pageAdapter.notifyDataSetChanged() Now, for each of the affected pages, see if the page is active by using pager.getChildAt(i) - this tells you if you have a view. If so, call pager.PopulateView(). If not, set the ReloadForm flag.

在此之后,当您重新加载第二组页面时,该错误仍然会导致一些页面显示旧数据。但是,现在它们将被刷新,您将看到新的数据-您的用户不会知道页面是不正确的,因为这种刷新将在他们看到页面之前发生。

希望这能帮助到一些人!

你只需要这段简单的代码:

代码:

private void updateAdapter() {
    if (adapterViewPager != null) {
        int from = vpMyViewPager.getCurrentItem() - vpMyViewPager.getOffscreenPageLimit();
        int to = vpMyViewPager.getCurrentItem() + vpMyViewPager.getOffscreenPageLimit();
        vpMyViewPager.removeAllViews();
        for (int i = from; i <= to; i++) {
            if (i < 0) {
                continue;
            }
            adapterViewPager.instantiateItem(vpMyViewPager, i);
        }
    }
}

解释:

If you haven't changed offscreenPageLimit of the ViewPager, it always has 3 to 4 children depending on which direction you are going. And in order to show the correct content inside its children, it uses the adapter to get the right content. now when you call removeAllViews() on your ViewPager, only 3 to 4 Views are actually being removed from the Window's hierarchy, and by calling instantiateItem(ViewGroup viewPager, int index), you are only recreating 3 to 4 Views. Then everything is back to normal, you swipe and scroll, the ViewPager shows contents using its adapter. The number of its children is not the same on all devices in all situations, for example if you set the offscreenPageLimit to 5, it will probably have around 11 to 12 children, but that's all, it's not much. it's fast.

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

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

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

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

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

我遇到过类似的问题,我有四个页面,其中一个页面更新了其他三个页面的视图。我能够更新widget (SeekBars, TextViews等)上的页面相邻的当前页面。当调用mTabsAdapter.getItem(position)时,最后两个页面将有未初始化的小部件。

为了解决我的问题,我在调用getItem(位置)之前使用了setSelectedPage(索引)。这将实例化页面,使我能够更改每个页面上的值和小部件。

在所有的更新之后,我将使用setSelectedPage(位置),然后是notifyDataSetChanged()。

您可以在主更新页面的ListView中看到轻微的闪烁,但并不明显。我还没有完全测试它,但它确实解决了我眼前的问题。