在 Android 中从 ViewPager 中删除片段页

我试图从 ViewPager 中动态添加和删除片段,添加工作没有任何问题,但删除并不像预期的那样工作。

每次我想删除当前项时,最后一个项都会被删除。

我还尝试在适配器的 getItemposition 方法中使用一个 FragmentStatePagerAdapter 或返回 POSITION _ NONE。

我做错了什么?

下面是一个基本的例子:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {


private Button mAdd;
private Button mRemove;
private ViewPager mPager;


private MyPagerAdapter mAdapter;


private ArrayList<String> mEntries = new ArrayList<String>();


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


mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");


mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);


mAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
addNewItem();
}
});


mRemove.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
removeCurrentItem();
}
});


mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);


mPager.setAdapter(mAdapter);


}


private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}


private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}


@Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
@Override
public int getCount() {
return mEntries.size();
}




private class MyPagerAdapter extends FragmentPagerAdapter {


private TextProvider mProvider;


public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}


@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}


@Override
public int getCount() {
return mProvider.getCount();
}


}


}

TextProvider.java

public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}

Java

public class MyFragment extends Fragment {


private String mText;


public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}


public MyFragment() {
}


public MyFragment(String text) {
this.mText = text;
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {


View root = inflater.inflate(R.layout.fragment, container, false);


((TextView) root.findViewById(R.id.position)).setText(mText);


return root;
}


}

Activity _ main. xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >


<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />


<Button
android:id="@+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />


<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />


</LinearLayout>

Xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >


<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />


</LinearLayout>
108309 次浏览

ViewPager 不会用上面的代码删除您的片段,因为它将几个视图(或者您的情况下的片段)加载到内存中。除了可见视图之外,它还将视图加载到可见视图的两侧。这提供了从一个视图到另一个视图的平滑滚动,这使得 ViewPager 非常酷。

为了达到你想要的效果,你需要做一些事情。

  1. 将 FragmentPagerAdapter 更改为 FragmentStatePagerAdapter。这样做的原因是,FragmentPagerAdapter 将永远保存它加载到内存中的所有视图。其中,FragmentStatePagerAdapter 处理当前视图和可遍历视图之外的视图。

  2. 重写适配器方法 getItemposition (如下所示)。当我们调用 mAdapter.notifyDataSetChanged();时,ViewPager 询问适配器,以确定在定位方面发生了什么变化。我们使用这个方法来说明一切都改变了,所以重新处理所有的视图定位。

这是密码。

private class MyPagerAdapter extends FragmentStatePagerAdapter {


//... your existing code


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


}

我希望这能帮到你。

private class MyPagerAdapter extends FragmentStatePagerAdapter {


//... your existing code


@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_UNCHANGED;
}
}

你可以把两者结合得更好:

private class MyPagerAdapter extends FragmentStatePagerAdapter {


//... your existing code


@Override
public int getItemPosition(Object object){


if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
return PagerAdapter.POSITION_NONE;
}
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.


}

由劳斯提出的解决方案不足以让事情为我工作,因为现有的碎片没有被摧毁。受到 这个答案的启发,我发现解决方案是覆盖 FragmentPagerAdaptergetItemId(int position)方法,在片段的预期位置发生变化时给出一个新的唯一 ID。

源代码:

private class MyPagerAdapter extends FragmentPagerAdapter {


private TextProvider mProvider;
private long baseId = 0;


public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}


@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}


@Override
public int getCount() {
return mProvider.getCount();
}




//this is called when notifyDataSetChanged() is called
@Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}




@Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}


/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* @param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}

现在,例如,如果您删除了一个选项卡或者对顺序做了一些更改,您应该在调用 notifyDataSetChanged()之前调用 notifyChangeInPosition(1),这将确保所有的片段都将被重新创建。

为什么这个解决方案有效

重写 getItemposition () :

当调用 notifyDataSetChanged()时,适配器调用它所附加的 ViewPagernotifyChanged()方法。然后,ViewPager检查适配器的 getItemPosition()为每个项目返回的值,删除那些返回 POSITION_NONE的项目(参见 源代码) ,然后重新填充。

重写 getItemId () :

ViewPager重新填充时,这是防止适配器重新加载旧片段所必需的。通过查看 FragmentPagerAdapter中的 源代码 for instantiateItem () ,您可以很容易地理解为什么这样做。

    final long itemId = getItemId(position);


// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}

如您所见,只有在片段管理器没有发现具有相同 Id 的现有片段时,才调用 getItem()方法。在我看来,即使在调用 notifyDataSetChanged()之后,旧的片段仍然附着在上面,这似乎是一个错误,但是 ViewPager的文档确实清楚地指出:

注意,这个类目前正处于早期设计和开发阶段。该 API 可能会在以后的兼容性库更新中发生变化,需要在根据新版本编译应用程序时更改源代码。

因此,希望这里给出的解决方案在支持库的未来版本中不是必需的。

我的想法是简单地将源代码从 android.support.v4.app.FragmentPagerAdpater复制到一个名为 CustumFragmentPagerAdapter.这使我有机会修改 instantiateItem(...),以便每次调用它时,它都会在添加从 getItem()方法接收的新片段之前删除/销毁当前附加的片段。

只需按以下方式修改 instantiateItem(...):

@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);


// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);


// remove / destroy current fragment
if (fragment != null) {
mCurTransaction.remove(fragment);
}


// get new fragment and add it
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));


if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}


return fragment;
}

我的最终版本代码,修复所有的错误。它花了我3天

更新于2020/07/18: 我已经修改了很多源代码,修复了很多 bug,但我不能保证它今天仍然可以工作。

Https://github.com/lin1987www/fragmentbuilder/blob/master/commonlibrary/src/main/java/android/support/v4/app/fragmentstatepageradapterfix.java

public class FragmentStatePagerAdapterFix extends PagerAdapter {
private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
private static final boolean DEBUG = false;


private WeakReference<FragmentActivity> wrFragmentActivity;
private WeakReference<Fragment> wrParentFragment;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;


protected ArrayList<Fragment> mFragments = new ArrayList<>();
protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
protected ArrayList<String> mFragmentTags = new ArrayList<>();
protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();


private Fragment mCurrentPrimaryItem = null;
private boolean[] mTempPositionChange;


@Override
public int getCount() {
return mFragmentClassNames.size();
}


public FragmentActivity getFragmentActivity() {
return wrFragmentActivity.get();
}


public Fragment getParentFragment() {
return wrParentFragment.get();
}


public FragmentStatePagerAdapterFix(FragmentActivity activity) {
mFragmentManager = activity.getSupportFragmentManager();
wrFragmentActivity = new WeakReference<>(activity);
wrParentFragment = new WeakReference<>(null);
}


public FragmentStatePagerAdapterFix(Fragment fragment) {
mFragmentManager = fragment.getChildFragmentManager();
wrFragmentActivity = new WeakReference<>(fragment.getActivity());
wrParentFragment = new WeakReference<>(fragment);
}


public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
add(fragClass, null, null);
}


public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
add(fragClass, args, null);
}


public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
add(fragClass, null, tag);
}


public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
add(fragClass, args, tag, getCount());
}


public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
mFragments.add(position, null);
mFragmentStates.add(position, null);
mFragmentTags.add(position, tag);
mFragmentClassNames.add(position, fragClass.getName());
mFragmentArgs.add(position, args);
mTempPositionChange = new boolean[getCount()];
}


public void remove(int position) {
if (position < getCount()) {
mTempPositionChange = new boolean[getCount()];
for (int i = position; i < mTempPositionChange.length; i++) {
mTempPositionChange[i] = true;
}
mFragments.remove(position);
mFragmentStates.remove(position);
mFragmentTags.remove(position);
mFragmentClassNames.remove(position);
mFragmentArgs.remove(position);
}
}


public void clear(){
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
}


@Override
public void startUpdate(ViewGroup container) {
}


@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment;
// If we already have this item instantiated, there is nothing
// to do.  This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
fragment = mFragments.get(position);
if (fragment != null) {
return fragment;
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
FragmentState fs = mFragmentStates.get(position);
if (fs != null) {
fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
// Fix bug
// http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
if (fragment.mSavedFragmentState != null) {
fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
}
}
if (fragment == null) {
fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
}
if (DEBUG) {
Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mFragmentStates.set(position, null);
mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
return fragment;
}


@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) {
Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment) object).getView());
}
if (position < getCount()) {
FragmentState fragmentState = new FragmentState(fragment);
Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
if (savedState != null) {
fragmentState.mSavedFragmentState = savedState.mState;
}
mFragmentStates.set(position, fragmentState);
mFragments.set(position, null);
}
mCurTransaction.remove(fragment);
}


@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}


@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
// Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
for (Fragment fragment : mFragments) {
if (fragment != null) {
fixActiveFragment(mFragmentManager, fragment);
}
}
}
}


@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}


@Override
public Parcelable saveState() {
Bundle state = null;
// 目前顯示的 Fragments
for (int i = 0; i < mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
if (mFragmentStates.size() > 0) {
if (state == null) {
state = new Bundle();
}
FragmentState[] fs = new FragmentState[mFragmentStates.size()];
mFragmentStates.toArray(fs);
state.putParcelableArray("states_fragment", fs);
}
return state;
}


@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
Parcelable[] fs = bundle.getParcelableArray("states_fragment");
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
if (fs != null) {
for (int i = 0; i < fs.length; i++) {
FragmentState fragmentState = (FragmentState) fs[i];
mFragmentStates.add(fragmentState);
if (fragmentState != null) {
mFragmentArgs.add(fragmentState.mArguments);
mFragmentTags.add(fragmentState.mTag);
mFragmentClassNames.add(fragmentState.mClassName);
} else {
mFragmentArgs.add(null);
mFragmentTags.add(null);
mFragmentClassNames.add(null);
}
mFragments.add(null);
}
}
Iterable<String> keys = bundle.keySet();
for (String key : keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
f.setMenuVisibility(false);
mFragments.set(index, f);
mFragmentArgs.set(index, f.mArguments);
mFragmentTags.set(index, f.mTag);
mFragmentClassNames.set(index, f.getClass().getName());
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
// If restore will change
notifyDataSetChanged();
}
}


public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
if (fm.mActive != null) {
int index = fragment.mIndex;
Fragment origin = fm.mActive.get(index);
if (origin != null) {
if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
Log.e(TAG,
String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
origin.getClass().getName(), origin.mIndex,
fragment.getClass().getName(), fragment.mIndex
));
}
}
fm.mActive.set(index, fragment);
}
}


// Fix
// http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
@Override
public int getItemPosition(Object object) {
int index = mFragments.indexOf(object);
if (index < 0) {
return PagerAdapter.POSITION_NONE;
}
boolean isPositionChange = mTempPositionChange[index];
int result = PagerAdapter.POSITION_UNCHANGED;
if (isPositionChange) {
result = PagerAdapter.POSITION_NONE;
}
return result;
}
}

我的工作解决方案,删除片段页面从查看页面

public class MyFragmentAdapter extends FragmentStatePagerAdapter {


private ArrayList<ItemFragment> pages;


public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
super(fragmentManager);
this.pages = pages;
}


@Override
public Fragment getItem(int index) {
return pages.get(index);
}


@Override
public int getCount() {
return pages.size();
}


@Override
public int getItemPosition(Object object) {
int index = pages.indexOf (object);


if (index == -1)
return POSITION_NONE;
else
return index;
}
}

当我需要删除一些网页的索引我这样做

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

这里是我的适配器初始化

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);

劳斯的回答很有效。但我不认为总是返回 POSITION _ NONE 是一个好主意。因为 POSITION _ NONE 意味着应该销毁片段并创建新的片段。 您可以在 ViewPager 的源代码中的 dataSetChanged 函数中检查这一点。

        if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
... not related code
mAdapter.destroyItem(this, ii.position, ii.object);

因此,我认为您最好使用虚引用的数组列表来保存您已经创建的所有片段。当您添加或删除某个页面时,您可以从您自己的数组列表中获得正确的位置。

 public int getItemPosition(Object object) {
for (int i = 0; i < mFragmentsReferences.size(); i ++) {
WeakReference<Fragment> reference = mFragmentsReferences.get(i);
if (reference != null && reference.get() != null) {
Fragment fragment = reference.get();
if (fragment == object) {
return i;
}
}
}
return POSITION_NONE;
}

根据注释,当主机视图试图确定项的位置是否已更改时,将调用 getItemposition。返回值表示它的新位置。

但这还不够,我们还有重要的一步要走。 在 FragmentStatePagerAdapter 的源代码中,有一个名为“ mFragments”的数组缓存没有被销毁的片段。在 instantiateItem 函数中。

if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}

当它发现缓存的片段不为空时,它直接返回缓存的片段。所以有个问题。例如,让我们删除位置2的一个页面,首先,我们从我们自己的引用数组列表中删除该片段。因此,在 getItemposition 中,它将为该片段返回 POSITION _ NONE,然后该片段将被销毁并从“ mFragments”中删除。

 mFragments.set(position, null);

现在位置3的碎片会在位置2。而具有 param 位置3的 instantiatedItem 将被调用。此时,“ mFramgents”中的第三项不为空,因此它将直接返回。但实际上它返回的是位置2的碎片。所以当我们翻到第三页的时候,我们会看到一个空白页。

解决这个问题。我的建议是,您可以将 FragmentStatePagerAdapter 的源代码复制到您自己的项目中,当您添加或删除操作时,您应该在“ mFragments”数组列表中添加和删除元素。

如果你只使用 PagerAdapter 而不是 FragmentStatePagerAdapter,事情会变得更简单。祝你好运。

在视图页面中动态添加或删除片段。
在活动开始时调用 setupViewPager (viewPager)。 要加载不同的片段,请调用 setupViewPagerCustom (viewPager)。 例如按钮点击呼叫: setupViewPagerCustom (viewPager) ;

    private void setupViewPager(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet1(), "HOME");
adapter.addFrag(new fragmnet2(), "SERVICES");


viewPager.setAdapter(adapter);
}


private void setupViewPagerCustom(ViewPager viewPager)
{


ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());


adapter.addFrag(new fragmnet3(), "Contact us");
adapter.addFrag(new fragmnet4(), "ABOUT US");




viewPager.setAdapter(adapter);
}


//Viewpageradapter, handles the views


static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();


public ViewPagerAdapter(FragmentManager manager){
super(manager);
}


@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}


@Override
public int getCount() {
return mFragmentList.size();
}


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


public void addFrag(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}


@Override
public CharSequence getPageTitle(int position){
return mFragmentTitleList.get(position);
}
}

FragmentStatePagerAdapter出了点问题。 在移除一个项目之后:

  • 还有一个项目用于一个位置(一个项目不属于该位置,但它旁边的位置)
  • 或者一些片段没有加载(在那个页面上只有可见的空白背景)

经过大量的实验,我想出了以下的解决办法。

public class SomeAdapter extends FragmentStatePagerAdapter {


private List<Item> items = new ArrayList<Item>();


private boolean removing;


@Override
public Fragment getItem(int position) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
// use items.get(position) to configure fragment
fragment.setArguments(args);
return fragment;
}


@Override
public int getCount() {
return items.size();
}


@Override
public int getItemPosition(Object object) {
if (removing) {
return PagerAdapter.POSITION_NONE;
}


Item item = getItemOfFragment(object);


int index = items.indexOf(item);
if (index == -1) {
return POSITION_NONE;
} else {
return index;
}
}


public void addItem(Item item) {
items.add(item);
notifyDataSetChanged();
}


public void removeItem(int position) {
items.remove(position);


removing = true;
notifyDataSetChanged();
removing = false;
}
}

此解决方案仅在删除项目时使用黑客技术。否则(例如,当添加一个条目时)它将保持原始代码的整洁性和性能。

当然,从适配器的外部只调用 addItem/RemoveItem,不需要调用 notifyDataSetChanged ()。

片段必须已经被删除,但问题是视图页保存状态

试试看

myViewPager.setSaveFromParentEnabled(false);

没有什么工作,但这解决了问题!

干杯!

尝试这个解决方案。我已经使用了绑定视图的数据绑定。您可以使用常用的“ findViewById ()”函数。

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;


private ViewPagerAdapter adapter;






@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.act_cp_loss);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
mContext = ActCPExpense.this;
initViewsAct();




} catch (Exception e) {
LogUtils.LOGE(TAG, e);
}


}




private void initViewsAct() {


adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
mBinding.viewpager.setAdapter(adapter);
mBinding.tab.setViewPager(mBinding.viewpager);






}






@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
int i = itemActUtility.getItemId();
if (i == android.R.id.home) {
onBackPressed();


}


return super.onOptionsItemSelected(itemActUtility);
}


@Override
public void onClick(View view) {
super.onClick(view);
int id = view.getId();
if (id == R.id.btnAdd) {
addFragment();


} else if (id == R.id.btnDelete) {
removeFragment();


}




}
private void addFragment(){
adapter.addFragment(FragmentCPPayee.newInstance("Title");
adapter.notifyDataSetChanged();
mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
adapter.removeItem(mBinding.viewpager.getCurrentItem());
mBinding.tab.setViewPager(mBinding.viewpager);
}




class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();




public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}


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




@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}


@Override
public int getCount() {
return mFragmentList.size();
}




public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);


}


public void removeItem(int pos) {


destroyItem(null, pos, mFragmentList.get(pos));
mFragmentList.remove(pos);
mFragmentTitleList.remove(pos);
adapter.notifyDataSetChanged();
mBinding.viewpager.setCurrentItem(pos - 1, false);
}


@Override
public CharSequence getPageTitle(int position) {
return "Title " + String.valueOf(position + 1);
}
}




}

我添加了一个函数“ clearFragments”,并使用该函数在设置新片段之前清除适配器。这将调用片段的正确删除操作。我的 pagerAdapter 类:

private class ChartPagerAdapter extends FragmentPagerAdapter{
private ArrayList<Fragment> fragmentList;


ChartPagerAdapter(FragmentManager fm){
super(fm);
fragmentList = new ArrayList<>();
}


void setFragments(ArrayList<? extends Fragment> fragments){
fragmentList.addAll(fragments);
}


void clearFragments(){
for(Fragment fragment:fragmentList)
getChildFragmentManager().beginTransaction().remove(fragment).commit();
fragmentList.clear();
}


@Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}


@Override
public int getCount() {
return fragmentList.size();
}


}

您可以只重写 destroyItem方法

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}

我用这些步骤解决了这个问题

1-use FragmentPagerAdapter 1-使用片段页面适配器

在每个片段中创建一个随机标识

fragment.id = new Random().nextInt();

3-重写适配器中的 getItemposition

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

在适配器中重写 getItemId

 @Override
public long getItemId(int position) {
return mDatasetFragments.get(position).id;
}

现在删除代码是

 adapter.mDatasetFragments.remove(< item to delete position >);
adapter.notifyDataSetChanged();

这对我有用,我希望能有帮助

为了未来的读者!

现在您可以使用 ViewPager2来动态添加、删除视图页面中的片段。

报价表 空气污染指数参考资料

ViewPager2取代了 ViewPager,解决了其前身的大部分问题 痛点,包括从右到左的布局支持,垂直 方向,可修改的片段集合等。

看看 Googlesample/android-viewpager2中的 MutableCollectionFragmentActivity.kt,它是一个从视图页面动态添加和删除片段的例子。


告诉你:

文章:

空气污染指数参考资料

发布说明

样本回购: https://github.com/googlesamples/android-viewpager2

现在是2020年。

简单添加到 PageAdapter:

override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}