1. 程式人生 > >Android 滑動,攔截事件處理

Android 滑動,攔截事件處理

之前一片文章 初略的講了一些關於事件傳遞的基本內容,現在這片部落格, 主要是具體去運用事件傳遞攔截的相關內容, ok 具體要實現的目標就是,  view 能夠正常的吃掉點選事件, 但是如果是滑動事件時, 則是父View 來處理來滑動另一個View 基本原理就是  在View group中得onInterceptTouchEvent 方法中判斷是否是滑動, 如果是滑動,那麼就返回true 自身去消費滑動事件, 還需要注意的是, 在處理滑動事件的時候需要注意 兩個手指滑動的情況 即 另一個手指按下也能夠繼續滑動,

不多說了上程式碼: 

/**
 * 只處理 縱向滑動事件, 其他事件不做 處理
 */
public class CustomScrollViewGroup extends RelativeLayout {

    public static final String TAG = "CustomScrollViewGroup";

    public CustomScrollViewGroup(Context context) {
        super(context);
        init();
    }

    public CustomScrollViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private static final int MIN_DISTANCE_FOR_FLING = 25; // dips

    private OnScrollListener mOnScrollListener;

    private int mTouchSlop;
    protected VelocityTracker mVelocityTracker;
    private int mMinimumVelocity;
    private int mMaximumVelocity;

    /**
     * Position of the last motion event.
     */
    private float mLastMotionX;
    private float mLastMotionY;

    private float mInitialMotionY, mInitialMotionX;

    protected int mActivePointerId = INVALID_POINTER;
    private static final int INVALID_POINTER = -1;

    private boolean mIsUnableToDrag = false;

    private int mFlingDistance;

    private boolean mIsBeingDragged = false;
    boolean mScrollToEnd = false;//標記View是否已經完全消失,OvershootInterpolator回彈時間忽略
    private boolean mQuickReturn = false;

    boolean isTouchOnRecycleView = false;

    private void init() {

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();

        final float density = getContext().getResources().getDisplayMetrics().density;
        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        LogUtil.d(TAG, "onInterceptTouchEvent ev = " + ev.getAction());
        final int action = ev.getAction();

        if (action == MotionEvent.ACTION_CANCEL
                || action == MotionEvent.ACTION_UP
                || (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {
            endDrag();
            return false;
        }

        switch (action & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                // Remember where the motion event started
                int index = MotionEventCompat.getActionIndex(ev);
                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
                if (mActivePointerId == INVALID_POINTER)
                    break;
                mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
                mInitialMotionY = mLastMotionY = MotionEventCompat.getY(ev, index);

                mIsBeingDragged = false;
                mIsUnableToDrag = false;
                break;

            case MotionEvent.ACTION_MOVE:
                final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
                if (mActivePointerId == INVALID_POINTER) {
                    break;
                }
                final float currentY = MotionEventCompat.getY(ev, activePointerIndex);
                final float deltaY = mLastMotionY - currentY;
                determineDrag(ev);
                break;
        }

        if (!mIsBeingDragged) {
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);
        }
        return mIsBeingDragged || mQuickReturn;
    }

    private int getPointerIndex(MotionEvent ev, int id) {
        int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);
        if (activePointerIndex == -1)
            mActivePointerId = INVALID_POINTER;
        return activePointerIndex;
    }

    private int mScrollY = 0;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        LogUtil.d(TAG, "onTouchEvent ev = " + ev.getAction());
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);
        final int action = ev.getAction();

        switch (action & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                // Remember where the motion event started
                int index = MotionEventCompat.getActionIndex(ev);
                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
                mLastMotionY = mInitialMotionY = ev.getY();
                mLastMotionX = mInitialMotionX = ev.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!mIsBeingDragged) {
                    determineDrag(ev);

                    if (mIsUnableToDrag)
                        return false;
                }

                if (mIsBeingDragged) {
                    final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
                    if (mActivePointerId == INVALID_POINTER)
                        break;
                    final float y = MotionEventCompat.getY(ev, activePointerIndex);
                    final float deltaY = mLastMotionY - y;
                    mLastMotionY = y;
                    float oldScrollY = mScrollY;
                    float scrollY = oldScrollY + deltaY;
                    mLastMotionX += scrollY - (int) scrollY;
                    mScrollY = (int) scrollY;

                    if (mOnScrollListener != null) {
                        mOnScrollListener.onScroll(0, deltaY);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker, mActivePointerId);
                    final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
                    if (mActivePointerId != INVALID_POINTER) {
                        final float y = MotionEventCompat.getY(ev, activePointerIndex);
                        final int totalDelta = (int) (y - mInitialMotionY);// doww -> up detal
                        if (Math.abs(totalDelta) > mFlingDistance && Math.abs(initialVelocity) > mMinimumVelocity) {
                            if (initialVelocity > 0 && totalDelta > 0) {
                                // 向下滑
                                if (mOnScrollListener != null) {
                                    mOnScrollListener.onFling(false);
                                }
                            } else if (initialVelocity < 0 && totalDelta < 0) {
                                // 向上滑
                                if (mOnScrollListener != null) {
                                    mOnScrollListener.onFling(true);
                                }
                            } else {
                                if (mOnScrollListener != null) {
                                    mOnScrollListener.onScrollOver();
                                }
                            }
                        } else {
                            if (mOnScrollListener != null) {
                                mOnScrollListener.onScrollOver();
                            }
                            // 不是 fling 最後是 劃上 還是劃下  外面判斷
                        }

                    }
                    mActivePointerId = INVALID_POINTER;
                    endDrag();
                }
                break;
            case MotionEventCompat.ACTION_POINTER_DOWN: {
                final int indexx = MotionEventCompat.getActionIndex(ev);
                mLastMotionY = MotionEventCompat.getY(ev, indexx);
                mLastMotionX = MotionEventCompat.getX(ev, indexx);
                mActivePointerId = MotionEventCompat.getPointerId(ev, indexx);
                break;
            }
            case MotionEventCompat.ACTION_POINTER_UP:
                int pointerIndex = getPointerIndex(ev, mActivePointerId);
                if (mActivePointerId == INVALID_POINTER)
                    break;
                mLastMotionY = MotionEventCompat.getY(ev, pointerIndex);
                mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);
                break;
        }

        return true;
    }

    private void determineDrag(MotionEvent ev) {
        final int activePointerId = mActivePointerId;
        final int pointerIndex = getPointerIndex(ev, activePointerId);
        if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)
            return;
        final float x = MotionEventCompat.getX(ev, pointerIndex);
        final float dx = x - mLastMotionX;
        final float xDiff = Math.abs(dx);
        final float y = MotionEventCompat.getY(ev, pointerIndex);
        final float dy = y - mLastMotionY;
        final float yDiff = Math.abs(dy);
        if (yDiff > mTouchSlop && yDiff > xDiff) {
            startDrag();
            mLastMotionX = x;
            mLastMotionY = y;
        } else if (xDiff > mTouchSlop) {
            mIsUnableToDrag = true;
        }
    }

    // 開始滑動
    private void startDrag() {
        mIsBeingDragged = true;
        mQuickReturn = false;
        mScrollToEnd = false;
    }

    private void endDrag() {
        mQuickReturn = false;
        mIsBeingDragged = false;
        mIsUnableToDrag = false;
        mActivePointerId = INVALID_POINTER;

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    public void setOnScrollListener(OnScrollListener onScrollListener) {
        mOnScrollListener = onScrollListener;
    }

    public interface OnScrollListener {
        void onScroll(float distanceX, float distanceY);

        void onFling(boolean isFlingUp);

        void onScrollOver();

    }
}
上面的是最主要的ViewGroup  下面在來看看 activity 中得程式碼 
public class MainActivity extends AppCompatActivity {
    public static final String TAG = "MainActivity";

    Button mButton;
    View mView;
    CustomScrollViewGroup mCustomScrollViewGroup;

    int maxTranY;

    float currentTransY = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.btn);
        mView = findViewById(R.id.scrolledView);
        mCustomScrollViewGroup = (CustomScrollViewGroup) findViewById(R.id.customscrollviewgroup);

        maxTranY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, getResources().getDisplayMetrics());

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "click btn", Toast.LENGTH_SHORT).show();
            }
        });

        mCustomScrollViewGroup.setOnScrollListener(new CustomScrollViewGroup.OnScrollListener() {
            /**
             * 滑動 的箭頭
             * @param distanceX 向左滑動 為正
             * @param distanceY 向上滑動 為正
             */
            @Override
            public void onScroll(float distanceX, float distanceY) {
                if (isAnimating) {
                    return;
                }
                currentTransY = currentTransY - distanceY;
                if (currentTransY < -maxTranY) {
                    currentTransY = -maxTranY;
                }

                if (currentTransY > 0) {
                    currentTransY = 0;
                }
                mView.setTranslationY(currentTransY);
                LogUtil.d(TAG, "onScroll distanceX = " + distanceX + " , distanceY = " + distanceY + ", currentTransY = " + currentTransY);
            }

            /**
             * 快速滑動的回撥
             * @param isFlingUp 是否是向上快速滑動
             */
            @Override
            public void onFling(boolean isFlingUp) {
                if (isAnimating) {
                    return;
                }
                LogUtil.d(TAG, "onFling isFlingUp = " + isFlingUp);
                if (isFlingUp) {
                    animatorScrollUp();
                } else {
                    animatorScrollDowm();
                }
            }

            /**
             * 滑動結束的監聽
             */
            @Override
            public void onScrollOver() {
                if (isAnimating) {
                    return;
                }
                LogUtil.d(TAG, "onScrollOver");
                if (currentTransY < -maxTranY / 2) {// 向上滑超過了 1/2
                    animatorScrollUp();
                } else {
                    animatorScrollDowm();
                }
            }
        });

    }

    boolean isAnimating = false;

    private void animatorScrollUp() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mView, "TranslationY", mView.getTranslationY(), -maxTranY);
        objectAnimator.setDuration(300);
        isAnimating = true;
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                currentTransY = -maxTranY;
                isAnimating = false;
            }
        });
        objectAnimator.start();
    }

    private void animatorScrollDowm() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mView, "TranslationY", mView.getTranslationY(), 0);
        objectAnimator.setDuration(300);
        isAnimating = true;
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                currentTransY = 0;
                isAnimating = false;
            }
        });
        objectAnimator.start();
    }
}




相關推薦

Android 滑動,攔截事件處理

之前一片文章 初略的講了一些關於事件傳遞的基本內容,現在這片部落格, 主要是具體去運用事件傳遞攔截的相關內容, ok 具體要實現的目標就是,  view 能夠正常的吃掉點選事件, 但是如果是滑動事件時, 則是父View 來處理來滑動另一個View 基本原理就是  在Vi

可能是最詳細的Android點選事件處理詳解(三)

前兩篇文章: 可能是最詳細的Android點選事件處理詳解 可能是最詳細的Android點選事件處理詳解(二) 這裡再次延伸一下,在ScrollView和RecyclerView巢狀中touch事件的傳遞過程,以及巢狀滑動衝突的問題。 如上圖,外層是一個Neste

可能是最詳細的Android點選事件處理詳解(二)

上一篇我們主要詳細描述了touch事件在各層的傳遞 本篇文章主要是對比touch在不可滾動和可滾動的ViewGroup事件的傳遞過程 如上圖: - 左圖:是ViewGroup巢狀View,不可滑動 - 右圖:也是ViewGroup(RecyclerView)巢

可能是最詳細的Android點選事件處理詳解

面試的時候,很多時候都會問到Touch事件的傳遞,而且問法角度都有所不同,但是還是會遵循基本的事件傳遞規則的,可能他問的你沒處理過,但是根據基本規則慢慢思考來回答,都不會錯。 一,簡介 首先我們知道touch事件 主要是是在三個方法中傳遞和處理的。分別是:

結合原始碼,重溫 Android View 的事件處理知多少 ?

前言 Android View 的 事件處理在我們的程式設計中,可謂是無處不在了。但對於大多數人而言,一直都是簡單的使用,對其原理缺乏深入地認識。 學 Android 有一段時間了,最近發現,很多基礎知識開始有些遺忘了,所以從新複習了 View 的事件分發。特地整理成了這篇文章分享給大家。 本文不難,可以作

Android事件傳遞、多點觸控及滑動衝突的處理

基本概念 所有Touch事件都會被封裝MotionEvent, 包括Touch的型別、位置(相對螢幕的絕對位置,相對View的相對位置)、時間、歷史記錄以及第幾個手指(多點觸控)等; 事件有多種型別,常用的事件型別有:ACTION_DOWN,ACTION_UP,ACTION_MOVE,ACTION

Android觸控事件的分發、攔截處理

 在Android觸控式螢幕的過程中,有三個重要的方法,dispatchTouchEvent(事件分發)、onInterceptTouchEvent(事件攔截)、onTouchEvent(事件處理、消

Android開發知識(七):Android事件處理機制:事件分發、傳遞、攔截處理機制的原理分析(上)

  在我們剛開始學習安卓的時候,總會一開始就接觸到Button,也就是對按鈕進行一個事件監聽的事件,當我們點選螢幕上的按鈕時就可以觸發一個點選事件。那麼,從我們點選螢幕到按鈕觸發事件這個過程,是什麼樣子的呢?本文我們就來談一下關於事件攔截處理機制的基本知識。

android 開發 View _14 MotionEvent和事件處理詳解,與實踐自定義滑動條View

MotionEvent MotionEvent物件是與使用者觸控相關的時間序列,該序列從使用者首次觸控式螢幕幕開始,經歷手指在螢幕表面的任何移動,直到手指離開螢幕時結束。手指的初次觸控(ACTION_DOWN操作),滑動(ACTION_MOVE操作)和擡起(ACTION

Android開發知識(八):Android事件處理機制:事件分發、傳遞、攔截處理機制的原理分析(中)

  在本章節中,我們重點談論一下onTouch、onClick、onLongClick三個方法被回撥的過程。   在上一篇文章中,我們談到關於為View新增一個點選事件SetOnClickListener後,就可以通過回撥onClick方法來實現事件的響應

android 滑動事件衝突解決 Touch事件處理機制

android中的事件型別分為按鍵事件和螢幕觸控事件,Touch事件是螢幕觸控事件的基礎事件,有必要對它進行深入的瞭解。 一個最簡單的螢幕觸控動作觸發了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->AC

android onTouchEvent 左右手勢滑動事件處理

要實現手指在螢幕上左右滑動的事件需要例項化物件GestureDetector,new GestureDetector(MainActivity.this,onGestureListener);首先實現監聽物件GestureDetector.OnGestureListener

一文讀懂 Android TouchEvent 事件分發、攔截處理過程

什麼是事件?事件是使用者觸控手機螢幕,引起的一系列TouchEvent,包括ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL等,這些action組合後變成點選事件、長按事件等。 在這篇文章中,用打Log測試的方法來了解Android TouchEvent 事件分發

Android — 長按ListView 利用上下文菜單(ActionMode) 進行批量事件處理

改變 out thread miss static server ann break 我們 好久沒寫博客拉``````` 近期最終略微閑一點了``````` 無聊拿手機清理短信。發現批量事件的處理還是挺管用的`````` 那麽自己也來山寨一記看看效果吧````

Android學習筆記(36):Android的兩種事件處理方式

post gravity cal log 基於 處理方法 hang mil 重寫 Android提供了兩種事件處理的方式:基於回調的事件處理 和 基於監聽的事件處理。 我們來說的easy理解一點: (1)基於回調的事件處理就是繼承GUI組件,並重寫該組件的

Android零基礎入門第66節:RecyclerView點擊事件處理

系統 ava oid 文章 click事件 需要 spin 圖像 line 前面兩期學習了RecyclerView的簡單使用,並為其item添加了分割線。在實際運用中,無論是List還是Grid效果,基本都會伴隨著一些點擊操作,那麽本期就來一起學習RecyclerVi

Android 自定義組件 事件處理

items int ems set rect 獲取 控件 pan highlight 以點擊事件為例: 覆寫方法: public boolean dispatchTouchEvent(MotionEvent event); 可以從MotionEvent 獲取事件坐標,

Android熱插拔事件處理流程--Vold

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android開發指南-使用者介面-事件處理

處理使用者介面事件 Handling UI Events 在 Android 上,不止一個途徑來偵聽使用者和應用程式之間互動的事件。對於使用者介面裡的事件,偵聽方法就是從與使用者互動的特定檢視物件截獲這些事件。檢視類提供了相應的手段。 在各種用來組建佈局的檢視類裡面,你可能會

Android完美處理RecyclerView實現item條目點選事件處理

前言: RecyclerView並沒有像ListView那樣提供了OnItemClick,OnItemLongClick等事件回撥介面,所以,我們需要自己寫介面去進行實現。 正題 ①、建立介面類OnRecyclerViewClickListener /** * Recy