其他
看完这一篇,ViewPager2基本可以投产了
https://me.csdn.net/a774057695
https://github.com/leobert-lan/ViewPager2-Demo/tree/master
用于展示普通视图,横向和纵向滑动 结合Fragment使用以及Fragment的生命周期 复杂布局下,常见的结构,配合CoordinatorLayout和NestedScroll使用 懒、预加载和状态恢复,主要观测生命周期 实现原理和源码分析
ViewPager实例 一个适配器实例 子视图
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* leobert
*/
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder> {
private List<Integer> colors = new ArrayList<>();
{
colors.add(android.R.color.black);
colors.add(android.R.color.holo_purple);
colors.add(android.R.color.holo_blue_dark);
colors.add(android.R.color.holo_green_light);
}
@NonNull
@Override
public ViewPagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewPagerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_page, parent,false));
}
@Override
public void onBindViewHolder(@NonNull ViewPagerViewHolder holder, int position) {
holder.mTvTitle.setText("item " + position);
holder.mContainer.setBackgroundResource(colors.get(position));
}
@Override
public int getItemCount() {
return colors.size();
}
class ViewPagerViewHolder extends RecyclerView.ViewHolder {
TextView mTvTitle;
RelativeLayout mContainer;
public ViewPagerViewHolder(@NonNull View itemView) {
super(itemView);
mContainer = itemView.findViewById(R.id.container);
mTvTitle = itemView.findViewById(R.id.tvTitle);
}
}
}
ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
viewPager2.setAdapter(viewPagerAdapter);
ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
viewPager2.setAdapter(viewPagerAdapter);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewPager2);
if (Build.VERSION.SDK_INT >= 29) {
saveAttributeDataForStyleable(context, R.styleable.ViewPager2, attrs, a, 0, 0);
}
try {
setOrientation(
a.getInt(R.styleable.ViewPager2_android_orientation, ORIENTATION_HORIZONTAL));
} finally {
a.recycle();
}
}
public ViewPagerFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public ViewPagerFragmentStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) {
if (position == 0)
return RvFragment.Companion.newInstance();
else
return PageFragment.newInstance(colors, position - 1);
}
@Override
public int getItemCount() {
return colors.size() + 1;
}
}
* @param fragmentActivity if the {@link ViewPager2} lives directly in a
* {@link FragmentActivity} subclass.
*
* @see FragmentStateAdapter#FragmentStateAdapter(Fragment)
* @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
*/
public FragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
}
/**
* @param fragment if the {@link ViewPager2} lives directly in a {@link Fragment} subclass.
*
* @see FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
* @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
*/
public FragmentStateAdapter(@NonNull Fragment fragment) {
this(fragment.getChildFragmentManager(), fragment.getLifecycle());
}
/**
* @param fragmentManager of {@link ViewPager2}'s host
* @param lifecycle of {@link ViewPager2}'s host
*
* @see FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
* @see FragmentStateAdapter#FragmentStateAdapter(Fragment)
*/
public FragmentStateAdapter(@NonNull FragmentManager fragmentManager,
@NonNull Lifecycle lifecycle) {
mFragmentManager = fragmentManager;
mLifecycle = lifecycle;
super.setHasStableIds(true);
}
/**
* Provide a new Fragment associated with the specified position.
* <p>
* The adapter will be responsible for the Fragment lifecycle:
* <ul>
* <li>The Fragment will be used to display an item.</li>
* <li>The Fragment will be destroyed when it gets too far from the viewport, and its state
* will be saved. When the item is close to the viewport again, a new Fragment will be
* requested, and a previously saved state will be used to initialize it.
* </ul>
* @see ViewPager2#setOffscreenPageLimit
*/
public abstract @NonNull Fragment createFragment(int position);
和:androidx.recyclerview.widget.RecyclerView.Adapter#getItemCount
/**
* Returns the total number of items in the data set held by the adapter.
*
* @return The total number of items in this adapter.
*/
public abstract int getItemCount();
* Similar in behavior to {@link FragmentStatePagerAdapter}
* <p>
* Lifecycle within {@link RecyclerView}:
* <ul>
* <li>{@link RecyclerView.ViewHolder} initially an empty {@link FrameLayout}, serves as a
* re-usable container for a {@link Fragment} in later stages.
* <li>{@link RecyclerView.Adapter#onBindViewHolder} we ask for a {@link Fragment} for the
* position. If we already have the fragment, or have previously saved its state, we use those.
* <li>{@link RecyclerView.Adapter#onAttachedToWindow} we attach the {@link Fragment} to a
* container.
* <li>{@link RecyclerView.Adapter#onViewRecycled} we remove, save state, destroy the
* {@link Fragment}.
* </ul>
*/
2020-03-30 20:22:59.272 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_CREATE
2020-03-30 20:22:59.272 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_START
2020-03-30 20:22:59.273 23446-23446/com.example.viewpager2demo E/lmsg: onResume:RvFragment
2020-03-30 20:22:59.273 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_RESUME
2020-03-30 20:23:07.304 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_CREATE
2020-03-30 20:23:07.304 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_START
2020-03-30 20:23:07.738 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_PAUSE
2020-03-30 20:23:07.738 23446-23446/com.example.viewpager2demo E/lmsg: onPause:RvFragment
2020-03-30 20:23:07.738 23446-23446/com.example.viewpager2demo E/lmsg: onResume:PageFragment 0
2020-03-30 20:23:07.739 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_RESUME
2020-03-30 20:23:09.083 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_CREATE
2020-03-30 20:23:09.083 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_START
2020-03-30 20:23:09.532 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_PAUSE
2020-03-30 20:23:09.533 23446-23446/com.example.viewpager2demo E/lmsg: onPause:PageFragment 0
2020-03-30 20:23:09.533 23446-23446/com.example.viewpager2demo E/lmsg: onResume:PageFragment 1
2020-03-30 20:23:09.534 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_RESUME
2020-03-30 20:23:10.426 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_CREATE
2020-03-30 20:23:10.427 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_START
2020-03-30 20:23:10.859 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_STOP
2020-03-30 20:23:10.859 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_DESTROY
2020-03-30 20:23:10.863 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_PAUSE
2020-03-30 20:23:10.864 23446-23446/com.example.viewpager2demo E/lmsg: onPause:PageFragment 1
2020-03-30 20:23:10.864 23446-23446/com.example.viewpager2demo E/lmsg: onResume:PageFragment 2
2020-03-30 20:23:10.864 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_RESUME
2020-03-30 20:23:12.051 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_CREATE
2020-03-30 20:23:12.051 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_START
2020-03-30 20:23:12.454 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_PAUSE
2020-03-30 20:23:12.454 23446-23446/com.example.viewpager2demo E/lmsg: onPause:PageFragment 2
2020-03-30 20:23:12.454 23446-23446/com.example.viewpager2demo E/lmsg: onResume:PageFragment 3
2020-03-30 20:23:12.455 23446-23446/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_RESUME
2020-03-30 20:37:19.762 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_START
2020-03-30 20:37:19.762 24759-24759/com.example.viewpager2demo E/lmsg: onResume:RvFragment
2020-03-30 20:37:19.762 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_RESUME
2020-03-30 20:37:19.795 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_CREATE
2020-03-30 20:37:19.795 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_START
第一页已经显示
切换到第二页
2020-03-30 20:37:46.293 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_CREATE
2020-03-30 20:37:46.294 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_START
2020-03-30 20:37:46.672 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: RvFragment ON_PAUSE
2020-03-30 20:37:46.674 24759-24759/com.example.viewpager2demo E/lmsg: onPause:RvFragment
2020-03-30 20:37:46.675 24759-24759/com.example.viewpager2demo E/lmsg: onResume:PageFragment 0
2020-03-30 20:37:46.676 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_RESUME
切换到第三页
2020-03-30 20:37:54.440 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_CREATE
2020-03-30 20:37:54.440 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_START
2020-03-30 20:37:54.826 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 0 ON_PAUSE
2020-03-30 20:37:54.827 24759-24759/com.example.viewpager2demo E/lmsg: onPause:PageFragment 0
2020-03-30 20:37:54.827 24759-24759/com.example.viewpager2demo E/lmsg: onResume:PageFragment 1
2020-03-30 20:37:54.827 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_RESUME
切换到第四页
2020-03-30 20:38:05.132 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_CREATE
2020-03-30 20:38:05.132 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_START
2020-03-30 20:38:05.517 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 1 ON_PAUSE
2020-03-30 20:38:05.518 24759-24759/com.example.viewpager2demo E/lmsg: onPause:PageFragment 1
2020-03-30 20:38:05.518 24759-24759/com.example.viewpager2demo E/lmsg: onResume:PageFragment 2
2020-03-30 20:38:05.518 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_RESUME
切换到第五页
2020-03-30 20:38:16.223 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 2 ON_PAUSE
2020-03-30 20:38:16.224 24759-24759/com.example.viewpager2demo E/lmsg: onPause:PageFragment 2
2020-03-30 20:38:16.224 24759-24759/com.example.viewpager2demo E/lmsg: onResume:PageFragment 3
2020-03-30 20:38:16.225 24759-24759/com.example.viewpager2demo D/lmsg: onStateChanged: PageFragment 3 ON_RESUME
private void removeFragment(long itemId) {
Fragment fragment = mFragments.get(itemId);
//中间代码略去
mFragmentManager.beginTransaction().remove(fragment).commitNow();
mFragments.remove(itemId);
}
addOp(new Op(OP_REMOVE, fragment));
void executeOps() {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.mFragment;
if (f != null) {
f.setNextTransition(mTransition, mTransitionStyle);
}
switch (op.mCmd) {
case OP_ADD:
f.setNextAnim(op.mEnterAnim);
mManager.addFragment(f, false);
break;
case OP_REMOVE:
f.setNextAnim(op.mExitAnim);
mManager.removeFragment(f);
//...
}
if (!mReorderingAllowed && op.mCmd != OP_ADD && f != null) {
mManager.moveFragmentToExpectedState(f);
}
}
if (!mReorderingAllowed) {
// Added fragments are added at the end to comply with prior behavior.
mManager.moveToState(mManager.mCurState, true);
}
}
androidx.fragment.app.FragmentManagerImpl#removeFragment
androidx.fragment.app.FragmentManagerImpl#moveToState(int, boolean)
androidx.fragment.app.FragmentManagerImpl#moveFragmentToExpectedState 即:
void moveFragmentToExpectedState(Fragment f) {
if (f == null) {
return;
}
if (!mActive.containsKey(f.mWho)) {
if (DEBUG) {
Log.v(TAG, "Ignoring moving " + f + " to state " + mCurState
+ "since it is not added to " + this);
}
return;
}
int nextState = mCurState;
if (f.mRemoving) {
if (f.isInBackStack()) {
nextState = Math.min(nextState, Fragment.CREATED);
} else {
nextState = Math.min(nextState, Fragment.INITIALIZING);
}
}
moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
//...
}
再顺着往下面,后面方法的代码着实太多,就不贴了
androidx.viewpager2.adapter.FragmentStateAdapter#gcFragments androidx.viewpager2.adapter.FragmentStateAdapter#onBindViewHolder androidx.viewpager2.adapter.FragmentStateAdapter#onViewRecycled
public final void onViewRecycled(@NonNull FragmentViewHolder holder) {
final int viewHolderId = holder.getContainer().getId();
final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
if (boundItemId != null) {
removeFragment(boundItemId);
mItemIdToViewHolder.remove(boundItemId);
}
}
androidx.fragment.app.Fragment#getViewLifecycleOwnerLiveData
https://proandroiddev.com/5-common-mistakes-when-using-architecture-components-403e9899f4cb
https://www.jianshu.com/p/c1ee77f8237f
配合CoordinatorLayout以及支持NestedScroll机制
/ 懒、预加载和状态恢复 /
* Set the number of pages that should be retained to either side of the
* current page in the view hierarchy in an idle state. Pages beyond this
* limit will be recreated from the adapter when needed.
*
* <p>This is offered as an optimization. If you know in advance the number
* of pages you will need to support or have lazy-loading mechanisms in place
* on your pages, tweaking this setting can have benefits in perceived smoothness
* of paging animations and interaction. If you have a small number of pages (3-4)
* that you can keep active all at once, less time will be spent in layout for
* newly created view subtrees as the user pages back and forth.</p>
*
* <p>You should keep this limit low, especially if your pages have complex layouts.
* This setting defaults to 1.</p>
*
* @param limit How many pages will be kept offscreen in an idle state.
*/
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
* @return The current value of the user-visible hint on this fragment.
* @see #setUserVisibleHint(boolean)
*
* @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}
* instead.
*/
@Deprecated
public boolean getUserVisibleHint() {
return mUserVisibleHint;
}
/**
* <p>Set the number of pages that should be retained to either side of the currently visible
* page(s). Pages beyond this limit will be recreated from the adapter when needed. Set this to
* {@link #OFFSCREEN_PAGE_LIMIT_DEFAULT} to use RecyclerView's caching strategy. The given value
* must either be larger than 0, or {@code #OFFSCREEN_PAGE_LIMIT_DEFAULT}.</p>
*
* <p>Pages within {@code limit} pages away from the current page are created and added to the
* view hierarchy, even though they are not visible on the screen. Pages outside this limit will
* be removed from the view hierarchy, but the {@code ViewHolder}s will be recycled as usual by
* {@link RecyclerView}.</p>
*
* <p>This is offered as an optimization. If you know in advance the number of pages you will
* need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting
* can have benefits in perceived smoothness of paging animations and interaction. If you have a
* small number of pages (3-4) that you can keep active all at once, less time will be spent in
* layout for newly created view subtrees as the user pages back and forth.</p>
*
* <p>You should keep this limit low, especially if your pages have complex layouts. By default
* it is set to {@code OFFSCREEN_PAGE_LIMIT_DEFAULT}.</p>
*
* @param limit How many pages will be kept offscreen on either side. Valid values are all
* values {@code >= 1} and {@link #OFFSCREEN_PAGE_LIMIT_DEFAULT}
* @throws IllegalArgumentException If the given limit is invalid
* @see #getOffscreenPageLimit()
*/
public void setOffscreenPageLimit(@OffscreenPageLimit int limit) {
if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {
throw new IllegalArgumentException(
"Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");
}
mOffscreenPageLimit = limit;
// Trigger layout so prefetch happens through getExtraLayoutSize()
mRecyclerView.requestLayout();
}
super(context);
initialize(context, null);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
@RequiresApi(21)
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
mAccessibilityProvider = sFeatureEnhancedA11yEnabled
? new PageAwareAccessibilityProvider()
: new BasicAccessibilityProvider();
mRecyclerView = new RecyclerViewImpl(context);
mRecyclerView.setId(ViewCompat.generateViewId());
mRecyclerView.setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
mLayoutManager = new LinearLayoutManagerImpl(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
setOrientation(context, attrs);
mRecyclerView.setLayoutParams(
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());
// Create ScrollEventAdapter before attaching PagerSnapHelper to RecyclerView, because the
// attach process calls PagerSnapHelperImpl.findSnapView, which uses the mScrollEventAdapter
mScrollEventAdapter = new ScrollEventAdapter(this);
// Create FakeDrag before attaching PagerSnapHelper, same reason as above
mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
mPagerSnapHelper = new PagerSnapHelperImpl();
mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
// Add mScrollEventAdapter after attaching mPagerSnapHelper to mRecyclerView, because we
// don't want to respond on the events sent out during the attach process
mRecyclerView.addOnScrollListener(mScrollEventAdapter);
mPageChangeEventDispatcher = new CompositeOnPageChangeCallback(3);
mScrollEventAdapter.setOnPageChangeCallback(mPageChangeEventDispatcher);
// Callback that updates mCurrentItem after swipes. Also triggered in other cases, but in
// all those cases mCurrentItem will only be overwritten with the same value.
final OnPageChangeCallback currentItemUpdater = new OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
if (mCurrentItem != position) {
mCurrentItem = position;
mAccessibilityProvider.onSetNewCurrentItem();
}
}
@Override
public void onPageScrollStateChanged(int newState) {
if (newState == SCROLL_STATE_IDLE) {
updateCurrentItem();
}
}
};
// Prevents focus from remaining on a no-longer visible page
final OnPageChangeCallback focusClearer = new OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
clearFocus();
if (hasFocus()) { // if clear focus did not succeed
mRecyclerView.requestFocus(View.FOCUS_FORWARD);
}
}
};
// Add currentItemUpdater before mExternalPageChangeCallbacks, because we need to update
// internal state first
mPageChangeEventDispatcher.addOnPageChangeCallback(currentItemUpdater);
mPageChangeEventDispatcher.addOnPageChangeCallback(focusClearer);
// Allow a11y to register its listeners after currentItemUpdater (so it has the
// right data). TODO: replace ordering comments with a test.
mAccessibilityProvider.onInitialize(mPageChangeEventDispatcher, mRecyclerView);
mPageChangeEventDispatcher.addOnPageChangeCallback(mExternalPageChangeCallbacks);
// Add mPageTransformerAdapter after mExternalPageChangeCallbacks, because page transform
// events must be fired after scroll events
mPageTransformerAdapter = new PageTransformerAdapter(mLayoutManager);
mPageChangeEventDispatcher.addOnPageChangeCallback(mPageTransformerAdapter);
attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
}
* Slightly modified RecyclerView to get ViewPager behavior in accessibility and to
* enable/disable user scrolling.
*/
private class RecyclerViewImpl extends RecyclerView {
RecyclerViewImpl(@NonNull Context context) {
super(context);
}
@RequiresApi(23)
@Override
public CharSequence getAccessibilityClassName() {
if (mAccessibilityProvider.handlesRvGetAccessibilityClassName()) {
return mAccessibilityProvider.onRvGetAccessibilityClassName();
}
return super.getAccessibilityClassName();
}
@Override
public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setFromIndex(mCurrentItem);
event.setToIndex(mCurrentItem);
mAccessibilityProvider.onRvInitializeAccessibilityEvent(event);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
return isUserInputEnabled() && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isUserInputEnabled() && super.onInterceptTouchEvent(ev);
}
}
LinearLayoutManagerImpl(Context context) {
super(context);
}
@Override
public boolean performAccessibilityAction(@NonNull RecyclerView.Recycler recycler,
@NonNull RecyclerView.State state, int action, @Nullable Bundle args) {
if (mAccessibilityProvider.handlesLmPerformAccessibilityAction(action)) {
return mAccessibilityProvider.onLmPerformAccessibilityAction(action);
}
return super.performAccessibilityAction(recycler, state, action, args);
}
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler,
@NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(recycler, state, info);
mAccessibilityProvider.onLmInitializeAccessibilityNodeInfo(info);
}
@Override
protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state,
@NonNull int[] extraLayoutSpace) {
int pageLimit = getOffscreenPageLimit();
if (pageLimit == OFFSCREEN_PAGE_LIMIT_DEFAULT) {
// Only do custom prefetching of offscreen pages if requested
super.calculateExtraLayoutSpace(state, extraLayoutSpace);
return;
}
final int offscreenSpace = getPageSize() * pageLimit;
extraLayoutSpace[0] = offscreenSpace;
extraLayoutSpace[1] = offscreenSpace;
}
@Override
public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
@NonNull View child, @NonNull Rect rect, boolean immediate,
boolean focusedChildVisible) {
return false; // users should use setCurrentItem instead
}
}
FragmentStateAdapter
final Adapter<?> currentAdapter = mRecyclerView.getAdapter();
mAccessibilityProvider.onDetachAdapter(currentAdapter);
unregisterCurrentItemDataSetTracker(currentAdapter);
mRecyclerView.setAdapter(adapter);
mCurrentItem = 0;
restorePendingState();
mAccessibilityProvider.onAttachAdapter(adapter);
registerCurrentItemDataSetTracker(adapter);
}
FragmentViewHolder
* {@link ViewHolder} implementation for handling {@link Fragment}s. Used in
* {@link FragmentStateAdapter}.
*/
public final class FragmentViewHolder extends ViewHolder {
private FragmentViewHolder(@NonNull FrameLayout container) {
super(container);
}
@NonNull static FragmentViewHolder create(@NonNull ViewGroup parent) {
FrameLayout container = new FrameLayout(parent.getContext());
container.setLayoutParams(
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
container.setId(ViewCompat.generateViewId());
container.setSaveEnabled(false);
return new FragmentViewHolder(container);
}
@NonNull FrameLayout getContainer() {
return (FrameLayout) itemView;
}
}
PagerSnapHelperImpl() {
}
@Nullable
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
// When interrupting a smooth scroll with a fake drag, we stop RecyclerView's scroll
// animation, which fires a scroll state change to IDLE. PagerSnapHelper then kicks in
// to snap to a page, which we need to prevent here.
// Simplifying that case: during a fake drag, no snapping should occur.
return isFakeDragging() ? null : super.findSnapView(layoutManager);
}
}
* A PageTransformer is invoked whenever a visible/attached page is scrolled.
* This offers an opportunity for the application to apply a custom transformation
* to the page views using animation properties.
*/
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -2 is two pages to the left.
* Minimum / maximum observed values depend on how many pages we keep
* attached, which depends on offscreenPageLimit.
*
* @see #setOffscreenPageLimit(int)
*/
void transformPage(@NonNull View page, float position);
}