我无法更新ViewPager中的内容。

FragmentPagerAdapter类中instantiateItem()和getItem()方法的正确用法是什么?

我只使用getItem()来实例化并返回我的片段:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

这很有效。但我不能改变内容。

所以我发现这个:ViewPager PagerAdapter没有更新视图

我的方法是对instantiateItem()方法中的任何实例化视图使用setTag()方法

现在我想实现instantiateItem()来实现这一点。但我不知道我必须返回什么(类型是对象)和什么与getItem(int位置)的关系?

我读了参考资料:

public abstract Fragment getItem (int position) Return the Fragment associated with a specified position. public Object instantiateItem (ViewGroup container, int position) Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup). Parameters container The containing View in which the page will be shown. position The page position to be instantiated. Returns Returns an Object representing the new page. This does not need to be a View, but can be some other container of the page.

但我还是不明白

这是我的代码。我用的是v4支持包。

查看页面测试

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

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

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

我的片段适配器

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

    @Override
    public int getCount() {
        return slideCount;
    }

    public void setData(String[] data) {
        this.data = data;
    }

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

我的片段

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

这里还有一个人也有类似的问题,没有答案 http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


当前回答

使用ViewPager2和FragmentStateAdapter:

ViewPager2支持动态更新数据。

在文档中有一个关于如何让它工作的重要说明:

注意:DiffUtil实用程序类依赖于按ID标识项目。如果使用ViewPager2对可变集合进行分页,还必须重写getItemId()和containsItem()。(强调我的)

基于ViewPager2文档和Android的Github样本项目,我们需要采取几个步骤:

设置FragmentStateAdapter并覆盖以下方法:getItemCount, createFragment, getItemId和containsItem(注意:FragmentStatePagerAdapter不被ViewPager2支持) 将适配器附加到ViewPager2 使用DiffUtil向ViewPager2发送列表更新(不需要使用DiffUtil,如示例项目所示)


例子:

private val items: List<Int>
    get() = viewModel.items
private val viewPager: ViewPager2 = binding.viewPager

private val adapter = object : FragmentStateAdapter(this@Fragment) {
    override fun getItemCount() = items.size
    override fun createFragment(position: Int): Fragment = MyFragment()
    override fun getItemId(position: Int): Long = items[position].id
    override fun containsItem(itemId: Long): Boolean = items.any { it.id == itemId }
}
viewPager.adapter = adapter
    
private fun onDataChanged() {
    DiffUtil
        .calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize(): Int = viewPager.adapter.itemCount
            override fun getNewListSize(): Int = viewModel.items.size
            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                viewPager.adapter?.getItemId(oldItemPosition) == viewModel.items[newItemPosition].id

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                areItemsTheSame(oldItemPosition, newItemPosition)
        }, false)
        .dispatchUpdatesTo(viewPager.adapter!!)
}

其他回答

而不是从getItemPosition()返回POSITION_NONE并引起完整的视图重建,这样做:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(updateData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

你的片段应该实现UpdateableFragment接口:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

以及界面:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

你的数据类:

public class UpdateData {
    //whatever you want here
}

在代码中的notifyDataSetChanged()之后尝试在ViewPager上销毁drawingcache()。

这可能对某些人有帮助-在我插入一个新页面时,视图寻呼机要求现有片段的位置两次,但没有要求新项目的位置,导致不正确的行为和数据不显示。

为FragmentStatePagerAdapter复制源代码(似乎已经很久没有更新了)。 覆盖notifyDataSetChanged () @Override 公共无效notifyDataSetChanged() { mFragments.clear (); super.notifyDataSetChanged (); } 为destroyItem()添加一个完整性检查以防止崩溃: if (position < mFragments.size()) { mFragments。集(位置、零); }

我遇到过这个问题,今天终于解决了,所以我把我学到的东西写下来,希望对那些刚接触Android ViewPager并像我一样更新的人有帮助。我使用FragmentStatePagerAdapter在API级别17,目前只有2个片段。我想一定有什么地方不对,请指正,谢谢。

Serialized data has to be loaded into memory. This can be done using a CursorLoader/AsyncTask/Thread. Whether it's automatically loaded depends on your code. If you are using a CursorLoader, it's auto-loaded since there is a registered data observer. After you call viewpager.setAdapter(pageradapter), the adapter's getCount() is constantly called to build fragments. So if data is being loaded, getCount() can return 0, thus you don't need to create dummy fragments for no data shown. After the data is loaded, the adapter will not build fragments automatically since getCount() is still 0, so we can set the actually loaded data number to be returned by getCount(), then call the adapter's notifyDataSetChanged(). ViewPager begin to create fragments (just the first 2 fragments) by data in memory. It's done before notifyDataSetChanged() is returned. Then the ViewPager has the right fragments you need. If the data in the database and memory are both updated (write through), or just data in memory is updated (write back), or only data in the database is updated. In the last two cases if data is not automatically loaded from the database to memory (as mentioned above). The ViewPager and pager adapter just deal with data in memory. So when data in memory is updated, we just need to call the adapter's notifyDataSetChanged(). Since the fragment is already created, the adapter's onItemPosition() will be called before notifyDataSetChanged() returns. Nothing needs to be done in getItemPosition(). Then the data is updated.

经过几个小时的挫折,同时尝试所有上述解决方案来克服这个问题,也尝试了其他类似问题的许多解决方案,比如这个,这个,这些都失败了,我解决了这个问题,并使ViewPager破坏旧的片段,并用新的片段填充分页器。我解决的问题如下:

1)使ViewPager类扩展FragmentPagerAdapter,如下:

 public class myPagerAdapter extends FragmentPagerAdapter {

2)为存储标题和片段的ViewPager创建一个Item,如下所示:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3)让ViewPager的构造函数把我的FragmentManager实例存储在我的类中,如下所示:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4)创建一个方法,通过直接从fragmentManager本身删除之前的所有片段,用新数据重新设置适配器数据,使适配器再次从新列表中设置新片段,如下所示:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5)从容器Activity或Fragment中,不要用新的数据重新初始化适配器。使用setPagerItems方法设置新数据,如下所示:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

我希望这能有所帮助。