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