动态更新ViewPager?

336

我无法更新ViewPager中的内容。

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

我之前只使用了getItem()来实例化和返回我的fragment:

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

这个方法很有效,但我无法更改内容。
所以我找到了这个:ViewPager PagerAdapter not updating the View

“我的方法是在instantiateItem()方法中为任何实例化的视图使用setTag()方法”

现在我想要实现instantiateItem()来做到这一点。但我不知道我必须返回什么(类型为Object),以及getItem(int position)之间的关系是什么?
我阅读了reference
  • public abstract Fragment getItem (int position)

    返回与指定位置相关联的Fragment。

  • public Object instantiateItem (ViewGroup container, int position)

    为给定的位置创建页面。适配器负责将视图添加到此处给定的容器中,尽管它只需确保在从finishUpdate(ViewGroup)返回时完成此操作。

    参数

    container 要显示该页的包含视图。 position 要实例化的页面位置。

    返回值

    返回表示新页面的对象。这不必是View,但可以是页面的其他容器。

但我仍然不理解。

这是我的代码。我正在使用support package v4。

ViewPagerTest

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

我的Fragment适配器

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

我的Fragment

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


为了更好地理解涉及的组件,阅读我的有关带有单独后退导航历史记录的选项卡ViewPager的教程可能会有所帮助。它附带了GitHub上的工作示例和其他资源:https://medium.com/@nilan/separate-back-navigation-for-a-tabbed-view-pager-in-android-459859f607e4 - marktani
遇到了一些问题:http://stackoverflow.com/questions/40149039/viewpager-recyclerview-issue - albert
你可以在GitHub上查看这个示例。希望这个示例能对你有所帮助。 - Cabezas
好的简单解决方案:stackoverflow.com/a/35450575/2162226 - Gene Bo
Google最近推出了ViewPager2,它简化了动态Fragment/View的更新。您可以在https://github.com/googlesamples/android-viewpager2或此答案https://dev59.com/QGw05IYBdhLWcg3wZBAC#57393414上查看示例。 - Yves Delerm
21个回答

2

我曾经遇到过相同的问题,搜索了很多次。在stackoverflow或谷歌上找到的任何答案都无法解决我的问题。我的问题很简单,我有一个列表,并使用viewpager显示该列表。当我向列表头添加新元素并刷新viewpager时,什么也没有改变。我的最终解决方案非常简单,任何人都可以使用。当添加新元素到列表中并希望刷新列表时,首先将viewpager适配器设置为null,然后重新创建适配器并将其设置到viewpager中。

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

请确保您的适配器必须扩展FragmentStatePagerAdapter。


1
我使用EventBus库来更新ViewPager中的Fragment内容。逻辑很简单,就像EventBus如何操作文档中所述。无需控制FragmentPagerAdapter实例。代码如下:

1:定义事件

定义需要更新的消息。

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. 准备订阅者

在需要更新的片段中编写以下代码。

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3. 发布事件

在需要更新参数的其他ActivityFragment中编写以下代码

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

我曾尝试了许多不同的方法,但没有一个真正解决了我的问题。下面是我通过使用大家提供的一些解决方案来解决它的方式。谢谢大家。

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

在onResume()之后,我只想刷新页面0。

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

在我的FragmentsMain中,有一个名为“page”的公共整数变量,它可以告诉我是否是我想要刷新的页面。
public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;

1
这可能对某些人有所帮助——在我的情况下,当插入一个新页面时,视图页面两次询问现有片段的位置,但不询问新项目的位置,导致错误的行为和数据未显示。
  1. Copy the source for for FragmentStatePagerAdapter (seems to have not been updated in ages).

  2. Override notifyDataSetChanged()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
    
  3. Add a sanity check to destroyItem() to prevent crashes:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
    

1

从那个URL中,我找到了解决方案。我在我的代码中使用了这个gist https://gist.github.com/ypresto/8c13cb88a0973d071a64中修改过的FragmentStatePagerAdapter,它开始按照预期工作了。 - Rafael
很好。是的。链接也有几个解决方案。 - cgr

1
我知道我来晚了。我通过在FragmentPagerAdapter#notifyDataSetChanged();之后立即调用TabLayout#setupWithViewPager(myViewPager);来解决了问题。

1

这是我的实现,它将@Bill Phillips的信息整合进来。大部分时间使用片段缓存,除非数据已更改。简单而且似乎运行良好。

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);

0

如果您想要按索引重新创建或重新加载片段,请使用FragmentStatePagerAdapter而不是FragmentPagerAdapter。 例如,如果您想要重新加载除FirstFragment之外的片段,则可以检查实例并像这样返回位置

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }

0

这个解决方案并不适用于所有人,但在我的情况下,ViewPager中的每个Fragment都是一个不同的类,并且只有一个Fragment会存在于内存中。

在这种限制下,这个解决方案是安全的,应该可以在生产环境中使用。

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

如果你有同一片段的多个版本,你可以使用相同的策略调用这些片段上的方法来确定是否是你想要更新的片段。


0

你需要更改 instantiateItem 的 mFragments 元素 getItemPosition。

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

基于 AndroidX FragmentStatePagerAdapter.java,因为在调用 notifyDataSetChanged() 时 mFragments 的元素位置不会改变。
来源:https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Example: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

您可以运行此项目以确认其工作方式。 https://github.com/cuichanghao/infivt


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接