Android-从重叠view响应问题到安卓事件分发机制

2019-01-02  本文已影响0人  九号锅炉

重叠view事件响应问题

工作当中遇到一个需求:有两个重叠并且全屏的Framelayout,交互逻辑是点击上层的button使得上层消失,下层可见;再点击下层使得下层消失上层出现。但是发现当点击上层非button区域时,下层也会响应点击事件,但是点击button区域则不会。这意味着点击事件穿过上层到达下层。对于安卓事件机制一知半解的我无从下手,上网搜了一下发现在上层FrameLayout下增加:

android:clickable="true"

这样的确可以保证上层点击事件不会到达下层,可是这是为什么呢?最初步的猜想是当上层FrameLayout的clickable属性为true时,点击事件已经被上层拦截了。这个和当你点击button时响应onclick()时的原理是不是一样?这就需要从源码分析了。

安卓事件分发机制基础

分析源码前先大致了解一下安卓事件分发机制。

/**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        ...
        boolean result = false;
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ...
        return result;
    }
public boolean onTouchEvent(MotionEvent event) {
   ...
    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
                if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
                    // take focus if we don't have it already and we should in
                    // touch mode.
                    boolean focusTaken = false;
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }
                    if (!mHasPerformedLongPress) {
                        // This is a tap, so remove the longpress check
                        removeLongPressCallback();
                        // Only perform take click actions if we were in the pressed state
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }
   ..
}

结论

综合上述对安卓事件分发的源码分析知道,当在上层FrameLayout中将clickable设置为true时,其实就是
当点击事件到达上层framelayout时,在onTouchEvent中对事件进行拦截,使得点击事件不会继续向下传递。当然,除了这个方法以外,也可以重写FrameLayout的onInterceptTouchEvent,在将这个函数的返回值为true,也可以实现拦截事件的作用。

上一篇下一篇

猜你喜欢

热点阅读