Удаление страницы фрагмента из ViewPager в Android
Я пытаюсь динамически добавлять и удалять фрагменты из 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>
Ответы
Ответ 1
Решение Louth было недостаточно для того, чтобы заставить меня работать, поскольку существующие фрагменты не уничтожаются. Мотивированный этим ответом, я обнаружил, что решение состоит в том, чтобы переопределить метод 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()
вызывается только в том случае, если диспетчер фрагментов не обнаруживает существующих фрагментов с тем же идентификатором. Для меня это похоже на ошибку, что старые фрагменты все еще прикреплены даже после вызова notifyDataSetChanged()
, но в документации для ViewPager
четко указано, что:
Обратите внимание, что этот класс в настоящее время находится в раннем дизайне и разработке. API, скорее всего, изменится в последующих обновлениях библиотеки совместимости, требуя изменений в исходном коде приложений, когда они скомпилированы против более новой версии.
Так что, надеюсь, обходное решение, данное здесь, не понадобится в будущей версии библиотеки поддержки.
Ответ 2
ViewPager не удаляет ваши фрагменты с помощью приведенного выше кода, поскольку он загружает в память несколько представлений (или фрагментов в вашем случае). В дополнение к видимому виду, он также загружает представление в любую сторону видимого. Это обеспечивает плавную прокрутку из вида для просмотра, что делает ViewPager таким классным.
Чтобы достичь желаемого эффекта, вам нужно сделать пару вещей.
-
Измените FragmentPagerAdapter на FragmentStatePagerAdapter. Причина этого в том, что FragmentPagerAdapter сохранит все виды, которые он загружает в память навсегда. Если FragmentStatePagerAdapter обладает представлениями, которые выходят за пределы текущего и проходящего просмотра.
-
Переопределите метод адаптера getItemPosition (показано ниже). Когда мы вызываем mAdapter.notifyDataSetChanged();
, ViewPager запрашивает адаптер, чтобы определить, что изменилось с точки зрения позиционирования. Мы используем этот метод, чтобы сказать, что все изменилось, поэтому переработайте все свое позиционирование.
И вот код...
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}
Ответ 3
мое рабочее решение для удаления фрагмента страницы из пейджера просмотра
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);
Ответ 4
Фрагмент должен быть уже удален, но проблема была сохранена в состоянии просмотра
Try
myViewPager.setSaveFromParentEnabled(false);
Ничего не получилось, но это решило проблему!
Приветствия!
Ответ 5
У меня возникла идея просто скопировать исходный код из 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;
}
Ответ 6
Вы можете комбинировать оба для лучшего:
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.
}
Ответ 7
Мой окончательный код версии, исправлены ВСЕ ошибки. Мне потребовалось 3 дня
https://github.com/lin1987www/FragmentBuilder/blob/master/app/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 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;
}
}
Ответ 8
Ответ Louth работает отлично. Но я не думаю, что всегда возвращать 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);
Итак, я думаю, вам лучше использовать arraylist of weakReference для сохранения всех фрагментов, которые вы создали. И когда вы добавляете или удаляете какую-либо страницу, вы можете получить правильную позицию от своего собственного arraylist.
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" не равен null, поэтому он будет возвращаться напрямую. Но на самом деле то, что оно вернуло, является фрагмент в позиции 2. Поэтому, когда мы перейдем на страницу 3, мы найдем там пустую страницу.
Чтобы обойти эту проблему. Могу посоветовать, что вы можете скопировать исходный код FragmentStatePagerAdapter в свой собственный проект, а когда вы добавляете или удаляете операции, вы должны добавлять и удалять элементы в arraylist "mFragments" .
Все будет проще, если вы просто используете PagerAdapter вместо FragmentStatePagerAdapter. Удачи.
Ответ 9
добавить или удалить фрагмент в viewpager динамически.
Вызовите setupViewPager (viewPager) для запуска активности. Для загрузки другого фрагмента вызова setupViewPagerCustom (viewPager). например при нажатии кнопки click: 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);
}
}
Ответ 10
У меня были проблемы с 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().
Ответ 11
Попробуйте это решение. Я использовал привязку данных для привязки просмотра. Вы можете использовать общую функцию "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);
}
}
}
Ответ 12
Я добавил функцию "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();
}
}
Ответ 13
Вы можете просто переопределить метод destroyItem
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
Ответ 14
я решил эту проблему с помощью этих шагов
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();
это сработало для меня, я надеюсь помочь
Ответ 15
Надеюсь, это поможет вам в том, что вы хотите.
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_UNCHANGED;
}
}