FragmentStatePagerAdapter2.java revision 64e2f4d13eda39377b80487d387b8e4a9f40b3b5
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.mail.utils; 18 19import android.app.Fragment; 20import android.app.FragmentManager; 21import android.app.FragmentTransaction; 22import android.os.Bundle; 23import android.os.Parcelable; 24import android.support.v13.app.FragmentCompat; 25import android.support.v13.app.FragmentStatePagerAdapter; 26import android.support.v4.util.SparseArrayCompat; 27import android.support.v4.view.PagerAdapter; 28import android.util.Log; 29import android.view.View; 30import android.view.ViewGroup; 31 32import java.util.ArrayList; 33 34/** 35 * Forked from support lib's {@link FragmentStatePagerAdapter}, with some minor 36 * changes that couldn't be accomplished through subclassing: 37 * <ul> 38 * <li>optionally disable stateful behavior when paging (controlled by {@link #mEnableSavedStates}), 39 * for situations where state save/restore when paging is unnecessary</li> 40 * <li>override-able {@link #setItemVisible(Fragment, boolean)} method for subclasses to 41 * add supplemental handling of visibility hints manually on pre-v15 devices</li> 42 * <li>add support to handle data set changes that cause item positions to change</li> 43 * <li>allow read access to existing Fragments by index ({@link #getFragmentAt(int)})</li> 44 * </ul> 45 */ 46public abstract class FragmentStatePagerAdapter2 extends PagerAdapter { 47 private static final String TAG = "FragmentStatePagerAdapter"; 48 private static final boolean DEBUG = false; 49 50 private final FragmentManager mFragmentManager; 51 private FragmentTransaction mCurTransaction = null; 52 53 private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); 54 private SparseArrayCompat<Fragment> mFragments = new SparseArrayCompat<Fragment>(); 55 private Fragment mCurrentPrimaryItem = null; 56 57 private boolean mEnableSavedStates; 58 59 public FragmentStatePagerAdapter2(FragmentManager fm) { 60 this(fm, true); 61 } 62 63 public FragmentStatePagerAdapter2(FragmentManager fm, boolean enableSavedStates) { 64 mFragmentManager = fm; 65 mEnableSavedStates = enableSavedStates; 66 } 67 68 /** 69 * Return the Fragment associated with a specified position. 70 */ 71 public abstract Fragment getItem(int position); 72 73 @Override 74 public void startUpdate(ViewGroup container) { 75 } 76 77 @Override 78 public Object instantiateItem(ViewGroup container, int position) { 79 // If we already have this item instantiated, there is nothing 80 // to do. This can happen when we are restoring the entire pager 81 // from its saved state, where the fragment manager has already 82 // taken care of restoring the fragments we previously had instantiated. 83 final Fragment existing = mFragments.get(position); 84 if (existing != null) { 85 return existing; 86 } 87 88 if (mCurTransaction == null) { 89 mCurTransaction = mFragmentManager.beginTransaction(); 90 } 91 92 Fragment fragment = getItem(position); 93 if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); 94 if (mEnableSavedStates && mSavedState.size() > position) { 95 Fragment.SavedState fss = mSavedState.get(position); 96 if (fss != null) { 97 fragment.setInitialSavedState(fss); 98 } 99 } 100 if (fragment != mCurrentPrimaryItem) { 101 setItemVisible(fragment, false); 102 } 103 mFragments.put(position, fragment); 104 mCurTransaction.add(container.getId(), fragment); 105 106 return fragment; 107 } 108 109 @Override 110 public void destroyItem(ViewGroup container, int position, Object object) { 111 Fragment fragment = (Fragment)object; 112 113 if (mCurTransaction == null) { 114 mCurTransaction = mFragmentManager.beginTransaction(); 115 } 116 if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object 117 + " v=" + ((Fragment)object).getView()); 118 if (mEnableSavedStates) { 119 while (mSavedState.size() <= position) { 120 mSavedState.add(null); 121 } 122 mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); 123 } 124 mFragments.delete(position); 125 126 mCurTransaction.remove(fragment); 127 } 128 129 @Override 130 public void setPrimaryItem(ViewGroup container, int position, Object object) { 131 Fragment fragment = (Fragment)object; 132 if (fragment != mCurrentPrimaryItem) { 133 if (mCurrentPrimaryItem != null) { 134 setItemVisible(mCurrentPrimaryItem, false); 135 } 136 if (fragment != null) { 137 setItemVisible(fragment, true); 138 } 139 mCurrentPrimaryItem = fragment; 140 } 141 } 142 143 @Override 144 public void finishUpdate(ViewGroup container) { 145 if (mCurTransaction != null) { 146 mCurTransaction.commitAllowingStateLoss(); 147 mCurTransaction = null; 148 mFragmentManager.executePendingTransactions(); 149 } 150 } 151 152 @Override 153 public boolean isViewFromObject(View view, Object object) { 154 return ((Fragment)object).getView() == view; 155 } 156 157 @Override 158 public Parcelable saveState() { 159 Bundle state = null; 160 if (mEnableSavedStates && mSavedState.size() > 0) { 161 state = new Bundle(); 162 Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; 163 mSavedState.toArray(fss); 164 state.putParcelableArray("states", fss); 165 } 166 for (int i=0; i<mFragments.size(); i++) { 167 final int pos = mFragments.keyAt(i); 168 final Fragment f = mFragments.valueAt(i); 169 if (state == null) { 170 state = new Bundle(); 171 } 172 String key = "f" + pos; 173 mFragmentManager.putFragment(state, key, f); 174 } 175 return state; 176 } 177 178 @Override 179 public void restoreState(Parcelable state, ClassLoader loader) { 180 if (state != null) { 181 Bundle bundle = (Bundle)state; 182 bundle.setClassLoader(loader); 183 mFragments.clear(); 184 if (mEnableSavedStates) { 185 Parcelable[] fss = bundle.getParcelableArray("states"); 186 mSavedState.clear(); 187 if (fss != null) { 188 for (int i=0; i<fss.length; i++) { 189 mSavedState.add((Fragment.SavedState)fss[i]); 190 } 191 } 192 } 193 Iterable<String> keys = bundle.keySet(); 194 for (String key: keys) { 195 if (key.startsWith("f")) { 196 int index = Integer.parseInt(key.substring(1)); 197 Fragment f = mFragmentManager.getFragment(bundle, key); 198 if (f != null) { 199 setItemVisible(f, false); 200 mFragments.put(index, f); 201 } else { 202 Log.w(TAG, "Bad fragment at key " + key); 203 } 204 } 205 } 206 } 207 } 208 209 public void setItemVisible(Fragment item, boolean visible) { 210 FragmentCompat.setMenuVisibility(item, visible); 211 FragmentCompat.setUserVisibleHint(item, visible); 212 } 213 214 @Override 215 public void notifyDataSetChanged() { 216 // update positions in mFragments 217 SparseArrayCompat<Fragment> newFragments = 218 new SparseArrayCompat<Fragment>(mFragments.size()); 219 for (int i=0; i<mFragments.size(); i++) { 220 final int oldPos = mFragments.keyAt(i); 221 final Fragment f = mFragments.valueAt(i); 222 final int newPos = getItemPosition(f); 223 224 if (newPos != POSITION_NONE) { 225 final int pos = (newPos >= 0) ? newPos : oldPos; 226 newFragments.put(pos, f); 227 } 228 } 229 mFragments = newFragments; 230 231 super.notifyDataSetChanged(); 232 } 233 234 public Fragment getFragmentAt(int position) { 235 return mFragments.get(position); 236 } 237} 238