Gradle dependencies
compile group: 'androidx.fragment', name: 'fragment', version: '1.5.0-rc01'
- groupId: androidx.fragment
- artifactId: fragment
- version: 1.5.0-rc01
Artifact androidx.fragment:fragment:1.5.0-rc01 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.fragment:fragment com.android.support:support-fragment
Androidx class mapping:
androidx.fragment.app.FragmentStatePagerAdapter android.support.v4.app.FragmentStatePagerAdapter
Overview
Implementation of PagerAdapter that
uses a Fragment to manage each page. This class also handles
saving and restoring of fragment's state.
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the saved
state of that fragment. This allows the pager to hold on to much less
memory associated with each visited page as compared to
FragmentPagerAdapter at the cost of potentially more overhead when
switching between pages.
When using FragmentPagerAdapter the host ViewPager must have a
valid ID set.
Subclasses only need to implement FragmentStatePagerAdapter.getItem(int)
and PagerAdapter.getCount() to have a working adapter.
Here is an example implementation of a pager containing fragments of
lists:
frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentStatePagerSupport.java
complete
The R.layout.fragment_pager
resource of the top-level fragment is:
frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager.xml
complete
The R.layout.fragment_pager_list
resource containing each
individual fragment's layout is:
frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager_list.xml
complete
Summary
Methods |
---|
public void | destroyItem(View container, int position, java.lang.Object object)
Remove a page for the given position. |
public void | finishUpdate(View container)
Called when the a change in the shown pages has been completed. |
public abstract Fragment | getItem(int position)
Return the Fragment associated with a specified position. |
public java.lang.Object | instantiateItem(View container, int position)
Create the page for the given position. |
public abstract boolean | isViewFromObject(View view, java.lang.Object object)
Determines whether a page View is associated with a specific key object
as returned by PagerAdapter.instantiateItem(ViewGroup, int). |
public void | restoreState(Parcelable state, java.lang.ClassLoader loader)
Restore any instance state associated with this adapter and its pages
that was previously saved by PagerAdapter.saveState(). |
public Parcelable | saveState()
Save any instance state associated with this adapter and its pages that should be
restored if the current UI state needs to be reconstructed. |
public void | setPrimaryItem(View container, int position, java.lang.Object object)
Called to inform the adapter of which item is currently considered to
be the "primary", that is the one show to the user as the current page. |
public void | startUpdate(View container)
Called when a change in the shown pages is going to start being made. |
from PagerAdapter | destroyItem, finishUpdate, getCount, getItemPosition, getPageTitle, getPageWidth, instantiateItem, notifyDataSetChanged, registerDataSetObserver, setPrimaryItem, startUpdate, unregisterDataSetObserver |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
BEHAVIOR_SET_USER_VISIBLE_HINTDeprecated: This behavior relies on the deprecated
Fragment.setUserVisibleHint(boolean) API. Use
FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT to switch to its replacement,
FragmentTransaction.setMaxLifecycle(Fragment, Lifecycle.State).
Indicates that Fragment.setUserVisibleHint(boolean) will be called when the current
fragment changes.
See also: FragmentStatePagerAdapter.FragmentStatePagerAdapter(FragmentManager, int)
public static final int
BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTIndicates that only the current fragment will be in the
state. All other Fragments are capped at .
See also: FragmentStatePagerAdapter.FragmentStatePagerAdapter(FragmentManager, int)
Constructors
Deprecated: use FragmentStatePagerAdapter.FragmentStatePagerAdapter(FragmentManager, int) with
FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
Constructor for FragmentStatePagerAdapter that sets the fragment manager for the
adapter. This is the equivalent of calling
FragmentStatePagerAdapter.FragmentStatePagerAdapter(FragmentManager, int) and passing in
FragmentStatePagerAdapter.BEHAVIOR_SET_USER_VISIBLE_HINT.
Fragments will have Fragment.setUserVisibleHint(boolean) called whenever the
current Fragment changes.
Parameters:
fm: fragment manager that will interact with this adapter
Constructor for FragmentStatePagerAdapter.
If FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT is passed in, then only the current
Fragment is in the state, while all other fragments are
capped at . If FragmentStatePagerAdapter.BEHAVIOR_SET_USER_VISIBLE_HINT is
passed, all fragments are in the state and there will be
callbacks to Fragment.setUserVisibleHint(boolean).
Parameters:
fm: fragment manager that will interact with this adapter
behavior: determines if only current fragments are in a resumed state
Methods
public abstract
Fragment getItem(int position)
Return the Fragment associated with a specified position.
public void
startUpdate(View container)
Deprecated: Use PagerAdapter.startUpdate(ViewGroup)
Called when a change in the shown pages is going to start being made.
Parameters:
container: The containing View which is displaying this adapter's
page views.
public java.lang.Object
instantiateItem(View container, int position)
Deprecated: Use PagerAdapter.instantiateItem(ViewGroup, int)
Create the page for the given position. The adapter is responsible
for adding the view to the container given here, although it only
must ensure this is done by the time it returns from
PagerAdapter.finishUpdate(ViewGroup).
Parameters:
container: The containing View in which the page will be shown.
position: The page position to be instantiated.
Returns:
Returns an Object representing the new page. This does not
need to be a View, but can be some other container of the page.
public void
destroyItem(View container, int position, java.lang.Object object)
Deprecated: Use PagerAdapter.destroyItem(ViewGroup, int, Object)
Remove a page for the given position. The adapter is responsible
for removing the view from its container, although it only must ensure
this is done by the time it returns from PagerAdapter.finishUpdate(View).
Parameters:
container: The containing View from which the page will be removed.
position: The page position to be removed.
object: The same object that was returned by
PagerAdapter.instantiateItem(View, int).
public void
setPrimaryItem(View container, int position, java.lang.Object object)
Deprecated: Use PagerAdapter.setPrimaryItem(ViewGroup, int, Object)
Called to inform the adapter of which item is currently considered to
be the "primary", that is the one show to the user as the current page.
Parameters:
container: The containing View from which the page will be removed.
position: The page position that is now the primary.
object: The same object that was returned by
PagerAdapter.instantiateItem(View, int).
public void
finishUpdate(View container)
Deprecated: Use PagerAdapter.finishUpdate(ViewGroup)
Called when the a change in the shown pages has been completed. At this
point you must ensure that all of the pages have actually been added or
removed from the container as appropriate.
Parameters:
container: The containing View which is displaying this adapter's
page views.
public abstract boolean
isViewFromObject(View view, java.lang.Object object)
Determines whether a page View is associated with a specific key object
as returned by PagerAdapter.instantiateItem(ViewGroup, int). This method is
required for a PagerAdapter to function properly.
Parameters:
view: Page View to check for association with object
object: Object to check for association with view
Returns:
true if view
is associated with the key object object
public Parcelable
saveState()
Save any instance state associated with this adapter and its pages that should be
restored if the current UI state needs to be reconstructed.
Returns:
Saved state for this adapter
public void
restoreState(Parcelable state, java.lang.ClassLoader loader)
Restore any instance state associated with this adapter and its pages
that was previously saved by PagerAdapter.saveState().
Parameters:
state: State previously saved by a call to PagerAdapter.saveState()
loader: A ClassLoader that should be used to instantiate any restored objects
Source
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.fragment.app;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager.widget.PagerAdapter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
* Implementation of {@link PagerAdapter} that
* uses a {@link Fragment} to manage each page. This class also handles
* saving and restoring of fragment's state.
*
* <p>This version of the pager is more useful when there are a large number
* of pages, working more like a list view. When pages are not visible to
* the user, their entire fragment may be destroyed, only keeping the saved
* state of that fragment. This allows the pager to hold on to much less
* memory associated with each visited page as compared to
* {@link FragmentPagerAdapter} at the cost of potentially more overhead when
* switching between pages.
*
* <p>When using FragmentPagerAdapter the host ViewPager must have a
* valid ID set.</p>
*
* <p>Subclasses only need to implement {@link #getItem(int)}
* and {@link #getCount()} to have a working adapter.
*
* <p>Here is an example implementation of a pager containing fragments of
* lists:
*
* {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentStatePagerSupport.java
* complete}
*
* <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
*
* {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager.xml
* complete}
*
* <p>The <code>R.layout.fragment_pager_list</code> resource containing each
* individual fragment's layout is:
*
* {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager_list.xml
* complete}
*
* @deprecated Switch to {@link androidx.viewpager2.widget.ViewPager2} and use
* {@link androidx.viewpager2.adapter.FragmentStateAdapter} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
public abstract class FragmentStatePagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentStatePagerAdapt";
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
@IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
private @interface Behavior { }
/**
* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
* fragment changes.
*
* @deprecated This behavior relies on the deprecated
* {@link Fragment#setUserVisibleHint(boolean)} API. Use
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
* {@link FragmentTransaction#setMaxLifecycle}.
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
@Deprecated
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
/**
* Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
* state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
*
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
private final FragmentManager mFragmentManager;
private final int mBehavior;
private FragmentTransaction mCurTransaction = null;
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
private ArrayList<Fragment> mFragments = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
private boolean mExecutingFinishUpdate;
/**
* Constructor for {@link FragmentStatePagerAdapter} that sets the fragment manager for the
* adapter. This is the equivalent of calling
* {@link #FragmentStatePagerAdapter(FragmentManager, int)} and passing in
* {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.
*
* <p>Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the
* current Fragment changes.</p>
*
* @param fm fragment manager that will interact with this adapter
* @deprecated use {@link #FragmentStatePagerAdapter(FragmentManager, int)} with
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
*/
@Deprecated
public FragmentStatePagerAdapter(@NonNull FragmentManager fm) {
this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
}
/**
* Constructor for {@link FragmentStatePagerAdapter}.
*
* If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current
* Fragment is in the {@link Lifecycle.State#RESUMED} state, while all other fragments are
* capped at {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is
* passed, all fragments are in the {@link Lifecycle.State#RESUMED} state and there will be
* callbacks to {@link Fragment#setUserVisibleHint(boolean)}.
*
* @param fm fragment manager that will interact with this adapter
* @param behavior determines if only current fragments are in a resumed state
*/
public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
@Behavior int behavior) {
mFragmentManager = fm;
mBehavior = behavior;
}
/**
* Return the Fragment associated with a specified position.
*/
@NonNull
public abstract Fragment getItem(int position);
@Override
public void startUpdate(@NonNull ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this
+ " requires a view id");
}
}
@SuppressWarnings("deprecation")
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
fragment.setUserVisibleHint(false);
}
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
}
return fragment;
}
// TODO(b/141958824): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
if (fragment.equals(mCurrentPrimaryItem)) {
mCurrentPrimaryItem = null;
}
}
@Override
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
fragment.setMenuVisibility(true);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(@NonNull ViewGroup container) {
if (mCurTransaction != null) {
// We drop any transactions that attempt to be committed
// from a re-entrant call to finishUpdate(). We need to
// do this as a workaround for Robolectric running measure/layout
// calls inline rather than allowing them to be posted
// as they would on a real device.
if (!mExecutingFinishUpdate) {
try {
mExecutingFinishUpdate = true;
mCurTransaction.commitNowAllowingStateLoss();
} finally {
mExecutingFinishUpdate = false;
}
}
mCurTransaction = null;
}
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return ((Fragment)object).getView() == view;
}
@Override
@Nullable
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
@Override
@SuppressWarnings("deprecation")
public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
if (fss != null) {
for (int i=0; i<fss.length; i++) {
mSavedState.add((Fragment.SavedState)fss[i]);
}
}
Iterable<String> keys = bundle.keySet();
for (String key: keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
f.setMenuVisibility(false);
mFragments.set(index, f);
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
}
}
}