1/*
2 * Copyright (C) 2011 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 android.support.v4.view;
18
19import android.database.DataSetObservable;
20import android.database.DataSetObserver;
21import android.os.Parcelable;
22import android.view.View;
23import android.view.ViewGroup;
24
25/**
26 * Base class providing the adapter to populate pages inside of
27 * a {@link ViewPager}.  You will most likely want to use a more
28 * specific implementation of this, such as
29 * {@link android.support.v4.app.FragmentPagerAdapter} or
30 * {@link android.support.v4.app.FragmentStatePagerAdapter}.
31 *
32 * <p>When you implement a PagerAdapter, you must override the following methods
33 * at minimum:</p>
34 * <ul>
35 * <li>{@link #instantiateItem(ViewGroup, int)}</li>
36 * <li>{@link #destroyItem(ViewGroup, int, Object)}</li>
37 * <li>{@link #getCount()}</li>
38 * <li>{@link #isViewFromObject(View, Object)}</li>
39 * </ul>
40 *
41 * <p>PagerAdapter is more general than the adapters used for
42 * {@link android.widget.AdapterView AdapterViews}. Instead of providing a
43 * View recycling mechanism directly ViewPager uses callbacks to indicate the
44 * steps taken during an update. A PagerAdapter may implement a form of View
45 * recycling if desired or use a more sophisticated method of managing page
46 * Views such as Fragment transactions where each page is represented by its
47 * own Fragment.</p>
48 *
49 * <p>ViewPager associates each page with a key Object instead of working with
50 * Views directly. This key is used to track and uniquely identify a given page
51 * independent of its position in the adapter. A call to the PagerAdapter method
52 * {@link #startUpdate(ViewGroup)} indicates that the contents of the ViewPager
53 * are about to change. One or more calls to {@link #instantiateItem(ViewGroup, int)}
54 * and/or {@link #destroyItem(ViewGroup, int, Object)} will follow, and the end
55 * of an update will be signaled by a call to {@link #finishUpdate(ViewGroup)}.
56 * By the time {@link #finishUpdate(ViewGroup) finishUpdate} returns the views
57 * associated with the key objects returned by
58 * {@link #instantiateItem(ViewGroup, int) instantiateItem} should be added to
59 * the parent ViewGroup passed to these methods and the views associated with
60 * the keys passed to {@link #destroyItem(ViewGroup, int, Object) destroyItem}
61 * should be removed. The method {@link #isViewFromObject(View, Object)} identifies
62 * whether a page View is associated with a given key object.</p>
63 *
64 * <p>A very simple PagerAdapter may choose to use the page Views themselves
65 * as key objects, returning them from {@link #instantiateItem(ViewGroup, int)}
66 * after creation and adding them to the parent ViewGroup. A matching
67 * {@link #destroyItem(ViewGroup, int, Object)} implementation would remove the
68 * View from the parent ViewGroup and {@link #isViewFromObject(View, Object)}
69 * could be implemented as <code>return view == object;</code>.</p>
70 *
71 * <p>PagerAdapter supports data set changes. Data set changes must occur on the
72 * main thread and must end with a call to {@link #notifyDataSetChanged()} similar
73 * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data
74 * set change may involve pages being added, removed, or changing position. The
75 * ViewPager will keep the current page active provided the adapter implements
76 * the method {@link #getItemPosition(Object)}.</p>
77 */
78public abstract class PagerAdapter {
79    private final DataSetObservable mObservable = new DataSetObservable();
80    private DataSetObserver mViewPagerObserver;
81
82    public static final int POSITION_UNCHANGED = -1;
83    public static final int POSITION_NONE = -2;
84
85    /**
86     * Return the number of views available.
87     */
88    public abstract int getCount();
89
90    /**
91     * Called when a change in the shown pages is going to start being made.
92     * @param container The containing View which is displaying this adapter's
93     * page views.
94     */
95    public void startUpdate(ViewGroup container) {
96        startUpdate((View) container);
97    }
98
99    /**
100     * Create the page for the given position.  The adapter is responsible
101     * for adding the view to the container given here, although it only
102     * must ensure this is done by the time it returns from
103     * {@link #finishUpdate(ViewGroup)}.
104     *
105     * @param container The containing View in which the page will be shown.
106     * @param position The page position to be instantiated.
107     * @return Returns an Object representing the new page.  This does not
108     * need to be a View, but can be some other container of the page.
109     */
110    public Object instantiateItem(ViewGroup container, int position) {
111        return instantiateItem((View) container, position);
112    }
113
114    /**
115     * Remove a page for the given position.  The adapter is responsible
116     * for removing the view from its container, although it only must ensure
117     * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
118     *
119     * @param container The containing View from which the page will be removed.
120     * @param position The page position to be removed.
121     * @param object The same object that was returned by
122     * {@link #instantiateItem(View, int)}.
123     */
124    public void destroyItem(ViewGroup container, int position, Object object) {
125        destroyItem((View) container, position, object);
126    }
127
128    /**
129     * Called to inform the adapter of which item is currently considered to
130     * be the "primary", that is the one show to the user as the current page.
131     *
132     * @param container The containing View from which the page will be removed.
133     * @param position The page position that is now the primary.
134     * @param object The same object that was returned by
135     * {@link #instantiateItem(View, int)}.
136     */
137    public void setPrimaryItem(ViewGroup container, int position, Object object) {
138        setPrimaryItem((View) container, position, object);
139    }
140
141    /**
142     * Called when the a change in the shown pages has been completed.  At this
143     * point you must ensure that all of the pages have actually been added or
144     * removed from the container as appropriate.
145     * @param container The containing View which is displaying this adapter's
146     * page views.
147     */
148    public void finishUpdate(ViewGroup container) {
149        finishUpdate((View) container);
150    }
151
152    /**
153     * Called when a change in the shown pages is going to start being made.
154     * @param container The containing View which is displaying this adapter's
155     * page views.
156     *
157     * @deprecated Use {@link #startUpdate(ViewGroup)}
158     */
159    @Deprecated
160    public void startUpdate(View container) {
161    }
162
163    /**
164     * Create the page for the given position.  The adapter is responsible
165     * for adding the view to the container given here, although it only
166     * must ensure this is done by the time it returns from
167     * {@link #finishUpdate(ViewGroup)}.
168     *
169     * @param container The containing View in which the page will be shown.
170     * @param position The page position to be instantiated.
171     * @return Returns an Object representing the new page.  This does not
172     * need to be a View, but can be some other container of the page.
173     *
174     * @deprecated Use {@link #instantiateItem(ViewGroup, int)}
175     */
176    @Deprecated
177    public Object instantiateItem(View container, int position) {
178        throw new UnsupportedOperationException(
179                "Required method instantiateItem was not overridden");
180    }
181
182    /**
183     * Remove a page for the given position.  The adapter is responsible
184     * for removing the view from its container, although it only must ensure
185     * this is done by the time it returns from {@link #finishUpdate(View)}.
186     *
187     * @param container The containing View from which the page will be removed.
188     * @param position The page position to be removed.
189     * @param object The same object that was returned by
190     * {@link #instantiateItem(View, int)}.
191     *
192     * @deprecated Use {@link #destroyItem(ViewGroup, int, Object)}
193     */
194    @Deprecated
195    public void destroyItem(View container, int position, Object object) {
196        throw new UnsupportedOperationException("Required method destroyItem was not overridden");
197    }
198
199    /**
200     * Called to inform the adapter of which item is currently considered to
201     * be the "primary", that is the one show to the user as the current page.
202     *
203     * @param container The containing View from which the page will be removed.
204     * @param position The page position that is now the primary.
205     * @param object The same object that was returned by
206     * {@link #instantiateItem(View, int)}.
207     *
208     * @deprecated Use {@link #setPrimaryItem(ViewGroup, int, Object)}
209     */
210    @Deprecated
211    public void setPrimaryItem(View container, int position, Object object) {
212    }
213
214    /**
215     * Called when the a change in the shown pages has been completed.  At this
216     * point you must ensure that all of the pages have actually been added or
217     * removed from the container as appropriate.
218     * @param container The containing View which is displaying this adapter's
219     * page views.
220     *
221     * @deprecated Use {@link #finishUpdate(ViewGroup)}
222     */
223    @Deprecated
224    public void finishUpdate(View container) {
225    }
226
227    /**
228     * Determines whether a page View is associated with a specific key object
229     * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
230     * required for a PagerAdapter to function properly.
231     *
232     * @param view Page View to check for association with <code>object</code>
233     * @param object Object to check for association with <code>view</code>
234     * @return true if <code>view</code> is associated with the key object <code>object</code>
235     */
236    public abstract boolean isViewFromObject(View view, Object object);
237
238    /**
239     * Save any instance state associated with this adapter and its pages that should be
240     * restored if the current UI state needs to be reconstructed.
241     *
242     * @return Saved state for this adapter
243     */
244    public Parcelable saveState() {
245        return null;
246    }
247
248    /**
249     * Restore any instance state associated with this adapter and its pages
250     * that was previously saved by {@link #saveState()}.
251     *
252     * @param state State previously saved by a call to {@link #saveState()}
253     * @param loader A ClassLoader that should be used to instantiate any restored objects
254     */
255    public void restoreState(Parcelable state, ClassLoader loader) {
256    }
257
258    /**
259     * Called when the host view is attempting to determine if an item's position
260     * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given
261     * item has not changed or {@link #POSITION_NONE} if the item is no longer present
262     * in the adapter.
263     *
264     * <p>The default implementation assumes that items will never
265     * change position and always returns {@link #POSITION_UNCHANGED}.
266     *
267     * @param object Object representing an item, previously returned by a call to
268     *               {@link #instantiateItem(View, int)}.
269     * @return object's new position index from [0, {@link #getCount()}),
270     *         {@link #POSITION_UNCHANGED} if the object's position has not changed,
271     *         or {@link #POSITION_NONE} if the item is no longer present.
272     */
273    public int getItemPosition(Object object) {
274        return POSITION_UNCHANGED;
275    }
276
277    /**
278     * This method should be called by the application if the data backing this adapter has changed
279     * and associated views should update.
280     */
281    public void notifyDataSetChanged() {
282        synchronized (this) {
283            if (mViewPagerObserver != null) {
284                mViewPagerObserver.onChanged();
285            }
286        }
287        mObservable.notifyChanged();
288    }
289
290    /**
291     * Register an observer to receive callbacks related to the adapter's data changing.
292     *
293     * @param observer The {@link android.database.DataSetObserver} which will receive callbacks.
294     */
295    public void registerDataSetObserver(DataSetObserver observer) {
296        mObservable.registerObserver(observer);
297    }
298
299    /**
300     * Unregister an observer from callbacks related to the adapter's data changing.
301     *
302     * @param observer The {@link android.database.DataSetObserver} which will be unregistered.
303     */
304    public void unregisterDataSetObserver(DataSetObserver observer) {
305        mObservable.unregisterObserver(observer);
306    }
307
308    void setViewPagerObserver(DataSetObserver observer) {
309        synchronized (this) {
310            mViewPagerObserver = observer;
311        }
312    }
313
314    /**
315     * This method may be called by the ViewPager to obtain a title string
316     * to describe the specified page. This method may return null
317     * indicating no title for this page. The default implementation returns
318     * null.
319     *
320     * @param position The position of the title requested
321     * @return A title for the requested page
322     */
323    public CharSequence getPageTitle(int position) {
324        return null;
325    }
326
327    /**
328     * Returns the proportional width of a given page as a percentage of the
329     * ViewPager's measured width from (0.f-1.f]
330     *
331     * @param position The position of the page requested
332     * @return Proportional width for the given page position
333     */
334    public float getPageWidth(int position) {
335        return 1.f;
336    }
337}
338