J'essaie d'ajouter et de supprimer de manière dynamique des fragments d'un ViewPager. L'ajout fonctionne sans problème, mais la suppression ne fonctionne pas comme prévu.
Chaque fois que je veux supprimer l'élément en cours, le dernier est supprimé.
J'ai également essayé d'utiliser un FragmentStatePagerAdapter ou de renvoyer POSITION_NONE dans la méthode getItemPosition de l'adaptateur.
Qu'est-ce que je fais mal?
Voici un exemple de base:
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>
La solution de Louth ne suffisait pas pour que les choses fonctionnent pour moi, car les fragments existants n'étaient pas détruits. Motivé par cette réponse , j’ai trouvé que la solution consistait à remplacer la méthode getItemId(int position)
de FragmentPagerAdapter
pour donner un nouvel ID unique chaque fois qu’il y avait un changement dans la position attendue d’un fragment.
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;
}
}
Maintenant, par exemple, si vous supprimez un seul onglet ou modifiez l'ordre, vous devez appeler notifyChangeInPosition(1)
avant d'appeler notifyDataSetChanged()
, ce qui garantira que tous les fragments seront recréés.
Remplacement de getItemPosition ():
Lorsque notifyDataSetChanged()
est appelé, l'adaptateur appelle la méthode notifyChanged()
de la ViewPager
à laquelle elle est attachée. La variable ViewPager
vérifie ensuite la valeur renvoyée par la fonction getItemPosition()
de l'adaptateur pour chaque élément, en supprimant les éléments renvoyant POSITION_NONE
(voir le code source ), puis en remplissant à nouveau.
Remplacement de getItemId ():
Cela est nécessaire pour empêcher l'adaptateur de recharger l'ancien fragment lorsque la variable ViewPager
est en train de se repeupler. Vous pouvez facilement comprendre pourquoi cela fonctionne en regardant le code source pour instantiateItem () dans 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));
}
Comme vous pouvez le constater, la méthode getItem()
n’est appelée que si le gestionnaire de fragments ne trouve aucun fragment existant portant le même identifiant. Pour moi, il semble y avoir un problème avec le fait que les vieux fragments sont toujours attachés même après l'appel de notifyDataSetChanged()
, mais la documentation de ViewPager
indique clairement que:
Notez que cette classe est actuellement en phase de conception et de développement. L'API changera probablement dans les mises à jour ultérieures de la bibliothèque de compatibilité, nécessitant des modifications du code source des applications lorsqu'elles sont compilées avec la nouvelle version.
J'espère donc que la solution proposée ici ne sera pas nécessaire dans une future version de la bibliothèque de support.
ViewPager ne supprime pas vos fragments avec le code ci-dessus car il charge plusieurs vues (ou fragments dans votre cas) en mémoire. En plus de la vue visible, il charge également la vue de chaque côté de la vue visible. Cela permet un défilement régulier d’une vue à l’autre qui rend le ViewPager si cool.
Pour obtenir l'effet souhaité, vous devez procéder de deux manières.
Remplacez FragmentPagerAdapter par FragmentStatePagerAdapter. La raison en est que FragmentPagerAdapter conservera pour toujours toutes les vues qu’il charge en mémoire. Où FragmentStatePagerAdapter élimine les vues extérieures aux vues actuelles et traversables.
Remplacez la méthode de l'adaptateur getItemPosition (illustrée ci-dessous). Lorsque nous appelons mAdapter.notifyDataSetChanged();
, ViewPager interroge l'adaptateur pour déterminer ce qui a changé en termes de positionnement. Nous utilisons cette méthode pour dire que tout a changé, alors retraitez tout votre positionnement de vue.
Et voici le code ...
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
@Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}
ma solution de travail pour supprimer la page de fragment de la vue pageur
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;
}
}
Et quand j'ai besoin de supprimer une page par index, je le fais
pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter
Ici c'est l'initialisation de mon adaptateur
MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Le fragment doit déjà être supprimé, mais le problème était l'état de sauvegarde du viewpager.
Essayer
myViewPager.setSaveFromParentEnabled(false);
Rien n'a fonctionné mais ceci a résolu le problème!
À votre santé !
J'ai eu l'idée de simplement copier le code source de Android.support.v4.app.FragmentPagerAdpater
dans une classe personnalisée nommée CustumFragmentPagerAdapter
. Cela m'a donné l'occasion de modifier la fonction instantiateItem(...)
de sorte que chaque fois qu'il est appelé, il supprime/détruit le fragment actuellement lié avant d'ajouter le nouveau fragment reçu de la méthode getItem()
.
Modifiez simplement la instantiateItem(...)
de la manière suivante:
@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;
}
Vous pouvez combiner les deux pour mieux:
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.
}
Vous pouvez simplement remplacer la méthode destroyItem
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
ajouter ou supprimer des fragments dans viewpager dynamiquement.
Appelez setupViewPager (viewPager) au début de l'activité . Pour charger un fragment différent, appelez setupViewPagerCustom (viewPager) . par exemple. lors d’un clic sur un bouton, appelez: 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);
}
}
j'ai résolu ce problème par ces étapes
1- utiliser FragmentPagerAdapter
2- dans chaque fragment créer un identifiant aléatoire
fragment.id = new Random().nextInt();
3- Remplacer getItemPosition dans l'adaptateur
@Override
public int getItemPosition(@NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
GetItemId dans 4 adaptateurs
@Override
public long getItemId(int position) {
return mDatasetFragments.get(position).id;
}
5- maintenant supprimer le code est
adapter.mDatasetFragments.remove(< item to delete position >);
adapter.notifyDataSetChanged();
cela a fonctionné pour moi j'espère l'aide
Essayez cette solution. J'ai utilisé la liaison de données pour la vue de liaison. Vous pouvez utiliser la fonction "findViewById ()" commune.
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);
}
}
}
J'ai ajouté une fonction "clearFragments" et j'ai utilisé cette fonction pour effacer l'adaptateur avant de définir les nouveaux fragments. Cela appelle les actions de suppression appropriées des fragments. Ma classe 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();
}
}
La réponse de Louth fonctionne bien. Mais je ne pense pas que toujours retourner POSITION_NONE est une bonne idée. Parce que POSITION_NONE signifie que ce fragment doit être détruit et qu'un nouveau fragment sera créé . Vous pouvez vérifier cela dans la fonction dataSetChanged dans le code source de ViewPager.
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
... not related code
mAdapter.destroyItem(this, ii.position, ii.object);
Donc, je pense que vous feriez mieux d'utiliser un arraylist de faible référence pour enregistrer tous les fragments que vous avez créés. Et lorsque vous ajoutez ou supprimez une page, vous pouvez obtenir la bonne position à partir de votre propre liste.
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;
}
Selon les commentaires, getItemPosition est appelée lorsque la vue de l'hôte tente de déterminer si la position d'un élément a été modifiée. Et la valeur de retour signifie sa nouvelle position.
Mais ce n'est pas suffisant. Nous avons encore une étape importante à franchir . Dans le code source de FragmentStatePagerAdapter, un tableau nommé "mFragments" met en cache les fragments qui ne sont pas détruits. Et dans la fonction instantiateItem.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
Il a renvoyé le fragment mis en cache directement lorsqu'il a découvert que ce fragment n'était pas nul. Donc, il y a un problème. Par exemple, supprimons une page en position 2. Premièrement, nous supprimons ce fragment de notre propre arraylist de référence. ainsi, dans getItemPosition, il retournera POSITION_NONE pour ce fragment, puis ce fragment sera détruit et supprimé de "mFragments".
mFragments.set(position, null);
Maintenant, le fragment en position 3 sera en position 2. Et instancié, un élément avec param position 3 sera appelé. À ce stade, le troisième élément de "mFramgents" n'est pas nul et sera donc renvoyé directement. Mais en réalité, le résultat est le fragment en position 2. Ainsi, lorsque nous passons à la page 3, nous y trouvons une page vide.
Pour contourner ce problème. Mon conseil est que vous pouvez copier le code source de FragmentStatePagerAdapter dans votre propre projet, et lorsque vous ajoutez ou supprimez des opérations, vous devez ajouter et supprimer des éléments dans la liste "mFragments".
Les choses seront plus simples si vous utilisez simplement PagerAdapter au lieu de FragmentStatePagerAdapter. Bonne chance.
J'ai eu quelques problèmes avec FragmentStatePagerAdapter
. Après avoir supprimé un élément:
Après de nombreuses expériences, j'ai proposé la solution suivante.
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;
}
}
Cette solution utilise uniquement un hack en cas de suppression d'un élément. Sinon (par exemple, lors de l’ajout d’un élément), il conserve la propreté et les performances du code original.
Bien entendu, depuis l'extérieur de l'adaptateur, vous appelez uniquement addItem/removeItem, il n'est pas nécessaire d'appeler notifyDataSetChanged ().
Mon code de version finale, a corrigé TOUS les bugs. Cela m'a pris 3 jours
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;
}
}