Intereting Posts
Как захватить клики UIMediaController через интерфейс управления литьем Как исходный код Android не имеет основного метода и все еще работает? Правильное использование yieldIfContendedSafely () в многопоточном приложении Android Как сохранить библиотеки тестирования Android отдельно от моих библиотек приложений в Android Studio? Как защитить приложение от пиратства Ошибка: причина: buildToolsVersion не указывается Удаленное видео Android с титаном AspectJ в Android: вызов pointcut (* Activity.onCreate (..)) не выделяет вызовы Activity.onCreate () Как я могу управлять звуковыми томами в моем приложении Android? HorizontalScrollView внутри SwipeRefreshLayout Android – Понижение растрового изображения на холсте Android Calendar Provider: существует ли уникальный идентификатор событий, который можно использовать на нескольких устройствах? Невозможно извлечь скрытый показатель для Android KeyStore Местное уведомление в телефонной таблице 3.3.0 Какова самая быстрая библиотека FFT для устройств iOS / Android ARM?

FragmentPagerAdapter не удаляет элементы (фрагменты) правильно

Я реализовал FragmentPagerAdapter и, используя список, чтобы сохранить все фрагменты для отображения ViewPager. On addItem () Я просто добавляю экземпляр-экземпляр, а затем вызываю notifyDataSetChanged (). Я не уверен, что это необходимо или нет.

Моя проблема просто … начните с фрагмента 1

[Fragment 1] 

Добавить новый фрагмент 2

 [Fragment 1] [Fragment 2] 

Удалить фрагмент 2

 [Fragment 1] 

Добавить новый фрагмент 3

 [Fragment 1] [Fragment 2] 

При добавлении новых фрагментов все кажется замечательным. Как только я удаляю фрагмент, а затем добавляю вновь созданный фрагмент, старый фрагмент все еще отображается. Когда я иду .getClass.getName (), он дает мне имя Fragment 3, но я все еще вижу фрагмент 2.

Я считаю, что это может быть проблемой с instantiateItem или такими, но я думал, что адаптер должен был справиться с этим для нас. Любые предложения были бы замечательными.

Код адаптера …

 public class MyPagerAdapter extends FragmentPagerAdapter { public final ArrayList<Fragment> screens2 = new ArrayList<Fragment>(); private Context context; public MyPagerAdapter(FragmentManager fm, Context context) { super(fm); this.context = context; } public void removeType(String name){ for(Fragment f: screens2){ if(f.getClass().getName().contains(name)){ screens2.remove(f); return; } } this.notifyDataSetChanged(); } public boolean addSt(String tag, Class<?> clss, Bundle args){ if(clss==null) return false; if(!clss.getName().contains("St")) return false; if(!args.containsKey("cId")) return false; boolean has = false; boolean hasAlready = false; for(Fragment tab: screens2){ if(tab.getClass().getName().contains("St")){ has = true; if(tab.getArguments().containsKey("cId")) if(tab.getArguments().getLong("cId") == args.getLong("cId")){ hasAlready = true; } if(!hasAlready){ // exists but is different so replace screens2.remove(tab); this.addScreen(tag, clss, args, C.PAGE_ST); // if returned true then it notifies dataset return true; } } hasAlready = false; } if(!has){ // no st yet exist in adapter this.addScreen(tag, clss, args, C.PAGE_ST); return true; } return false; } public boolean removeCCreate(){ this.removeType("Create"); return false; } @Override public int getItemPosition(Object object) { return POSITION_NONE; //To make notifyDataSetChanged() do something } public void addCCreate(){ this.removeCCreate(); Log.w("addding c", " "); this.addScreen("Create C", CreateCFragment.class, null, C.PAGE_CREATE_C); } public void addScreen(String tag, Class<?> clss, Bundle args, int type){ if(clss!=null){ screens2.add(Fragment.instantiate(context, clss.getName(), args)); } } @Override public int getCount() { return screens2.size(); } @Override public Fragment getItem(int position) { return screens2.get(position); } } 

Я понимаю, что в коде используется некоторое «гетто» для определения типа фрагмента, однако я написал этот код строго для тестирования функциональности. Любая помощь или идеи были бы замечательными, поскольку кажется, что не многие люди отважились в мир FragmentPagerAdapters.

Solutions Collecting From Web of "FragmentPagerAdapter не удаляет элементы (фрагменты) правильно"

У меня такая же проблема, и мое решение перегрузило метод «destroyItem» следующим образом.

 @Override public void destroyItem(ViewGroup container, int position, Object object) { FragmentManager manager = ((Fragment)object).getFragmentManager(); FragmentTransaction trans = manager.beginTransaction(); trans.remove((Fragment)object); trans.commit(); } 

Это работа для меня, есть ли у кого-нибудь другие решения?

Обновлено:

Я обнаружил, что этот код не сделал ненужным фрагмент, поэтому я добавил условие, чтобы избежать его.

 @Override public void destroyItem(ViewGroup container, int position, Object object) { if (position >= getCount()) { FragmentManager manager = ((Fragment) object).getFragmentManager(); FragmentTransaction trans = manager.beginTransaction(); trans.remove((Fragment) object); trans.commit(); } } 

Обновил этот пост и включил мое решение (если кто-то может улучшить, дайте мне знать)

Хорошо, я теперь решил свою проблему хакерским способом, но да, это работает;). Если кто-то может улучшить мое решение, пожалуйста, дайте мне знать. Для моего нового решения я теперь использую CustomFragmentStatePagerAdapter, но он не сохраняет состояние, как должно, и сохраняет все фрагменты в списке. Это может вызвать проблему с памятью, если пользователь имеет более 50 фрагментов, например, обычный FragmentPagerAdapter. Было бы здорово, если бы кто-то мог добавить состояние к моему решению, не удаляя мои исправления. Благодарю.

Итак, вот мой CustomFragmentStatePagerAdapter.java

 package com.tundem.webLab.Adapter; import java.util.ArrayList; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.PagerAdapter; import android.util.Log; import android.view.View; import android.view.ViewGroup; public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { private static final String TAG = "FragmentStatePagerAdapter"; private static final boolean DEBUG = false; private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; public ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); public ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); private Fragment mCurrentPrimaryItem = null; public CustomFragmentStatePagerAdapter(FragmentManager fm) { mFragmentManager = fm; } /** * Return the Fragment associated with a specified position. */ public abstract Fragment getItem(int position); @Override public void startUpdate(ViewGroup container) {} @Override public Object instantiateItem(ViewGroup container, int position) { // 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. // DONE Remove of the add process of the old stuff /* if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } */ if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } Fragment fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); if (fss != null) { try // DONE: Try Catch { fragment.setInitialSavedState(fss); } catch (Exception ex) { // Schon aktiv (kA was das heißt xD) } } } while (mFragments.size() <= position) { mFragments.add(null); } fragment.setMenuVisibility(false); mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment); return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.remove(fragment); /*if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment) * object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); * 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); } if (fragment != null) { fragment.setMenuVisibility(true); } mCurrentPrimaryItem = fragment; } } @Override public void finishUpdate(ViewGroup container) { if (mCurTransaction != null) { mCurTransaction.commitAllowingStateLoss(); mCurTransaction = null; mFragmentManager.executePendingTransactions(); } } @Override public boolean isViewFromObject(View view, Object object) { return ((Fragment) object).getView() == view; } @Override public Parcelable saveState() { Bundle state = null; if (mSavedState.size() > 0) { state = new Bundle(); Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; mSavedState.toArray(fss); state.putParcelableArray("states", fss); } for (int i = 0; i < mFragments.size(); i++) { Fragment f = mFragments.get(i); if (f != null) { if (state == null) { state = new Bundle(); } String key = "f" + i; mFragmentManager.putFragment(state, key, f); } } return state; } @Override public void restoreState(Parcelable state, ClassLoader loader) { if (state != null) { Bundle bundle = (Bundle) state; bundle.setClassLoader(loader); Parcelable[] fss = bundle.getParcelableArray("states"); mSavedState.clear(); mFragments.clear(); if (fss != null) { for (int i = 0; i < fss.length; i++) { mSavedState.add((Fragment.SavedState) fss[i]); } } 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) { while (mFragments.size() <= index) { mFragments.add(null); } f.setMenuVisibility(false); mFragments.set(index, f); } else { Log.w(TAG, "Bad fragment at key " + key); } } } } } } 

Вот мой обычный FragmentAdapter.java

 package com.tundem.webLab.Adapter; import java.util.LinkedList; import java.util.List; import android.support.v4.app.FragmentManager; import com.tundem.webLab.fragments.BaseFragment; import com.viewpagerindicator.TitleProvider; public class FragmentAdapter extends CustomFragmentStatePagerAdapter implements TitleProvider { public List<BaseFragment> fragments = new LinkedList<BaseFragment>(); private int actPage; public FragmentAdapter(FragmentManager fm) { super(fm); } public void setActPage(int actPage) { this.actPage = actPage; } public void addItem(BaseFragment fragment) { // TODO if exists don't open / change to that tab fragments.add(fragment); } public BaseFragment getActFragment() { return getItem(getActPage()); } public int getActPage() { return actPage; } @Override public BaseFragment getItem(int position) { if (position < getCount()) { return fragments.get(position); } else return null; } @Override public int getCount() { return fragments.size(); } @Override public String getTitle(int position) { return fragments.get(position).getTitle(); } @Override public int getItemPosition(Object object) { return POSITION_NONE; } } 

И так я удаляю фрагмент. (Я знаю, что это немного больше, чем только .remove ()). Не стесняйтесь улучшать мое решение, вы также можете добавить этот код где-то в адаптере, так что да. Это зависит от пользователя, который пытается реализовать это. Я использую это в своем TabHelper.java (класс, который обрабатывает все операции с табуляторами, такие как delete, add, …)

  int act = Cfg.mPager.getCurrentItem(); Cfg.mPager.removeAllViews(); Cfg.mAdapter.mFragments.remove(act); try { Cfg.mAdapter.mSavedState.remove(act); } catch (Exception ex) {/* Already removed */} try { Cfg.mAdapter.fragments.remove(act); } catch (Exception ex) {/* Already removed */} Cfg.mAdapter.notifyDataSetChanged(); Cfg.mIndicator.notifyDataSetChanged(); 

Описание Cfg. вещь. Я сохраняю ссылку на эти объекты в классе cfg, поэтому я всегда могу использовать их без необходимости использования специального Factory.java …

Да. Надеюсь, я смог помочь. Не стесняйтесь улучшать это, но дайте мне знать, чтобы я мог улучшить свой код.

Благодарю.

Если я пропустил какой-либо код, дайте мне знать.


Мой старый ответ также работает, но только если у вас разные фрагменты. FileFragment, WebFragment, … Нет, если вы дважды используете один из этих фрагментов.

На данный момент я его псевдою работаю. Это действительно грязное решение, и я все еще ищу лучшего. Пожалуйста помоги.

Я изменил код, где я удаляю вкладку:

  public static void deleteActTab() { //We set this on the indicator, NOT the pager int act = Cfg.mPager.getCurrentItem(); Cfg.mAdapter.removeItem(act); List<BaseFragment> frags = new LinkedList<BaseFragment>(); frags = Cfg.mAdapter.fragments; Cfg.mPager = (ViewPager)Cfg.act.findViewById(R.id.pager); Cfg.mPager.setAdapter(Cfg.mAdapter); Cfg.mIndicator.setViewPager(Cfg.mPager); Cfg.mAdapter.fragments = frags; if(act > 0) { Cfg.mPager.setCurrentItem(act-1); Cfg.mIndicator.setCurrentItem(act-1); } Cfg.mIndicator.notifyDataSetChanged(); } 

Если кто-то может улучшить этот код, дайте мне знать. Если кто-то может сказать нам реальный ответ на эту проблему. Пожалуйста, добавьте его здесь. Многие люди сталкиваются с этой проблемой. Я добавил репутацию 50 для тех, кто ее разрешил. Я также могу дать пожертвование тому, кто его решает.

благодаря

Принимая «лучшее из обоих миров» (я имею в виду ответы @Tericky Shih и @mikepenz), у нас это короткое и простое:

 public class MyPagerAdapter extends FragmentPagerAdapter { public ArrayList<Fragment> fragments = new ArrayList<Fragment>(); ... @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); if (position >= getCount()) fm.beginTransaction().remove((Fragment) object).commit(); } @Override public int getItemPosition(Object object) { if (fragments.contains(object)) return fragments.indexOf(object); else return POSITION_NONE; } } 

Главное отличие состоит в том, что если какой-либо фрагмент не изменяется, вам не нужно уничтожать его представление и не нужно возвращать POSITION_NONE для него. В то же время я столкнулся с ситуацией, когда ViewPager содержал ссылку на элемент, который был уже уничтожен, поэтому проверка if (fragments.contains(object)) помогает определить, не нужен ли этот элемент больше.

У меня была ситуация, похожая на вашу. Недавно мне нужно было добавить и удалить фрагменты из ViewPager. В первом режиме у меня есть фрагменты 0, 1 и 2, а во втором режиме у меня есть фрагменты 0 и 3. Я хочу, чтобы Фрагмент 0 был одинаковым для обоих режимов и сохранял информацию.

Все, что мне нужно было сделать, это переопределить FragmentPagerAdapter.getItemId, чтобы убедиться, что я вернул уникальный номер для каждого другого фрагмента – по умолчанию возвращается «позиция». Мне также пришлось снова установить адаптер в ViewPager – новый экземпляр будет работать, но я верну его обратно в тот же экземпляр. Настройка адаптера приводит к тому, что ViewPager удаляет все представления и пытается их добавить снова.

Однако фокус в том, что адаптер только вызывает getItem, когда он хочет создать экземпляр фрагмента – не каждый раз, когда он показывает это. Это происходит потому, что они кэшируются и просматривают их с помощью «позиции», возвращаемой getItemId.

Представьте, что у вас есть три фрагмента (0, 1 и 2), и вы хотите удалить «1». Если вы вернете «позицию» для getItemId, то удаление фрагмента 1 не будет работать, потому что, когда вы пытаетесь показать фрагмент 2 после удаления фрагмента 1, пейджер / адаптер подумает, что у него уже есть фрагмент для этой «позиции» и продолжит отображение фрагмента 1 ,

FYI: Я попробовал notifyDataSetChanged вместо установки адаптера, но это не сработало для меня.

Во-первых, пример getItemId override и то, что я сделал для моего getItem:

 public class SectionsPagerAdapter extends FragmentPagerAdapter { ... @Override public long getItemId(int position) { // Mode 1 uses Fragments 0, 1 and 2. Mode 2 uses Fragments 0 and 3 if ( mode == 2 && position == 1 ) return 3; return position; } @Override public Fragment getItem(int position) { if ( mode == 1 ) { switch (position) { case 0: return <<fragment 0>>; case 1: return <<fragment 1>>; case 2: return <<fragment 2>>; } } else // Mode 2 { switch (position) { case 0: return <<fragment 0>>; case 1: return <<fragment 3>>; } } return null; } } 

Теперь изменение режима:

 private void modeChanged(int newMode) { if ( newMode == mode ) return; mode = newMode; // Calling mSectionsPagerAdapter.notifyDataSetChanged() is not enough here mViewPager.setAdapter(mSectionsPagerAdapter); } 

Может быть, этот ответ поможет вам.

Используйте FragmentStatePagerAdapter вместо FragmentPagerAdapter .

Поскольку FragmentPagerAdapter не разрушает представления. Для получения дополнительной информации прочтите этот ответ .

Не сработало для меня. Моему решению был добавлен FragmentStatePagerAdapter.java в мой проект, переименованный в FragmentStatePagerAdapter2.java. В destroyItem () я немного изменился на основе журналов ошибок. Из

 // mFragments.set(position, null); 

в

 if (position < mFragments.size())mFragments.remove(position); 

Возможно, у вас нет той же проблемы, просто проверьте журнал. Упование это помогает кому-то!

После многих попыток я получил его для работы, чтобы он правильно удалял или прикреплял третий фрагмент в конечной позиции.

 Object fragments[] = new Object[3]; int mItems = 2; MyAdapter mAdapter; ViewPager mPager; public void addFragment(boolean bool) { mAdapter.startUpdate(mPager); if (!bool) { mAdapter.destroyItem(mPager, 2, fragments[2]); mItems = 2; fNach = false; } else if (bool && !fNach){ mItems = 3; mAdapter.instantiateItem(mPager,2); fNach = true; } mAdapter.finishUpdate(mPager); mAdapter.notifyDataSetChanged(); } public class MyAdapter extends FragmentPagerAdapter { MyAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return mItems; } @Override public CharSequence getPageTitle(int position) { ... (code for the PagerTitleStrip) } @Override public Fragment getItem(int position) { Fragment f = null; switch (position) { case 0: f = new Fragment1(); break; case 1: f = new Fragment2(); break; case 2: f = new Fragment3(); break; } return f; } @Override public Object instantiateItem(ViewGroup container, int position) { Object o = super.instantiateItem(container,position); fragments[position] = o; return o; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); System.out.println("Destroy item " + position); if (position >= getCount()) { FragmentManager manager = ((Fragment) object).getFragmentManager(); FragmentTransaction ft = manager.beginTransaction(); ft.remove((Fragment) object); ft.commit(); } } } 

Некоторое уточнение: чтобы получить ссылку на объект для вызова destroyItem, я сохранил объекты, возвращенные из объекта instantiateItem в массиве. Когда вы добавляете или удаляете фрагменты, вы должны объявлять его с помощью startUpdate, finishUpdate и notifyDataSetChanged. Количество элементов должно быть изменено вручную, для добавления вы увеличиваете его и создаете экземпляр, тогда getItem создает его. Для удаления вы вызываете destroyItem, и в этом коде важно положение> = mItems, потому что destroyItem также вызывается, если фрагмент выходит из кеша. Вы не хотите его удалять. Единственное, что не работает, – это анимация прокрутки. После удаления последней страницы анимация «не может прокрутить влево» на новой последней странице не восстанавливается правильно. Если вы проведете его, появится пустая страница, и она отскакивает назад.

Реальная проблема заключается в том, что FragmentPagerAdapter использует позицию фрагмента в вашем списке как идентификатор. Поэтому, если вы добавляете новый список или просто удаляете элементы, элемент «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); 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)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } 

а также

  private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id; } 

а также

  * Return a unique identifier for the item at the given position. * <p> * <p>The default implementation returns the given position. * Subclasses should override this method if the positions of items can change.</p> * * @param position Position within this adapter * @return Unique identifier for the item at position */ public long getItemId(int position) { return position; }