package com.banner.recycler.recyclerviewpager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.os.Build;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* RecyclerViewPager
*
* @author Green
*/
public class RecyclerViewPager extends RecyclerView {
public static final boolean DEBUG = BuildConfig.DEBUG;
private RecyclerViewPagerAdapter<?> mViewPagerAdapter;
private float mTriggerOffset = 0.25f;
private float mFlingFactor = 0.15f;
private float mMillisecondsPerInch = 25f;
private float mTouchSpan;
private List<OnPageChangedListener> mOnPageChangedListeners;
private int mSmoothScrollTargetPosition = -1;
private int mPositionBeforeScroll = -1;
private boolean mSinglePageFling;
boolean isInertia; // inertia slide state
float minSlideDistance;
PointF touchStartPoint;
boolean mNeedAdjust;
int mFisrtLeftWhenDragging;
int mFirstTopWhenDragging;
View mCurView;
int mMaxLeftWhenDragging = Integer.MIN_VALUE;
int mMinLeftWhenDragging = Integer.MAX_VALUE;
int mMaxTopWhenDragging = Integer.MIN_VALUE;
int mMinTopWhenDragging = Integer.MAX_VALUE;
private int mPositionOnTouchDown = -1;
private boolean mHasCalledOnPageChanged = true;
private boolean reverseLayout = false;
private float mLastY;
public RecyclerViewPager(Context context) {
this(context, null);
}
public RecyclerViewPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RecyclerViewPager(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initAttrs(context, attrs, defStyle);
setNestedScrollingEnabled(false);
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
minSlideDistance = viewConfiguration.getScaledTouchSlop();
}
private void initAttrs(Context context, AttributeSet attrs, int defStyle) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewPager, defStyle,
0);
mFlingFactor = a.getFloat(R.styleable.RecyclerViewPager_rvp_flingFactor, 0.15f);
mTriggerOffset = a.getFloat(R.styleable.RecyclerViewPager_rvp_triggerOffset, 0.25f);
mSinglePageFling = a.getBoolean(R.styleable.RecyclerViewPager_rvp_singlePageFling, mSinglePageFling);
isInertia = a.getBoolean(R.styleable.RecyclerViewPager_rvp_inertia, false);
mMillisecondsPerInch = a.getFloat(R.styleable.RecyclerViewPager_rvp_millisecondsPerInch, 25f);
a.recycle();
}
public void setFlingFactor(float flingFactor) {
mFlingFactor = flingFactor;
}
public float getFlingFactor() {
return mFlingFactor;
}
public void setTriggerOffset(float triggerOffset) {
mTriggerOffset = triggerOffset;
}
public float getTriggerOffset() {
return mTriggerOffset;
}
public void setSinglePageFling(boolean singlePageFling) {
mSinglePageFling = singlePageFling;
}
public boolean isSinglePageFling() {
return mSinglePageFling;
}
public boolean isInertia() {
return isInertia;
}
public void setInertia(boolean inertia) {
isInertia = inertia;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
try {
Field fLayoutState = state.getClass().getDeclaredField("mLayoutState");
fLayoutState.setAccessible(true);
Object layoutState = fLayoutState.get(state);
Field fAnchorOffset = layoutState.getClass().getDeclaredField("mAnchorOffset");
Field fAnchorPosition = layoutState.getClass().getDeclaredField("mAnchorPosition");
fAnchorPosition.setAccessible(true);
fAnchorOffset.setAccessible(true);
if (fAnchorOffset.getInt(layoutState) > 0) {
fAnchorPosition.set(layoutState, fAnchorPosition.getInt(layoutState) - 1);
} else if (fAnchorOffset.getInt(layoutState) < 0) {
fAnchorPosition.set(layoutState, fAnchorPosition.getInt(layoutState) + 1);
}
fAnchorOffset.setInt(layoutState, 0);
} catch (Throwable e) {
e.printStackTrace();
}
super.onRestoreInstanceState(state);
}
@Override
public void setAdapter(Adapter adapter) {
mViewPagerAdapter = ensureRecyclerViewPagerAdapter(adapter);
super.setAdapter(mViewPagerAdapter);
}
@Override
public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
mViewPagerAdapter = ensureRecyclerViewPagerAdapter(adapter);
super.swapAdapter(mViewPagerAdapter, removeAndRecycleExistingViews);
}
@Override
public Adapter getAdapter() {
if (mViewPagerAdapter != null) {
return mViewPagerAdapter.mAdapter;
}
return null;
}
public RecyclerViewPagerAdapter getWrapperAdapter() {
return mViewPagerAdapter;
}
@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
if (layout instanceof LinearLayoutManager) {
reverseLayout = ((LinearLayoutManager) layout).getReverseLayout();
}
}
@Override
public boolean fling(int velocityX, int velocityY) {
boolean flinging = super.fling((int) (velocityX * mFlingFactor), (int) (velocityY * mFlingFactor));
if (flinging) {
if (getLayoutManager().canScrollHorizontally()) {
adjustPositionX(velocityX);
} else {
adjustPositionY(velocityY);
}
}
if (DEBUG) {
Log.d("@", "velocityX:" + velocityX);
Log.d("@", "velocityY:" + velocityY);
}
return flinging;
}
@Override
public void smoothScrollToPosition(int position) {
if (DEBUG) {
Log.d("@", "smoothScrollToPosition:" + position);
}
if (mPositionBeforeScroll < 0) {
mPositionBeforeScroll = getCurrentPosition();
}
mSmoothScrollTargetPosition = position;
if (getLayoutManager() != null && getLayoutManager() instanceof LinearLayoutManager) {
// exclude item decoration
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
if (getLayoutManager() == null) {
return null;
}
return ((LinearLayoutManager) getLayoutManager())
.computeScrollVectorForPosition(targetPosition);
}
@Override
protected void onTargetFound(View targetView, State state, Action action) {
if (getLayoutManager() == null) {
return;
}
int dx = calculateDxToMakeVisible(targetView,
getHorizontalSnapPreference());
int dy = calculateDyToMakeVisible(targetView,
getVerticalSnapPreference());