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