尝试将片段动态添加到Android ViewPager中的不同页面。
动态地将片段添加到片段列表的末尾可以很好地工作,但是尝试将其添加到下一个插槽(当前可见页面之后的看不见的页面)。我尝试将其添加到片段的数组列表中,并使用notifyDataSetChanged,并尝试在将每个片段设置为上一个项目之后在每个片段上使用set。
编辑:更新的代码
class MainPagerAdapter extends FragmentPagerAdapter {
public MainPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public PageFragment getItem(int position) {
return PageFragment.newInstance(Datamart.getInstance().getURLs().get(position));
}
@Override
public int getCount() {
return Datamart.getInstance().getURLs().size();
}
@Override
public int getItemPosition(Object object) {
PageFragment pageFragment = (PageFragment) object;
for ( int i = 0; i < getCount(); i++ ) {
if ( pageFragment.getURL().equals( Datamart.getInstance().getURLs().get(i) ) ) {
return i;
}
}
return POSITION_NONE;
}
}
在Datamart中:
public class Datamart {
static Datamart instance;
private ArrayList<String> URLs;
private MainPagerAdapter mainPagerAdapter;
private ViewPager viewPager;
public Datamart() {
URLs = new ArrayList<>();
}
public static Datamart getInstance() {
if (instance == null) {
instance = new Datamart();
}
return instance;
}
public MainPagerAdapter getMainPagerAdapter() {
return mainPagerAdapter;
}
public void setMainPagerAdapter(MainPagerAdapter mainPagerAdapter) {
this.mainPagerAdapter = mainPagerAdapter;
}
public ViewPager getViewPager() {
return viewPager;
}
public void setViewPager(ViewPager viewPager) {
this.viewPager = viewPager;
}
public void addToEnd( String URL ) {
URLs.add(URL);
mainPagerAdapter.notifyDataSetChanged();
}
public void addNext( String URL ) {
URLs.add(viewPager.getCurrentItem() + 1, URL);
mainPagerAdapter.notifyDataSetChanged();
}
public ArrayList<String> getURLs() {
return URLs;
}
public void setURLs(ArrayList<String> URLs) {
this.URLs = URLs;
}
}
最佳答案
ViewPager
尽最大努力尝试跟踪所有片段的去向,但由于将片段插入中间而没有告知,您感到困惑。
让我们做一个例子。假设您有两页,那么您将显示第一页,第二页是右侧的屏幕外页。您第一页有fragment0
,第二页有fragment1
。
首先,请记住,由于ViewPager
正在左右管理屏幕外页面,因此它已经认为知道第二个页面片段是什么,即fragment1
。
在现有页面片段之间插入片段时,ViewPager
不会立即询问您第二个位置的片段,而是要询问第二个位置的片段是否已更改。
这是getItemPosition()
的来源。您没有在getItemPosition()
中实现FragmentPagerAdapter
,所以getItemPosition()
的默认行为是返回POSITION_UNCHANGED
。 (如您所见,这就是为什么在末尾添加片段不会导致错误的原因。)
因此,当您调用notifyDataSetChanged()
时,ViewPager
调用了getCount()
并看到有两个页面,而不是两个页面。然后,它为第0页和第1页(它知道的页面)调用getItemPosition()
,并且您的默认实现告诉它什么都没有改变。
但是确实发生了变化,您在两个现有页面的中间插入了一个页面/片段。 ViewPager
询问您第二页是否更改,并且您的getItemPosition()
应该返回了2
而不是POSITION_UNCHANGED
。
现在,由于您的getCount()
返回了3
而不是2
,因此ViewPager
知道末尾有一个新页面,它不知道,因此它调用getItem()
来获取新片段。
这是发生错误的地方。您的getItem()
方法在第3页返回了fragment1
,并且ViewPager
阻塞了,因为您将它已经知道的片段交给了它。具体来说,它将片段的标签用于一些内部管理。它在页面的容器ID和项目位置(页面编号)之外创建标签。因此,在任何给定时间,它都期望(a)标签为空,或者(b)标签将与根据容器ID和商品位置已经计算出的值相同。这些条件都不是正确的,所以这就是抛出IllegalStateException
的原因。
那么如何解决这个问题?
您实现一个做正确的事情的getItemPosition()
。调用getItemPosition()
时,将扫描片段列表,找到匹配的片段时,将返回片段的索引。如果删除了一个片段,使得列表中没有匹配的片段,则返回POSITION_NONE
告诉ViewPager
应该删除该页面。 ViewPager
然后将调用您的getItem()
方法以获取新页面。这样,ViewPager
可以始终与适配器保持同步。
编写FragmentPagerAdapter
时,我什至不使用模型中的片段。如果我正在编写您的适配器,那么我将只有一个URL列表,因为看起来每个页面/片段都有其自己的唯一URL。
我还将在fragment类上实现一个getUrl()
方法,以获取用于创建该fragment的URL。
因此,对于getItemPosition()
,我将在传入的片段上调用getUrl()
,然后在适配器的列表中搜索该URL。然后,我将返回列表中URL的索引,如果不存在,则返回POSITION_NONE
。
然后在getItem()
中,我将使用传入的列表位置处的URL实例化一个新片段。我的规则是,直到ViewPager
通过调用getItem()
专门请求它之前,我才不会实例化一个片段。
这样,只有ViewPager
会访问这些片段,而我不会遇到这些类型的问题。
另外,在ViewPager
调用startUpdate()
到调用finishUpdate()
的时间之间,请勿对适配器模型进行任何更改。这就是ViewPager
告诉适配器它在确定所有页面片段的去向的“关键部分”中的方式。PagerAdapter
可以忍受。在getCount()
,getItemPosition()
和getItem()
方法中放入一些调试日志,以便您了解ViewPager
如何使用适配器管理其页面。
关于java - 接下来在已经运行的Android ViewPager中插入新的Fragment,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30282516/