View.java

1.事件分发
优先级:onTouch > onTouchEvent > OnLongClickListener>OnClickListener

  public boolean dispatchTouchEvent(MotionEvent event) {
            //先调用onTouch
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
           // 再调用onTouchEvent
            if (!result && onTouchEvent(event)) {
                result = true;
            }
  }

public boolean onTouchEvent(MotionEvent event) {
switch(action){
 case MotionEvent.ACTION_UP:
  // 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)) {
                                    //调用onClick方法
                                    performClickInternal();
                                }
                            }
   case MotionEvent.ACTION_DOWN:
            if (!clickable) {
                        //处理长按事件
                        checkForLongClick(0, x, y);
                        break;
                    }
case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
                        drawableHotspotChanged(x, y);
                    }

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Remove any future long press/tap checks
                        removeTapCallback();
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            setPressed(false);
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }
                    break;
}
}

ACTION_CANCEL 触发时机:
子控件接收到down或move后,被父控件在 onInterceptTouchEvent 根据move位置进行拦截,将事件转换成cancel传给子控件 ,以至于子控件接受不到后续Up事件.

 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

不过子View可以通过设置requestDisallowInterceptTouchEvent(true)来达到禁止父ViewGroup拦截事件的目的

  1. post方法为什么绘制后执行
public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

/**
     * Returns the queue of runnable for this view.
     *
     * @return the queue of runnables for this view
     */
    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }
//dispatchAttachedToWindow中调用mRunQueue执行之前缓存的消息
 void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
         ....
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
}

//dispatchAttachedToWindow调用时机
//ViewGroup.java  
// Android 26.0
public void addView(View child, int index, LayoutParams params) {
    ...
        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

 private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {
    ....
 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
}

3.View的三个构造函数调用时机:

public RelativeLayout(Context context) {
       this(context, null);
   }

   public RelativeLayout(Context context, AttributeSet attrs) {
       this(context, attrs, 0);
   }

   public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
       this(context, attrs, defStyleAttr, 0);
   }

代码动态创建对象调用函数1,xml中创建对象调用函数2;第三个函数系统是不调用的,要由View(我们自定义的或系统预定义的View)显式调用;

  1. MeasureSpec : MeasureSpec.getMode+MeasureSpec.getSize
    如果不考虑父 view 的影响,测试结果如下:
  • wrap_parent -> MeasureSpec.AT_MOST
  • match_parent -> MeasureSpec.EXACTLY
  • 具体值 -> MeasureSpec.EXACTLY

实际情况:

  • childMeasureSpec = parentMeasureSpec + lp.height

MeasureSpec.UNSPECIFIED:ScrollView的子View ,不受父View限制。

你可能感兴趣的:(View.java)