ViewPager PagerAdapter不更新视图


597

我正在使用兼容性库中的ViewPager。我成功地显示了几个可以翻页的视图。

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

我已经尝试过各种方法,例如调用mAdapter.notifyDataSetChanged()mViewPager.invalidate()甚至每次我想使用新的数据列表时都创建一个全新的适配器。

没有任何帮助,文本视图与原始数据保持不变。

更新: 我做了一个小测试项目,几乎可以更新视图了。我将在下面粘贴课程。

似乎没有更新的是第二个视图,仍然保留“ 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) {
        }
    }
}

1
请参阅stackoverflow.com/questions/12510404/…,了解FragmentStatePagerAdapter的潜在错误。
samis 2014年

2
抱歉疏通了。您的问题确实帮助了我,谢谢!我没有得到的一件事是,在活动中更新它时,如何更改对PagerAdapter中数据的引用
Ewanw

让你的适配器扩展了BaseAdapter参见本: stackoverflow.com/questions/13664155/...

1
显然,如果您再次将适配器设置为视图寻呼机,则它将重置。
EpicPandaForce'2

Answers:


856

有几种方法可以实现此目的。

第一种选择比较容易,但是效率低一些。

像这样覆盖getItemPositionPagerAdapter

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

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

第二个选项,阿尔瓦罗路易斯曼特(先前alvarolb)建议,是setTag()方法在instantiateItem()实例化新视图时的。然后notifyDataSetChanged(),您可以使用findViewWithTag()来查找要更新的视图,而不是使用。

第二种方法是非常灵活和高性能的。对alvarolb的原始研究表示敬意。


4
比我的工作更好,似乎没有任何副作用,谢谢。
C0deAttack 2011年

3
我有一个类似的问题。我的viewPager将显示新的searc结果,但没有默认返回到光标的第一个条目。有谁知道在更新数据集时如何重置位置?
mAndroid 2011年

1
@mAndroid,您应该另外提出一个问题并提供更多详细信息。
rui.araujo 2011年

1
返回POSITION_NONE显然会给整个应用程序增加很多效率,因为这会导致Android删除与查询位置相关联的视图。然后,在显示之前,需要再次重新创建视图。解决了视图未更新的问题,但可能不是复杂布局的最佳解决方案。
wufoo 2012年

11
更好的答案(不删除所有项目)-> stackoverflow.com/a/10852046/898056
yhpark 2013年

484

我认为NET中没有任何错误PagerAdapter。问题在于,了解其工作原理有些复杂。查看这里解释的解决方案,从我的角度来看,存在误解,因此实例化视图的使用率很低。

最近几天,我PagerAdapterViewPager和在一起工作,发现以下内容:

上的notifyDataSetChanged()方法PagerAdapter将仅通知ViewPager基础页面已更改。例如,如果您已动态创建/删除页面(在列表中添加或删除项目),ViewPager则应注意这一点。在这种情况下,我认为ViewPager使用getItemPosition()getCount()方法确定是否应删除或实例化新视图。

我认为ViewPager,经过notifyDataSetChanged()调用需要它的子视图,并检查其与位置getItemPosition()。如果对于子视图,此方法返回POSITION_NONE,则ViewPager表示该视图已被删除,并调用destroyItem()并删除了该视图。

这样,覆盖 getItemPosition()POSITION_NONE如果只想更新页面的内容为始终返回是完全错误的,因为先前创建的视图将被销毁,每次调用时都会创建新的视图notifyDatasetChanged()。似乎只花了几TextView秒钟就不会错了,但是当您有复杂的视图(例如从数据库填充的ListViews)时,这可能是一个真正的问题,而且浪费资源。

因此,有几种方法可以有效地更改视图的内容,而不必再次删除和实例化视图。这取决于您要解决的问题。我的方法是将该setTag()方法用于instantiateItem()。因此,当您要更改数据或使所需的视图无效时,可以调用上的findViewWithTag()方法ViewPager来检索先前实例化的视图并根据需要修改/使用它,而不必每次都删除/创建新视图更新一些价值。

例如,假设您有100个页面,TextView每个页面的时间为100 秒,而您只想定期更新一个值。使用前面介绍的方法,这意味着您将TextView在每次更新中删除并实例化100 s。它没有任何意义...


11
+1-您的解决方案不仅发挥了魅力,而且在我不得不更新包含包含列表视图,文本视图和图像的选项卡的页面时,我完全遇到了您在此描述的问题。(OutOfErrorExceptions等...)谢谢!
schlingel

3
@alvarolb:您好,我真的不知道您setTag在method中的意思onInstantiateItem,您想用一些编码更新此答案吗?谢谢。
休塔2014年

35
如果您写了一个例子,那将是完美的。
Sami Eltamawy,2015年

13
就像有人说的:一个例子将不胜感激!
Jey10年

15
六年来,没有人能提供一个例子。
Oussaki '17

80

更改FragmentPagerAdapterFragmentStatePagerAdapter

覆盖getItemPosition()方法并返回POSITION_NONE

最终,它会收听notifyDataSetChanged()on view寻呼机。


我认为您应该正确实现整个getItemPosition,如果找不到该片段,则返回POSITION_NONE。
tkhduracell

46

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

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


8
您能否提供完整的适配器示例?这是使用片段完成的吗?

9
<使用新数据刷新视图> ?? 这是什么意思,或者我们必须在此处添加他的视图,否则什么都没有?
阿卡什M

此方法更新所有视图。如果只想更新一个视图,则可以这样编写自己的notifyDataItemChanged方法:public void notifyDataItemChanged(int pos){TextView tv =(TextView)views.get(pos).findViewById(R.id.tv); tv.setText(list.get(pos)); super.notifyDataSetChanged(); }
Kai Wang

如果新的寻呼机大小小于旧的大小,则此代码将崩溃
Anton Duzenko

@AntonDuzenko正确,删除视图非常困难。
MLProgrammer-CiM

28

有同样的问题。对我来说,它可以扩展FragmentStatePagerAdapter,并覆盖以下方法:

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

@Override
public void restoreState(Parcelable state, ClassLoader loader) {

}

这也是对我有用的唯一解决方案。我的情况不是更新片段,而是动态删除或添加片段。如果不扩展FragmentStatePagerAdapter,ViewPager将返回缓存的片段,这是不正确的位置。谢谢!
Sira Lam

在支持库28.0.0
Oleksii K.19年

重写做什么?如何创建与适配器关联的状态对象?
Phani Rithvij,

谢谢哥们儿。此解决方案仅适用
Parthi

20

经过数小时的挫折之后,尝试上述所有解决方案来克服此问题,并且还尝试了其他类似问题的解决方案,例如thisthisthis,这一切都失败了,我解决了这个问题,并ViewPager破坏了旧的Fragmentpager用新Fragment的。我已经解决了以下问题:

1)使ViewPager类扩展FragmentPagerAdapter如下:

 public class myPagerAdapter extends FragmentPagerAdapter {

2)为以下项创建一个用于ViewPager存储title和的项目fragment

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)使ViewPagerTake my FragmentManagerinstance 的构造函数将其存储在my中class,如下所示:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

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

4)创建的方法重新设置adapter删除所有以前用新的数据数据fragmentfragmentManager自身直接使adapter设置新fragment如下又从新的列表:

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)从容器中ActivityFragment不要用新数据重新初始化适配器。通过使用新数据的方法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();

希望对您有所帮助。


@Tomer您遇到了什么问题?
Sami Eltamawy,


9

我遇到了同样的问题,我的解决方案使用FragmentPagerAdapterFragmentPagerAdapter#getItemId(int position)

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

默认情况下,此方法返回项目的位置。我想ViewPager检查是否itemId已更改,并且仅在已更改的情况下才重新创建页面。但是未覆盖的版本返回的位置与itemId即使页面实际上有所不同,也会,并且ViewPager并未定义该页面被替换并需要重新创建。

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

我认为这是一种更一致的方法,而不是使用本主题中提到的视图标签作为解决方案。但可能并非所有情况都如此。


1
它是。我已经检查了一下:仅POSITION_NONE是不够的。感谢您的精彩回答!:)
Slava

中甚至没有此功能PagerAdapter。这是什么班?
萨凯特

@Saket,您是对的,课程是FragmentPagerAdapter
atlascoder

6

我发现这个问题非常有趣。可以使用FragmentStatePagerAdapterandroid.support.v4.app.FragmentStatePagerAdapter),而不是使用FragmentPagerAdapter(将所有碎片保留在内存中),每次选择时都会重新加载碎片。

两个适配器的实现是相同的。因此,我们只需要在“ extend FragmentStatePagerAdapter ”上更改“ extend FragmentPagerAdapter


好点,它将帮助其他人解决与片段相关的viewPager的内存问题
Ashu Kumar

很好的答案,这对我很有帮助。
Sergio Marani

5

经过大量搜索,我找到了一个非常好的解决方案,我认为这是解决此问题的正确方法。本质上,InstantiateItem仅在实例化视图时调用,除非视图被销毁,否则不会再调用(这是在重写getItemPosition函数以返回POSITION_NONE时发生的情况)。相反,您要做的就是保存创建的视图并在适配器中对其进行更新,生成一个get函数以使其他人可以对其进行更新,或者通过set函数来更新适配器(我最喜欢的)。

因此,在您的MyViewPagerAdapter中添加一个变量,例如:

private View updatableView;

在您的InstantiateItem中:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

这样,您就可以创建一个函数来更新视图:

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

希望这可以帮助!


我意识到我没有设置数据,instantiateItem这就是为什么我的视图没有更新的原因。从您的回答中我意识到了。+1
Phani Rithvij '19

5

在OP提出问题两年半之后,这个问题仍然是一个问题。显然Google对此的优先级不是特别高,因此我找到了一种解决方法,而不是找到解决方法。对我来说最大的突破是发现什么问题的真正原因(见接受的答案这一职位)。一旦发现问题是所有活动页面均未正确刷新,我的解决方法就很明显了:

在我的片段(页面)中:

  • 我从onCreateView中提取了用于填充表单的所有代码,并将其放在一个名为PopulateForm的函数中,该函数可从任何地方而不是由框架调用。此函数尝试使用getView获取当前的View,如果该值为null,则仅返回。重要的是PopulateForm仅包含显示的代码-所有其他创建FocusChange侦听器的代码,等等仍在OnCreate中
  • 创建一个布尔值,可用作指示必须重新加载表单的标志。我的是mbReloadForm
  • 如果设置了mbReloadForm,则重写OnResume()以调用PopulateForm()。

在我的活动中,我在哪里进行页面加载:

  • 进行任何更改之前,请转到第0页。我正在使用FragmentStatePagerAdapter,所以我知道最多影响两到三个页面。更改为第0页可确保仅在第0、1、2页上出现问题。
  • 在清除旧列表之前,请先将其取为size()。这样,您可以知道该错误影响了多少页。如果> 3,则将其减小为3-如果您使用的是其他PagerAdapter,则必须查看要处理的页面数(也许是全部?)
  • 重新加载数据并调用pageAdapter.notifyDataSetChanged()
  • 现在,对于每个受影响的页面,使用pager.getChildAt(i)查看该页面是否处于活动状态-这会告诉您是否有视图。如果是这样,请调用pager.PopulateView()。如果没有,请设置ReloadForm标志。

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

希望这对某人有帮助!


5

所有这些解决方案都没有帮助我。因此,我找到了一个可行的解决方案:setAdapter每次都可以,但这还不够。您应该在更改适配器之前执行以下操作:

FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List<Fragment> fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
    transaction.remove(f);
}
transaction.commit();

然后:

viewPager.setAdapter(adapter);

谢谢!就我而言,我使用了ViewPager内部片段,因此替换slideShowPagerAdapter.getFragmentManager()getChildFragmentManager()。也许getFragmentManager()会对您有帮助。我用过FragmentPagerAdapter,没有FragmentStatePagerAdapter。另请参阅stackoverflow.com/a/25994654/2914140,了解如何修改。
CoolMind

5

一种更简单的方法:使用FragmentPagerAdapter,然后将分页的视图包装到片段上。他们确实得到更新


5

感谢rui.araujo和Alvaro Luis Bustamante。首先,我尝试使用rui.araujo的方法,因为它很容易。它可以工作,但是当数据更改时,页面将明显重绘。这很糟糕,所以我尝试使用Alvaro Luis Bustamante的方法。这是完美的。这是代码:

@Override
protected void onStart() {
    super.onStart();
}

private class TabPagerAdapter extends PagerAdapter {
    @Override
    public int getCount() {
        return 4;
    }

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

    @Override
    public void destroyItem(final View container, final int position, final Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View view = LayoutInflater.from(
                getBaseContext()).inflate(R.layout.activity_approval, null, false);
        container.addView(view);
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        view.setTag(position);
        new ShowContentListTask(listView, position).execute();
        return view;
    }
}

当数据改变时:

for (int i = 0; i < 4; i++) {
    View view = contentViewPager.findViewWithTag(i);
    if (view != null) {
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        new ShowContentListTask(listView, i).execute();
    }
}

4

以防万一有人使用FragmentStatePagerAdapter基于的适配器(这将使ViewPager创建显示目的所需的最少页面,对于我来说最多为2),@ rui.araujo的答案将覆盖适配器中的getItemPosition不会造成重大浪费,但仍然可以改善。

用伪代码:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}

在ViewPager中,很容易知道项目的过去位置,最完美的实现是在getItemPosition()数据集更改时返回新位置,而ViewPager会知道它们是否更改。可悲的是,ViewPager没有做到这一点。
VinceStyling 2014年

3

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

为了解决我的问题,我setSelectedPage(index)打电话之前曾用过getItem(position)。这将实例化页面,使我能够更改每个页面上的值和小部件。

所有的更新后,我会用setSelectedPage(position)后面notifyDataSetChanged()

您可以在主要更新页面的ListView中看到轻微的闪烁,但是没有任何明显的变化。我尚未对其进行全面测试,但确实可以解决我的紧迫问题。


嗨,我对您的代码感兴趣,我面临类似的问题,我能够更新相邻的片段,但是当前可见的片段不会得到更新。我对这里的所有答案都很困惑。我如何更新VIewPager适配器显示的当前片段中的视图
Pankaj Nimgade 2015年

3

我只是发布此答案,以防其他人发现它有用。为了做完全相同的事情,我只需从兼容性库中获取ViewPager和PagerAdapter的源代码,并在我的代码中对其进行编译(您需要整理所有错误并自行导入,但是绝对可以做到)。

然后,在CustomViewPager中,创建一个名为updateViewAt(int position)的方法。该视图本身可以从ViewPager类中定义的ArrayList mItems中获取(您需要在实例化项上为视图设置一个ID,并将此ID与updateViewAt()方法中的位置进行比较)。然后,您可以根据需要更新视图。


2

我想,我有ViewPager的逻辑。

如果需要刷新一组页面并根据新的数据集显示它们,则调用notifyDataSetChanged()。然后,ViewPager多次调用getItemPosition(),将Fragment作为对象传递给该对象。该片段可以来自旧数据集(我要丢弃),也可以来自新数据集(我要显示)。所以,我重写了getItemPosition()然后必须以某种方式确定我的Fragment是来自旧数据集还是来自新数据集。

在我的情况下,我有一个2窗格的布局,在左窗格中有一个顶部项目列表,在右窗格中有一个滑动视图(ViewPager)。因此,我将指向当前当前项的链接存储在PagerAdapter以及每个实例化页面Fragment的内部。当列表中选定的顶层项目更改时,我将新的顶层项目存储在PagerAdapter中,并调用notifyDataSetChanged()。然后在覆盖的 getItemPosition()中,将适配器的顶部项目与片段的顶部项目进行比较。 并且只有当它们不相等时,我才返回POSITION_NONE。 然后,PagerAdapter重新实例化返回POSITION_NONE的所有片段。

注意。存储顶级商品ID而不是参考可能是一个更好的主意。

下面的代码段有点说明性,但我根据实际工作的代码进行了改编。

public class SomeFragment extends Fragment {
  private TopItem topItem;
}

public class SomePagerAdapter extends FragmentStatePagerAdapter {
  private TopItem topItem;

  public void changeTopItem(TopItem newTopItem) {
    topItem = newTopItem;
    notifyDataSetChanged();
  }

  @Override
  public int getItemPosition(Object object) {
    if (((SomeFragment) object).getTopItemId() != topItem.getId()) {
      return POSITION_NONE;
    }
    return super.getItemPosition(object);
  }
}

感谢所有以前的研究人员!


2

下面的代码为我工作。

创建一个扩展FragmentPagerAdapter类的类,如下所示。

public class Adapter extends FragmentPagerAdapter {

private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> object;

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

public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) {
    super(fm);
    mActivity = mA;
    mFragmentManager = fm;
    object = new ArrayList<>();
    mFragmentTags = new HashMap<Integer, String>();
    this.tabCount = numberOfTabs;
}

@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return Fragment0.newInstance(mActivity);
        case 1:
            return Fragment1.newInstance(mActivity);
        case 2:
            return Fragment2.newInstance(mActivity);
        default:
            return null;
    }}


@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Log.e("Already defined","Yes");
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        Log.e("Fragment Tag","" + position + ", " + tag);
        mFragmentTags.put(position, tag);
    }else{
        Log.e("Already defined","No");
    }
    container_id = container.getId();
    this.container = container;
    if(position == 0){
        this.object.add(0,object);
    }else if(position == 1){
        this.object.add(1,object);
    }else if(position == 2){
        this.object.add(2,object);
    }
    return object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    if (object instanceof Fragment) {
        Log.e("Removed" , String.valueOf(position));
    }
}

@Override
public int getItemPosition (Object object)
{   int index = 0;
    if(this.object.get(0) == object){
        index = 0;
    }else if(this.object.get(1) == object){
        index = 1;
    }else if(this.object.get(2) == object){
        index = 2;
    }else{
        index = -1;
    }
    Log.e("Index" , "..................." + String.valueOf(index));
    if (index == -1)
        return POSITION_NONE;
    else
        return index;
}

public String getFragmentTag(int pos){
    return "android:switcher:"+R.id.pager+":"+pos;
}

public void NotifyDataChange(){
    this.notifyDataSetChanged();
}

public int getcontainerId(){
    return container_id;
}

public ViewGroup getContainer(){
    return this.container;
}

public List<Object> getObject(){
    return this.object;
}

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

然后在您创建的每个Fragment内,创建一个updateFragment方法。在这种方法中,您需要更改片段中需要更改的内容。例如,在我的情况下,Fragment0包含一个GLSurfaceView,它根据.ply文件的路径显示3d对象,因此在我的updateFragment方法中,我将路径更改为该ply文件。

然后创建一个ViewPager实例,

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

和一个Adpater实例,

adapter = new Adapter(getSupportFragmentManager(), 3, this);

然后这样做

viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);

然后在该类内部,您初始化了上面的Adapter类并创建了一个viewPager,每次您想更新一个片段(在我们的示例中为Fragment0)时,请使用以下命令:

adapter.NotifyDataChange();

adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager.

fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0.

fragment0.updateFragment() method which include the updates on this fragment

adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0.

该解决方案基于Alvaro Luis Bustamante提出的技术。


1

1.首先,必须在Pageradapter类中设置getItemposition方法。2.必须读取View Pager的确切位置;然后将其作为新位置的数据位置发送。4.在setonPageChange内的onclick侦听器上编写更新按钮onclick侦听器听众

我修改了程序代码,只设置了特定的位置元素

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List<String> data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

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

      updateButton = (Button) findViewById(R.id.update);

    myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i2) {
             //Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPageSelected( int i) {
          // here you will get the position of selected page
            final int k = i;
             updateViewPager(k);

        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });
}

private void updateViewPager(final int i) {  
    updateButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
            data.set(i, "Replaced "+i);         
            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 int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @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) {
    }
}
}

1

对我有用的是去 viewPager.getAdapter().notifyDataSetChanged();

并在适配器中放入用于更新视图的代码,getItemPosition如下所示

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

可能不是解决该问题的最正确方法,但它确实有效(该return POSITION_NONE技巧对我造成了崩溃,因此没有选择)


1

您可以动态更新所有片段,可以通过三个步骤进行查看。

在您的适配器中:

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

现在在您的活动中:

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

最后在您的片段中,像这样:

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


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

    //to refresh your view
    refresh();

}}

您可以在此处看到完整的代码。

感谢Alvaro Luis Bustamante。


1

始终返回POSITION_NONE很简单,但是效率不高,因为它会引起已实例化的所有页面的实例化。

我创建了一个库ArrayPagerAdapter来动态更改PagerAdapters中的项目。

在内部,此库的适配器仅在必要时POSITION_NONE打开getItemPosiition()

您可以使用此库来动态更改项目,如下所示。

@Override
protected void onCreate(Bundle savedInstanceState) {
        /** ... **/
    adapter = new MyStatePagerAdapter(getSupportFragmentManager()
                            , new String[]{"1", "2", "3"});
    ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
     adapter.add("4");
     adapter.remove(0);
}

class MyPagerAdapter extends ArrayViewPagerAdapter<String> {

    public MyPagerAdapter(String[] data) {
        super(data);
    }

    @Override
    public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
        View v = inflater.inflate(R.layout.item_page, container, false);
        ((TextView) v.findViewById(R.id.item_txt)).setText(item);
        return v;
    }
}

Thils库还支持Fragments创建的页面。


1

这适用于像我这样的所有人,他们需要从服务(或其他后台线程)更新Viewpager,并且所有建议都没有起作用:经过一番日志检查后,我意识到notifyDataSetChanged()方法再也不会返回。getItemPosition(Object object)被称为一个全部结束而无需进一步处理。然后我在父PagerAdapter类的文档中找到(不在子类的文档中),“数据集更改必须在主线程上发生,并且必须以对notifyDataSetChanged()的调用结尾”。因此,在这种情况下,有效的解决方案是(使用FragmentStatePagerAdapter和getItemPosition(Object object)设置为返回POSITION_NONE):

然后调用notifyDataSetChanged():

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

1

您可以像这样在Viewpager上添加传呼机转换

myPager.setPageTransformer(true, new MapPagerTransform());

在下面的代码中,当页面滚动时,我在运行时更改了视图颜色

public class MapPagerTransform implements ViewPager.PageTransformer {


    public void transformPage(View view, float position) {
     LinearLayout  showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL);

     if (position < 0) {

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else if (position >0){

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else {
                showSelectionLL.setBackgroundColor(Color.RED);
     }
   }
}

1

我知道我很晚,但仍然可以帮助别人。我只是扩展了正确的答案,并且我也添加了对此的评论。

好,

答案本身说这是低效的

因此,为了使其仅在需要时刷新,您可以执行此操作

private boolean refresh;

public void refreshAdapter(){
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if(refresh){
        refresh = false;
        return POSITION_NONE;
    }else{
        return super.getItemPosition(object);
    }
}

1

ViewPager并非旨在支持动态视图更改。

我在寻找与此https://issuetracker.google.com/issues/36956111特别是https://issuetracker.google.com/issues/36956111#comment56相关的另一个错误时已对此进行确认。

这个问题有点老了,但是Google最近用ViewPager2解决了这个问题。它将允许用标准解决方案替代手工解决方案(未维护且可能有故障的解决方案)。它还可以防止不必要地重新创建视图,就像某些答案一样。

对于ViewPager2示例,您可以检查https://github.com/googlesamples/android-viewpager2

如果要使用ViewPager2,则需要在build.gradle文件中添加以下依赖项:

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

然后,您可以将xml文件中的ViewPager替换为:

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

之后,您需要在活动中将ViewPager替换为ViewPager2

ViewPager2需要RecyclerView.Adapter或FragmentStateAdapter,在您的情况下,它可以是RecyclerView.Adapter

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方法来刷新视图。



0

我想我已经做了一种简单的方法来通知数据集更改:

首先,稍微更改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);

而已。


0

对于它的价值,在KitKat + adapter.notifyDataSetChanged()上,只要您setOffscreenPageLimit足够高,似乎足以使新视图显示出来。通过执行操作,我可以获得所需的行为viewPager.setOffscreenPageLimit(2)


0

我实际上notifyDataSetChanged()ViewPagerCirclePageIndicator之后使用,然后调用destroyDrawingCache()ViewPager并且可以工作。其他任何解决方案都不适合我。


最初的问题已经存在了将近2年,我敢肯定,到现在为止答案可能并不适合所有版本的Android API,Android API仍有许多不同之处。
C0deAttack
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.