Видаліть сторінку фрагмента з ViewPager в Android


138

Я намагаюся динамічно додавати та видаляти фрагменти з ViewPager, додавання працює без проблем, але видалення не працює, як очікувалося.

Щоразу, коли я хочу видалити поточний елемент, останній видаляється.

Я також спробував використовувати FragmentStatePagerAdapter або повернути POSITION_NONE в методі getItemPosition адаптера.

Що я роблю неправильно?

Ось основний приклад:

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

MyFragment.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>

fragment.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>

2
+1 для вихідного коду
wizurd

чи правильно використовувати конструктор за замовчуванням у MyFragment.java??
codeKiller

Відповіді:


95

Рішення Лута було недостатньо для того, щоб працювати на мене, оскільки існуючі фрагменти не руйнуються. Мотивований цією відповіддю , я виявив, що рішення полягає в тому, щоб замінити getItemId(int position)метод FragmentPagerAdapterнадання нового унікального ідентифікатора кожного разу, коли відбулася зміна очікуваної позиції фрагмента.

Вихідний код:

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

Тепер, наприклад, якщо ви видалите одну вкладку або внесли якісь зміни в порядок, вам слід зателефонувати notifyChangeInPosition(1)перед викликом notifyDataSetChanged(), що забезпечить відтворення всіх Фрагментів.

Чому це рішення працює

Переосмислення getItemPosition ():

Коли notifyDataSetChanged()викликається, адаптер викликає notifyChanged()метод, до ViewPagerякого він приєднаний. В ViewPagerтой перевіряє значення , повернене в адаптері getItemPosition()для кожного елемента, видаляючи ті елементи , які повертають POSITION_NONE(див вихідного коду ) , а потім репопулірующіе.

Переосмислення getItemId ():

Це необхідно для того, щоб адаптер не перевантажував старий фрагмент під час ViewPagerпереселення. Ви можете легко зрозуміти, чому це працює, переглянувши вихідний код для instantiateItem () в FragmentPagerAdapter.

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

Як бачимо, getItem()метод викликається лише в тому випадку, якщо менеджер фрагментів не знайде існуючих фрагментів з тим самим Id. Мені здається помилкою, що старі фрагменти все ще додаються навіть після notifyDataSetChanged()виклику, але в документації на ViewPagerчітко зазначено, що:

Зауважте, цей клас наразі знаходиться на стадії розробки та розробки. API, ймовірно, зміниться в пізніших оновленнях бібліотеки сумісності, вимагаючи змін у вихідному коді додатків, коли вони компілюються з новою версією.

Тож сподіваємось, що наведене тут рішення не буде потрібним у майбутній версії бібліотеки підтримки.


1
Дуже дякую за цю відповідь! Намагається вирішити цю проблему без використання FragmentStatePagerAdapter. Відмінне пояснення теж. Ви знайшли спосіб запустити getItem, коли повертаєтесь до фрагмента? Я намагався без успіху використовувати ваше розумне повідомлення notifyDataSetChanged () у різних місцях (тобто onStop)
u2tall

Я не дуже розумію ваше запитання ... Будь ласка, опублікуйте нове запитання, включаючи вихідний код, якщо ви не можете розібратися.
Тім Рей

1
2016 рік, це вирішення ще потрібно зробити notifyDataSetChanged()для роботи.
Субін Себастьян

Оце Так! Дивовижне пояснення. У мене був досить складний панель перегляду, де я динамічно виймаю і вставляю елементи, і я не змушую його працювати, не розуміючи всього цього.
рвап

Ще потрібне це рішення для заміни старого фрагмента. Співав.
kopikaokao

291

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

}

1
Гаразд, це здається найпростішим вирішенням цього питання. Хоча кілька інших способів вирішення обговорюються у звітах про помилки тут: code.google.com/p/android/isissue/detail?id=19110 і тут: code.google.com/p/android/isissue/detail?id= 19001
Субін Себастьян

@Louth: Я використовую ViewPager з TabsAdapter, як поширюється з FragmentStatePagerAdapter (наприклад, на сторінці пошуку Google: developer.android.com/reference/android/support/v4/view/… ). Моє наступне запитання: як ви видалите вкладку в першу чергу? Дякую.
gcl1

4
@Louth Коли ми зателефонуємо notifyDataSetChanged (), viewpager створює нові фрагменти, але старіші залишаються в пам'яті. І моя проблема така; вони отримують дзвінок на свою подію onOptionsItemSelected. Як я можу позбутися старих фрагментів?
силок

1
Дякую, що лише заміна FragmentPagerAdapter на FragmentStatePagerAdapter вирішила мою проблему. Після цього я просто видаляю AllAllViews () на ViewPager і знову динамічно завантажую фрагменти.
Міхал

17
Чому ви завжди повертаєте POSITION_NONE, а не дійсно шукаєте, чи певний вид / фрагмент / об’єкт все ще існує та чи дійсний? Цей метод повинен відповісти на питання "ей, чи можу я використати цей повторно?" - і завжди говорити "Ні!" просто означає відпочинок всього! У багатьох випадках це непотрібно.
Зордід

14

моє робоче рішення для видалення фрагмента сторінки з пейджера перегляду

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

Будь ласка, поділіться кодом для додавання фрагмента?
VVB

Але при додаванні це відображає повільність після проведенного пальця двічі / тричі
VVB

якщо ви не видаляєте предмети, він повертає лише предмет, нічого більше ... ви протестуєте на vm чи реальному пристрої?
Василь Валчев

Спробуйте інші рішення, відповіді яких більше голосів. Пейджер View має дуже складну систему грошових коштів для кращої роботи на swip, тому ці хакі не обробляються оригінальним видом пейджера з цільовим призначенням ...
Василь Валчев

11

Фрагмент потрібно вже видалити, але проблема збереглась у збереженні viewpager

Спробуйте

myViewPager.setSaveFromParentEnabled(false);

Нічого не працювало, але це вирішило питання!

Ура!


2

У мене виникла ідея просто скопіювати вихідний код із 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;
}

1

Ви можете комбінувати обидва для кращого:

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.

}

Це працює лише в тому випадку, якщо ви видаляєте вкрай праву вкладку, щоб видалити внутрішню або ліву вкладки, ви повинні змінити порядок вкладок і повернути нове положення, як зазначено в розробнику
BitByteDog

1

Для майбутніх читачів!

Тепер ви можете використовувати ViewPager2 для динамічного додавання, видалення фрагмента з панелі перегляду.

Цитуючи довідник API форми

ViewPager2 замінює ViewPager, звертаючись до більшості больових точок попередника, включаючи підтримку планування справа наліво, вертикальну орієнтацію, модифіковані колекції фрагментів тощо.

Візьміть погляд на MutableCollectionFragmentActivity.ktв googlesample / андроїд-viewpager2 для прикладу додавання, видалення фрагментів динамічно з ViewPager.


До твого відома:

Статті:

Посилання API

Примітки до випуску

Зразки Repo: https://github.com/googlesamples/android-viewpager2


У мене є 2 фрагменти mainfragment та otherfragment. Я додаю otherfragment після кожної 5-ї позиції, і це не в arraylist, тож як видалити інший фрагмент, якщо його немає в списку? Я використовую viewpager2
b devloper

0

Відповідь Лаут працює чудово. Але я не думаю, що завжди повертати POSITION_NONE - це гарна ідея. Оскільки POSITION_NONE означає, що фрагмент слід знищити і створити новий фрагмент. Ви можете перевірити це у функції dataSetChanged у вихідному коді ViewPager.

        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. І інстанціонованийІмен з позицією парам 3 буде викликаний. Наразі третій елемент у "mFramgents" не є нульовим, тому він повернеться безпосередньо. Але насправді те, що воно повернуло, - це фрагмент у позиції 2. Отже, коли ми перетворимось на сторінку 3, ми знайдемо там порожню сторінку.

Щоб вирішити цю проблему. Моя порада полягає в тому, що ви можете скопіювати вихідний код FragmentStatePagerAdapter у свій власний проект, а коли ви робите додавання чи видалення операцій, ви повинні додавати та вилучати елементи з масиву "mFragments".

Речі будуть простішими, якщо ви просто використовуєте PagerAdapter замість FragmentStatePagerAdapter. Щасти.


0

динамічно додавати або видаляти фрагмент у панелі перегляду.
Настройка викликуViewPager (viewPager) при запуску активності. Щоб завантажити різні параметри виклику фрагментаViewPagerCustom (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);
    }
}

0

У мене були деякі проблеми 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 ().


0

Спробуйте це рішення. Я використовував прив'язку даних для перегляду прив'язки. Ви можете використовувати загальну функцію "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);
    }
}


}

0

Я додав функцію "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();
    }

}

0

Я вирішив цю проблему цими кроками

1- використовуйте FragmentPagerAdapter

2- у кожному фрагменті створіть випадковий ідентифікатор

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

3- перекрити getItemPosition в адаптері

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

4-перекрити getItemId в адаптері

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

5- тепер видалити код є

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

це працювало для мене, я сподіваюся, допоможе


0

Мій остаточний код версії, виправлені ВСІ помилки. На це пішло 3 дні

Оновлено 2020/07/18: Я змінив багато вихідного коду та виправив так багато помилок, але я не обіцяю, що це все ще працює сьогодні.

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

У мене є 2 фрагменти mainfragment та otherfragment. Я додаю otherfragment після кожної 5-ї позиції, і це не в arraylist, тож як видалити інший фрагмент, якщо його немає в списку? Я використовую viewpager2
b devloper

Посилання github не працює
b devloper

-1

Ви можете просто перекрити destroyItemметод

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

-9

Я сподіваюся, що це може допомогти тому, що ви хочете.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

9
це якраз протилежна прийнятій відповіді - як це може працювати ??
Зордід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.