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