1. 程式人生 > >自定義View解決滑動衝突

自定義View解決滑動衝突

最近在讀Android開發藝術探索,本文作為自己對view的滑動衝突的理解和實踐記錄

而滑動衝突,需要了解Android的事件分發機制,如果這個還有些疑惑的地方,請參考這篇文章,以及其中的參考文章

還需要View的Measure和Layout的相關知識View的Measure流程總結

自定義view注意

1.如果直接繼承view,此時wrap_content和使用match_parent效果一樣.需要在onMeasure()中處理AT_MOST條件,處理wrap_content.

2.margin要在onLayout中設定,padding需要在 onDraw中設定

3.重新整理回撥,停止縣城或者動畫 在view.onDetachedFromWindow

4.在dispatchTouchEventTouchEvent中處理好滑動事件.

滑動衝突的種類

場景一:外部和內部倆層滑動方向不一致

場景二:外部和內部倆層滑動方向一致

場景三:主要是針對場景一和二的巢狀

滑動處理千篇一律, 只要你找到什麼時候父控制元件滑動,什麼時候子空間滑動.然後再父佈局中,選擇 是否自己處理onInterceptTouchEvent(),就好了.

Android開發藝術探索中有兩種方式,分別為外部攔截髮,和內部攔截法.我上面說的是外部攔截法(感覺這個好用些).
具體的內容,大家可以看 Android開發藝術探索第三章第五節相關內容.

情形1的處理

先上效果圖
scollview_gif_1.gif

下面是自定義的view,解決了上述情景1的問題.


/**
    自定義滑動viewPager
 * Created by chenchangjun on 17/7/14.
 */

public class HorizontalScrollView extends ViewGroup {


    private static final String TAG = HorizontalScrollView.class.getSimpleName();


    private int mChildWidth = 1;
    private int mChildIndex = 1
; private int mLastX; private int mLastY; private int mLastXIntercept = 0; private int mLastYIntercept = 0; /** * Scroller只是個計算器,處理滑動效果的,例如ViewPager,listview等的內部類 */ private Scroller mScroller; /** * 速度獲取器 */ private VelocityTracker mVelocityTracker; private int mChildrenCount; private void init() { mScroller = new Scroller(getContext()); mVelocityTracker = VelocityTracker.obtain(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept = false; int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: intercept = false; if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; if (Math.abs(deltaX) > Math.abs(deltaY)) { intercept = true; } else { intercept = false; } break; case MotionEvent.ACTION_UP: intercept = false; break; default: break; } Log.d(TAG, "intercept=" + intercept); mLastX = x; mLastY = y; mLastXIntercept = x; mLastYIntercept = y; return intercept; } @Override public boolean onTouchEvent(MotionEvent event) { mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; scrollBy(-deltaX, 0);//視覺上向右滑動,相對的,view橫向向左移動. break; case MotionEvent.ACTION_UP: int scrollX = getScrollX(); // int srcollToChildIndex=scrollX/mChildWidth; mVelocityTracker.computeCurrentVelocity(1000); float xVelocity = mVelocityTracker.getXVelocity(); if (Math.abs(xVelocity) >= 30) { //當一秒滑動畫素大於30畫素的時候, mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;//計算pager下標mChildIndex.如果手指從右向左,則xVelocity為負,mChildIndex+1;反之,易然. } else { mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth; } int dex=0; /* if (mChildIndex >= getChildCount()) { mChildIndex = 0; } else if (mChildIndex < 0) { mChildIndex = getChildCount() - 1; } else { dex = mChildIndex * mChildWidth - scrollX; }*/ mChildIndex=Math.max(0,Math.min(mChildIndex,mChildrenCount-1)); dex = mChildIndex * mChildWidth - scrollX; smoothScrollBy(dex, 0); mVelocityTracker.clear(); break; default: break; } mLastX = x; mLastY = y; return true; } private void smoothScrollBy(int dx, int dy) { mScroller.startScroll(getScrollX(), 0, dx, 0, 500); invalidate(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childLeft = 0; final int childCount = getChildCount(); mChildrenCount = childCount; for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { mChildWidth = child.getMeasuredWidth(); child.layout(childLeft, 0, childLeft + child.getMeasuredWidth(), child.getMeasuredHeight()); childLeft += mChildWidth; } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = 0; int measuredHeight = 0; final int childCount = getChildCount(); measureChildren(widthMeasureSpec, heightMeasureSpec); int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (childCount == 0) { setMeasuredDimension(0, 0); } else if (heightSpecMode == MeasureSpec.AT_MOST && widthSpecMode == MeasureSpec.AT_MOST) { final View childView = getChildAt(0);//因為這裡的child都是同類,所以偷懶~取第一個測量尺寸就夠啦 measuredWidth = childView.getMeasuredWidth() * childCount; measuredHeight = childView.getMeasuredHeight() * childCount; setMeasuredDimension(measuredWidth, measuredHeight); } else if (heightSpecMode == MeasureSpec.AT_MOST) { final View childView = getChildAt(0);//因為這裡的child都是同類,所以偷懶~取第一個測量尺寸就夠啦 measuredHeight = childView.getMeasuredHeight() * childCount; setMeasuredDimension(widthSpaceSize, measuredHeight); } else if (widthSpecMode == MeasureSpec.AT_MOST) { final View childView = getChildAt(0);//因為這裡的child都是同類,所以偷懶~取第一個測量尺寸就夠啦 measuredWidth = childView.getMeasuredWidth() * childCount; setMeasuredDimension(measuredWidth, heightSpaceSize); } else { setMeasuredDimension(widthSpaceSize, heightSpaceSize); } } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } @Override protected void onDetachedFromWindow() { mVelocityTracker.recycle(); super.onDetachedFromWindow(); } public HorizontalScrollView(Context context) { super(context); init(); } public HorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public HorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public HorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } }

總結

對於第一種,思路是在ACTION_MOVE的時候,判斷x偏移量是否大於y的偏移量.如果大於,就page++,.

對於第二種,需要判斷子view是否滑動到了頂部,或者底部,如果是,讓父控制元件滑動即可.

對於第三種,需要結合第一種,和第二種進行判斷.

在判斷滑動衝突的過程中,重點放在 InterceptTouchEvent()中,還有TouchEvent種進行處理.