Android中自定上下拖動Viewpager
我們在淘寶上購物的時候,在瀏覽商品頁面時,有看到有”繼續拖動,檢視圖文詳情”,實則就是縱向拖動的Viewpager。今天我們就要來實現它。先上效果圖。
要實現上面的效果,我們今天必須學習兩樣東西,一個就是ViewDragHelper,另外一個就是GestureDetectorCompat。好了,我們先,來看看View的拖動工具類。ViewDragHelper的基本用法很簡單。官方自帶的DrawerLayout側滑選單,也是用的這個實現的額。下面我們實現一個View的拖動效果。
先自定義一個View在構造方法裡面例項化一個ViewDragHelper物件,直接呼叫靜態方法裡面有三個引數,第一個Context。第二個精度,第三個拖動回撥。這裡要說一下,精度給的值越大越敏感。
mDragHelper = ViewDragHelper.create(this, 10f, new DragHelperCallback());
第三個引數通過,繼承ViewDragHelper.Callback,你就可以在回撥中處理各種拖動行為。當然這裡我們自定義的一個類DragHelperCallback來重寫裡面的方法,簡單來講,我們重寫三個方法就可以了,如下
@Override
public boolean tryCaptureView(View arg0, int arg1) {
return true;
}
@Override
public int clampViewPositionVertical (View child, int top, int dy) {
return top;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dy) {
return left;
}
以上三個方法,tryCaptureView的返回值可以決定一個parentview中哪個子view可以拖動,即可以判斷方法引數中的int值,給以具體的返回false或true。返回true表示該子View可以被拖動。英語好的朋友可能已經大概知道接下來兩個方法的作用了;對,翻譯過來,就是固定View的縱向和橫向位置。也就是說給拖動View設定邊界。。我們重點來說一下方法裡面第二個引數。第二個引數是指當前拖動子view應該到達的x座標。按照正常邏輯,應該是原值返回,但是我們在使用手機或者其他電子裝置的時候是希望在螢幕以內,以外我們看不到,就沒有意義了。所以,我們要更改一下,方法裡面的內容讓其返回一個恰當的值。
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//得到頂部內邊距
int topBound = getPaddingTop();
//得到底部內邊距
int bottomBound = getHeight() - child.getHeight();
//返回最小值
int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
我們希望子View在ViewGroup內運動。最小Y軸即為topBound。最大即為bottomBound。通過Math.min方法判斷最小值,返回。就可以讓子View在父View內部運動。不會滑出邊界。同樣橫向方法照樣改寫。最小橫向移動位置在leftBound。最大為整個view最右邊位置減去子view最右邊位置再減去View的右內邊距。
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int leftBound = getPaddingLeft();
int rightBound = getWidth() - child.getWidth();
int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
這樣,就實現了上面的效果,很簡單吧,但是如果想要更復雜的操作,那就得繼續深入。我們還可以重寫幾個方法用於其他功能。比如
//手指釋放的時候回撥// 滑動鬆開後
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel){}
//在邊界拖動時回撥
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId){}
我們想要view拖動後回到原位,首先拖動前先儲存,view的位置座標,然後在onViewReleased()方法種呼叫settleCapturedViewAt(int,int)方法裡面傳入x,y值。最後重繪invalidate();就可以了。邊界拖動同樣通過呼叫captureChildView(edgeFlags, pointerId)就可以了。我們就簡單的做了一個可拖動子view的viewgroup了。全部程式碼貼出來如下。
public class MyViewGoup extends LinearLayout {
private ViewDragHelper mDragger;
public MyViewGoup(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, 10f, new DragHelperCallback());
}
class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int leftBound = getPaddingLeft();
int rightBound = getWidth() - child.getWidth();
int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//得到頂部內邊距
int topBound = getPaddingTop();
//得到底部內邊距
int bottomBound = getHeight() - child.getHeight();
//返回最小值
int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragger.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
}
上面簡單的view拖動,我們就完成了,接下來開始我們的上下拖動ViewPager。大概思路也是一樣的。先自定一個類,繼承自ViewGroup。然後。在xml檔案中裝入兩個子佈局(ViewGroup)。然後,通過設定兩個子佈局的高度來控制view的拖動。同樣開始也是構造方法,建立ViewDragHelper物件。設定回撥。設定監聽,初始化炒作。
public class DragLayout extends ViewGroup {
/**
* 拖動工具類
*/
private final ViewDragHelper mDragHelper;
/**
* 手勢處理,Y軸
**/
private GestureDetectorCompat gestureDetectorCompat;
/**
* 上下兩個frameLayout,在Activity中注入fragment
*/
private View frameView1, frameView2;
private int viewHeight;
/**
* 滑動速度的閾值,超過這個絕對值認為是上下
*/
private static final int VEL_THRESHOLD = 100;
/**
* 單位是畫素,當上下滑動速度不夠時,通過這個閾值來判定是應該粘到頂部還是底部
*/
private static final int DISTANCE_THRESHOLD = 100;
/**
* 手指按下,frameView1的gettop
*/
private int downTop1;
/**
* 手指鬆開是否載入一下一頁的 介面
*/
private ShowNextPageNotifier nextPageNotifier;
public DragLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragLayout(Context context) {
this(context, null);
}
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper
.create(this, 10f, new DragHelperCallback());
//邊界攔截,全部
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
gestureDetectorCompat = new GestureDetectorCompat(context,
new YScrollDetector());
}
這裡,我們加入了手勢操作GestureDetectorCompat 。同樣在構造方法中建立一個例項。需要兩個引數。第一個Context上下文。第二個引數監聽。Detector類對外提供了兩個介面:OnGestureListener,OnDoubleTapListener,還有一個內部類SimpleOnGestureListener;SimpleOnGestureListener類是GestureDetector提供給我們的一個更方便的響應不同手勢的類,它實現了上述兩個介面,該類是static class,也就是說它實際上是一個外部類,我們可以在外部繼承這個類,重寫裡面的手勢處理方法。因此實現手勢識別有兩種方法,一種實現OnGestureListener介面,另一種是使用SimpleOnGestureListener類。我們今天這裡繼承SimpleOnGestureListener監聽,主要判斷Y軸上的手勢(因為是上下滑動)。
/**
* 手勢監聽
*/
private class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (frameView2.getTop() <= 0 && distanceY > 0f) {
return false;
}
// 垂直滾動時dy>dx,才被認定是上下拖動
return Math.abs(distanceY) > Math.abs(distanceX);
}
}
我們剛才做的一個小荔枝。自定義類是繼承的LinearLayout。所以就沒有重寫OnLayout方法。這裡我們繼承自ViewGroup所以要重寫該方法。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (frameView1.getTop() == 0) {
frameView1.layout(l, 0, r, b - t);
frameView2.layout(l, 0, r, b - t);
//
viewHeight = frameView1.getMeasuredHeight();
//將fragmeView2向下移動距離
frameView2.offsetTopAndBottom(viewHeight);
} else {
// 如果已經被初始化,這次onLayout只需將之前的狀態存入即可
frameView1
.layout(l, frameView1.getTop(), r, frameView1.getBottom());
frameView2
.layout(l, frameView2.getTop(), r, frameView2.getBottom());
}
}
先判斷第一個View是否在最頂上,如果在,就將fragmeView2排在View1下面。沒在表示,在拖動狀態。注意,排布時候呼叫的方法。offsetTopAndBottom方法就是將View的位置向下移動,它和scrollTo的區別是,前一個是表示移動View座標變化,後一個是移動View的內容,而View的座標不變。
有了排布,我們還是要有測量的,不然排布或者得到View的寬高的時候不準確,或者得不到。接下來測量
@SuppressLint("NewApi")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
//計算要將View的內邊距計算進去然後再計算整個寬度高度,
// 並且最後必須呼叫setMeasuredDimension方法設定寬度和高度,
// resolveSizeAndState() 方法將返回一個合適的尺寸,只要將測量模式和我們計算的寬度高度傳進去即可。
// 該方法在API11開始出現,低於該版本將無法使用該方法,
setMeasuredDimension(
resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
上面已經說了,resolveSizeAndState()方法低版本用不了,那麼,就手動複製進來,
/**
* 這是View的方法,該方法不支援android低版本(2.2、2.3)的作業系統,所以手動複製過來以免強制退出
*/
public static int resolveSizeAndState(int size, int measureSpec,
int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
接下來,我們在來寫最重要的拖動回撥類
/**
* 拖拽效果主要邏輯
*/
private class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
int childIndex = 1;
if (changedView == frameView2) {
childIndex = 2;
}
// 一個view位置改變,另一個view的位置要跟進
onViewPosChanged(childIndex, top);
}
/**
* 返回值可以決定一個parentview中哪個子view可以拖動
*/
@Override
public boolean tryCaptureView(View arg0, int arg1) {
return true;
}
@Override
public int getViewVerticalDragRange(View child) {
return 1;
}
/**
* 滑動鬆開後
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
animTopOrBottom(releasedChild, yvel);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int finalTop = top;
// 第一個view被拖動
if (child == frameView1) {
if (top > 0) {
// 不讓第一個view網上拖,因為頂部會白板
finalTop = 0;
}
} else if (child == frameView2) {// 第二個view被拖動
if (top < 0) {
// 不讓第二個view往上拖動,因為底部會白板
finalTop = 0;
}
}
// finalTop代表的是理論上應該拖動到的位置。此處計算拖動的距離除以一個引數(3),
// 是讓滑動的速度變慢。數值越大,滑動的越慢
return child.getTop() + (finalTop - child.getTop()) / 3;
}
}
在上下拖動的時候我們還會去判斷是否是最上面一個View,如果是就不允許再繼續想上翻頁了。同樣如果是最下面的View,也不允許向下翻頁。同時為了美觀,這裡設定動畫過度效果。animTopOrBottom方法如下
/**
* 滑動時view位置改變協調處理
*
* @param viewIndex 滑動view的index(1或2)
* @param top 滑動View的top位置
*/
private void onViewPosChanged(int viewIndex, int top) {
if (viewIndex == 1) {
int offsetTopBottom = viewHeight + frameView1.getTop()
- frameView2.getTop();
frameView2.offsetTopAndBottom(offsetTopBottom);
} else if (viewIndex == 2) {
int offsetTopBottom = frameView2.getTop() - viewHeight
- frameView1.getTop();
frameView1.offsetTopAndBottom(offsetTopBottom);
}
}
/**
* 滑動鬆開後,需要向上或者向下粘到特定的位置
*
* @param releasedChild
* @param yvel
*/
public void animTopOrBottom(View releasedChild, float yvel) {
// 預設是最頂端
int finalTop = 0;
if (releasedChild == frameView1) {
// 拖動第一個view鬆手
if (yvel < -VEL_THRESHOLD
|| (downTop1 == 0 && frameView1.getTop() < -DISTANCE_THRESHOLD)) {
// 向上的速度足夠大,就滑動到頂端
// 向上滑動的距離超過某個值,就滑動到頂端
finalTop = -viewHeight;
// 下一頁可以初始化了
if (nextPageNotifier != null) {
nextPageNotifier.onDragNext();
}
}
} else {
// 拖動第二個view鬆手
if (yvel > VEL_THRESHOLD
|| (downTop1 == -viewHeight && releasedChild.getTop() > DISTANCE_THRESHOLD)) {
// 保持原地不懂
finalTop = viewHeight;
if (nextPageNotifier != null) {
nextPageNotifier.onDragLast();
}
}
}
if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
當然,在滑動結束後,即,ACTION_UP後,我們呼叫smoothSlideViewTo方法來判斷是否結束拖動,如果返回為true,則進行重置。這個時候,我們可以設定監聽上下View的回撥介面。可以在鬆開手後回撥方法內,設定其他事務。
public interface ShowNextPageNotifier {
public void onDragNext();
public void onDragLast();
}
當然了,我們的父控制元件同樣需要設定監聽,和重寫方法,
@Override
protected void onFinishInflate() {
// 初始化兩個view
frameView1 = getChildAt(0);
frameView2 = getChildAt(1);
}
@Override
public void computeScroll() {
//自動滾動停止時(continueSettling()裡檢測到滾動結束時)
if (mDragHelper.continueSettling(true)) {
//通知View進行重繪
ViewCompat.postInvalidateOnAnimation(this);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mDragHelper.continueSettling(true)) {
//正在動畫中的時候,不處理touch事件
return false;
}
boolean yScroll = gestureDetectorCompat.onTouchEvent(ev);
//是否應該攔截事件
boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
//action_down時就讓mDragHelper開始工作,否則有時候導致異常
mDragHelper.processTouchEvent(ev);
downTop1 = frameView1.getTop();
}
return yScroll && shouldIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 統一交給mDragHelper處理 由DragHelperCallback實現拖動效果
mDragHelper.processTouchEvent(event);
return true;
}
/**
* 設定下一頁監聽
**/
public void setNextPageListener(ShowNextPageNotifier nextPageNotifier) {
this.nextPageNotifier = nextPageNotifier;
}
上面註釋很詳細,大家,都應該看得懂。這裡還要強調一下的是,如果是在父控制元件裡面有DragHelper然後其子類容器控制元件也有DragHelper,快速滑動時有衝突的情況,這個時候最好在ACTION_DOWN的時候判斷一下,就可以避免了。
好了,自定義ViewGroup就完成了,是不是很簡單,當然通過這篇文章的學習,我們掌握了ViewDragHelper的用法,那麼什麼側滑,拖拽,之類的自定義控制元件就可以實現起來很輕鬆了 。最後將完整程式碼貼出來,供大家參考,大晚上的寫著也有點困了,謝謝!
/**
* viewGroup容器 實現上下兩個layout拖動切換
*/
public class DragLayout extends ViewGroup {
/**
* 拖動工具類
*/
private final ViewDragHelper mDragHelper;
/**
* 手勢處理,Y軸
**/
private GestureDetectorCompat gestureDetectorCompat;
/**
* 上下兩個frameLayout,在Activity中注入fragment
*/
private View frameView1, frameView2;
private int viewHeight;
/**
* 滑動速度的閾值,超過這個絕對值認為是上下
*/
private static final int VEL_THRESHOLD = 100;
/**
* 單位是畫素,當上下滑動速度不夠時,通過這個閾值來判定是應該粘到頂部還是底部
*/
private static final int DISTANCE_THRESHOLD = 100;
/**
* 手指按下,frameView1的gettop
*/
private int downTop1;
/**
* 手指鬆開是否載入一下一頁的 介面
*/
private ShowNextPageNotifier nextPageNotifier;
public DragLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragLayout(Context context) {
this(context, null);
}
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper
.create(this, 10f, new DragHelperCallback());
/**邊界攔截,全部**/
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
gestureDetectorCompat = new GestureDetectorCompat(context,
new YScrollDetector());
}
@Override
protected void onFinishInflate() {
// 初始化兩個view
frameView1 = getChildAt(0);
frameView2 = getChildAt(1);
}
@Override
public void computeScroll() {
//自動滾動停止時(continueSettling()裡檢測到滾動結束時)
if (mDragHelper.continueSettling(true)) {
//通知View進行重繪
ViewCompat.postInvalidateOnAnimation(this);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mDragHelper.continueSettling(true)) {
//正在動畫中的時候,不處理touch事件
return false;
}
boolean yScroll = gestureDetectorCompat.onTouchEvent(ev);
//是否應該攔截事件
boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
//action_down時就讓mDragHelper開始工作,否則有時候導致異常
mDragHelper.processTouchEvent(ev);
downTop1 = frameView1.getTop();
}
return yScroll && shouldIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 統一交給mDragHelper處理 由DragHelperCallback實現拖動效果
mDragHelper.processTouchEvent(event);
return true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (frameView1.getTop() == 0) {
frameView1.layout(l, 0, r, b - t);
frameView2.layout(l, 0, r, b - t);
viewHeight = frameView1.getMeasuredHeight();
//將fragmeView2向下移動距離
frameView2.offsetTopAndBottom(viewHeight);
} else {
// 如果已經被初始化,這次onLayout只需將之前的狀態存入即可
frameView1
.layout(l, frameView1.getTop(), r, frameView1.getBottom());
frameView2
.layout(l, frameView2.getTop(), r, frameView2.getBottom());
}
}
@SuppressLint("NewApi")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
//計算要將View的內邊距計算進去然後再計算整個寬度高度,
// 並且最後必須呼叫setMeasuredDimension方法設定寬度和高度,
// resolveSizeAndState() 方法將返回一個合適的尺寸,只要將測量模式和我們計算的寬度高度傳進去即可。
// 該方法在API11開始出現,低於該版本將無法使用該方法,
setMeasuredDimension(
resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
/**
* 這是View的方法,該方法不支援android低版本(2.2、2.3)的作業系統,所以手動複製過來以免強制退出
*/
public static int resolveSizeAndState(int size, int measureSpec,
int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
/**
* 設定下一頁監聽
**/
public void setNextPageListener(ShowNextPageNotifier nextPageNotifier) {
this.nextPageNotifier = nextPageNotifier;
}
/**
* 手勢監聽
*/
private class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (frameView2.getTop() <= 0 && distanceY > 0f) {
return false;
}
// 垂直滾動時dy>dx,才被認定是上下拖動
return Math.abs(distanceY) > Math.abs(distanceX);
}
}
/**
* 滑動時view位置改變協調處理
*
* @param viewIndex 滑動view的index(1或2)
* @param top 滑動View的top位置
*/
private void onViewPosChanged(int viewIndex, int top) {
if (viewIndex == 1) {
int offsetTopBottom = viewHeight + frameView1.getTop()
- frameView2.getTop();
frameView2.offsetTopAndBottom(offsetTopBottom);
} else if (viewIndex == 2) {
int offsetTopBottom = frameView2.getTop() - viewHeight
- frameView1.getTop();
frameView1.offsetTopAndBottom(offsetTopBottom);
}
}
/**
* 拖拽效果主要邏輯
*/
private class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
int childIndex = 1;
if (changedView == frameView2) {
childIndex = 2;
}
// 一個view位置改變,另一個view的位置要跟進
onViewPosChanged(childIndex, top);
}
/**
* 返回值可以決定一個parentview中哪個子view可以拖動
*/
@Override
public boolean tryCaptureView(View arg0, int arg1) {
return true;
}
@Override
public int getViewVerticalDragRange(View child) {
return 1;
}
/**
* 滑動鬆開後
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
animTopOrBottom(releasedChild, yvel);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int finalTop = top;
// 第一個view被拖動
if (child == frameView1) {
if (top > 0) {
// 不讓第一個view網上拖,因為頂部會白板
finalTop = 0;
}
} else if (child == frameView2) {// 第二個view被拖動
if (top < 0) {
// 不讓第二個view往上拖動,因為底部會白板
finalTop = 0;
}
}
// finalTop代表的是理論上應該拖動到的位置。此處計算拖動的距離除以一個引數(3),
// 是讓滑動的速度變慢。數值越大,滑動的越慢
return child.getTop() + (finalTop - child.getTop()) / 3;
}
}
/**
* 滑動鬆開後,需要向上或者向下粘到特定的位置
*
* @param releasedChild
* @param yvel
*/
public void animTopOrBottom(View releasedChild, float yvel) {
// 預設是最頂端
int finalTop = 0;
if (releasedChild == frameView1) {
// 拖動第一個view鬆手
if (yvel < -VEL_THRESHOLD
|| (downTop1 == 0 && frameView1.getTop() < -DISTANCE_THRESHOLD)) {
// 向上的速度足夠大,就滑動到頂端
// 向上滑動的距離超過某個值,就滑動到頂端
finalTop = -viewHeight;
// 下一頁可以初始化了
if (nextPageNotifier != null) {
nextPageNotifier.onDragNext();
}
}
} else {
// 拖動第二個view鬆手
if (yvel > VEL_THRESHOLD
|| (downTop1 == -viewHeight && releasedChild.getTop() > DISTANCE_THRESHOLD)) {
// 保持原地不懂
finalTop = viewHeight;
if (nextPageNotifier != null) {
nextPageNotifier.onDragLast();
}
}
}
if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
public void scrollChildView(int childIndex) {
if (childIndex == 0) {
scrollChildView(frameView1);
} else if (childIndex == 1) {
scrollChildView(frameView2);
}
}
public void scrollChildView(View view) {
if (nextPageNotifier != null) {
if (view == frameView1) {
nextPageNotifier.onDragLast();
} else if (view == frameView2) {
nextPageNotifier.onDragNext();
}
}
if (mDragHelper.smoothSlideViewTo(view, 0, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
public interface ShowNextPageNotifier {
public void onDragNext();
public void onDragLast();
}
}