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

当前回答

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

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

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

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

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

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

结论

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

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

其他回答

所有这些答案对我都不起作用。

唯一对我有用的是,我必须再次将适配器设置为viewpager,然后它将刷新内容。

removeView(int pos)在我的PagerAdaper

public void removeView(int index) {
       imageFileNames.remove(index);
       notifyDataSetChanged();
}

无论我在哪里删除文件,我都必须这样做

imagePagerAdapter.removeView(currentPosition);
viewPager.setAdapter(imagePagerAdapter);

编辑:

下面的方法是有效的,你可以使用下面的方法。

public void updateView(int pos){
      viewPager.setAdapter(null);
      imagePagerAdapter =new ImagePagerAdapter(YOUR_CONTEXT,YOUR_CONTENT);
      viewPager.setAdapter(imagePagerAdapter);
      viewPager.setCurrentItem(pos);
}

用你的上下文替换你的YOUR_CONTEXT,用你的内容名称替换你的内容,即更新列表或其他东西。

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

alvarolb给出的答案绝对是最好的方法。基于他的回答,实现这一点的一个简单方法是简单地按位置存储活动视图:

SparseArray<View> views = new SparseArray<View>();

@Override
public Object instantiateItem(View container, int position) {
    View root = <build your view here>;
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

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

然后,一旦覆盖notifyDataSetChanged方法,您就可以刷新视图…

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       <refresh view with new data>
    }
    super.notifyDataSetChanged();
}

实际上,您可以在instantiateItem和notifyDataSetChanged中使用类似的代码来刷新视图。在我的代码中,我使用了完全相同的方法。

ViewPager不是为支持动态视图更改而设计的。

我已经确认了这一点,同时寻找与此相关的另一个bug https://issuetracker.google.com/issues/36956111,特别是https://issuetracker.google.com/issues/36956111#comment56

这个问题有点老了,但是谷歌最近用ViewPager2解决了这个问题。 它将允许用标准的解决方案取代手工的(不需要维护并且可能有bug的)解决方案。它还可以防止像某些回答那样不必要地重新创建视图。

对于ViewPager2的例子,您可以查看https://github.com/googlesamples/android-viewpager2

如果您想使用ViewPager2,您需要在构建中添加以下依赖项。Gradle文件:

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

然后你可以替换你的ViewPager在你的xml文件:

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

在那之后,你需要在你的活动中用ViewPager2替换ViewPager

ViewPager2需要一个RecyclerView。适配器,或者FragmentStateAdapter,在你的例子中,它可以是RecyclerView。适配器

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

在使用TabLayout的情况下,你可以使用TabLayoutMediator:

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

然后,您将能够通过修改适配器的数据并调用notifyDataSetChanged方法来刷新视图

将FragmentPagerAdapter更改为FragmentStatePagerAdapter。

重写getItemPosition()方法并返回POSITION_NONE。

最终,它将侦听视图分页器上的notifyDataSetChanged()。