BaseFragmentPagerAdapter.java revision f77a7eb196d16110c7b1087352b423913821ff61
1f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/* 2f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Copyright (C) 2011 Google Inc. 3f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed to The Android Open Source Project. 4f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 5f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 6f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * you may not use this file except in compliance with the License. 7f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * You may obtain a copy of the License at 8f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 9f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 10f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 11f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Unless required by applicable law or agreed to in writing, software 12f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 13f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * See the License for the specific language governing permissions and 15f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * limitations under the License. 16f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 17f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 18f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpackage com.android.ex.photo.adapters; 19f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 20f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.Fragment; 21f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.FragmentManager; 22f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.app.FragmentTransaction; 23f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.os.Parcelable; 24f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.app.FragmentPagerAdapter; 25f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.PagerAdapter; 26f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.util.Log; 27f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.util.LruCache; 28f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.view.View; 29f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 30f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/** 31f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * NOTE: This is a direct copy of {@link FragmentPagerAdapter} with four very important 32f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * modifications. 33f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <p> 34f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <ol> 35f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>The method {@link #makeFragmentName(int, int)} is declared "protected" 36f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * in our class. We need to be able to re-define the fragment's name according to data 37f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * only available to sub-classes.</li> 38f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search 39f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * the entire view hierarchy for the given view.</li> 40f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and 41f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * added to a cache. If the fragment is evicted from the cache, it will be deleted. 42f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * An album may contain thousands of photos and we want to avoid having thousands of 43f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * fragments.</li> 44f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <li>The interface {@link OnFragmentPagerListener} and supporting plumbing has been 45f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * added.</li> 46f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * </ol> 47f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 48f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpublic abstract class BaseFragmentPagerAdapter extends PagerAdapter { 49f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 50f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Listener for fragment pager events 51f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 52f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public interface OnFragmentPagerListener { 53f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 54f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * The given fragment has been made the activated fragment. 55f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 56f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void onPageActivated(Fragment fragment); 57f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 58f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 59f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** The default size of {@link #mFragmentCache} */ 60f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private static final int DEFAULT_CACHE_SIZE = 5; 61f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private static final String TAG = "FragmentPagerAdapter"; 62f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private static final boolean DEBUG = false; 63f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 64f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private final FragmentManager mFragmentManager; 65f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private FragmentTransaction mCurTransaction = null; 66f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private Fragment mCurrentPrimaryItem = null; 67f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private OnFragmentPagerListener mPagerListener; 68f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** A cache to store detached fragments before they are removed */ 69f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE); 70f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 71f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public BaseFragmentPagerAdapter(FragmentManager fm) { 72f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mFragmentManager = fm; 73f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 74f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 75f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 76f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Return the Fragment associated with a specified position. 77f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 78f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public abstract Fragment getItem(int position); 79f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 80f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 81f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void startUpdate(View container) { 82f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 83f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 84f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 85f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public Object instantiateItem(View container, int position) { 86f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (mCurTransaction == null) { 87f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction = mFragmentManager.beginTransaction(); 88f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 89f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 90f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Do we already have this fragment? 91f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein String name = makeFragmentName(container.getId(), position); 92f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 93f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Remove item from the cache 94f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mFragmentCache.remove(name); 95f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 96f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein Fragment fragment = mFragmentManager.findFragmentByTag(name); 97f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (fragment != null) { 98f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment); 99f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction.attach(fragment); 100f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } else { 101f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein fragment = getItem(position); 102f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); 103f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction.add(container.getId(), fragment, 104f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein makeFragmentName(container.getId(), position)); 105f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 106f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (fragment != mCurrentPrimaryItem) { 107f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein fragment.setMenuVisibility(false); 108f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 109f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 110f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return fragment; 111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 113f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 114f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void destroyItem(View container, int position, Object object) { 115f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (mCurTransaction == null) { 116f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction = mFragmentManager.beginTransaction(); 117f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 118f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object 119f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein + " v=" + ((Fragment)object).getView()); 120f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 121f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein Fragment fragment = (Fragment) object; 122f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein String name = fragment.getTag(); 123f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (name == null) { 124f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // We prefer to get the name directly from the fragment, but, if the fragment is 125f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // detached before the add transaction is committed, this could be 'null'. In 126f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // that case, generate a name so we can still cache the fragment. 127f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein name = makeFragmentName(container.getId(), position); 128f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 129f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 130f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mFragmentCache.put(name, fragment); 131f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction.detach(fragment); 132f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 133f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 134f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 135f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void setPrimaryItem(View container, int position, Object object) { 136f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein Fragment fragment = (Fragment) object; 137f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (fragment != mCurrentPrimaryItem) { 138f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (mCurrentPrimaryItem != null) { 139f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurrentPrimaryItem.setMenuVisibility(false); 140f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 141f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (fragment != null) { 142f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein fragment.setMenuVisibility(true); 143f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 144f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurrentPrimaryItem = fragment; 145f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 146f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 147f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (mPagerListener != null) { 148f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mPagerListener.onPageActivated(fragment); 149f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 150f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 151f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 152f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 153f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void finishUpdate(View container) { 154f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (mCurTransaction != null) { 155f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction.commitAllowingStateLoss(); 156f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction = null; 157f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mFragmentManager.executePendingTransactions(); 158f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 159f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 160f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 161f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 162f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public boolean isViewFromObject(View view, Object object) { 163f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Ascend the tree to determine if the view is a child of the fragment 164f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein View root = ((Fragment) object).getView(); 165f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein for (Object v = view; v instanceof View; v = ((View) v).getParent()) { 166f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (v == root) { 167f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return true; 168f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 169f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 170f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return false; 171f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 172f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 173f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 174f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public Parcelable saveState() { 175f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return null; 176f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 177f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 178f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 179f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void restoreState(Parcelable state, ClassLoader loader) { 180f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 181f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 182f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** Sets the fragment pager listener */ 183f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void setFragmentPagerListener(OnFragmentPagerListener pagerListener) { 184f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mPagerListener = pagerListener; 185f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 186f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 187f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** Creates a name for the fragment */ 188f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein protected String makeFragmentName(int viewId, int index) { 189f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return "android:switcher:" + viewId + ":" + index; 190f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 191f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 192f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 193f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * A cache of detached fragments. 194f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 195f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private class FragmentCache extends LruCache<String, Fragment> { 196f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public FragmentCache(int size) { 197f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein super(size); 198f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 199f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 200f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 201f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein protected void entryRemoved(boolean evicted, String key, 202f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein Fragment oldValue, Fragment newValue) { 203f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // remove the fragment if it's evicted OR it's replaced by a new fragment 204f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (evicted || (newValue != null && oldValue != newValue)) { 205f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mCurTransaction.remove(oldValue); 206f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 207f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 208f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 209f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein} 210