Android怎么自界说一个view ViewRootImpl制作流程

1、自界说特点

在res/values/attrs.xml文件里界说一个declare-styleable name:自界说view的姓名

<resources>
   <declare-styleable name="CustomView">
     <attr name="background" format="color"/> //界说特点:称号 + 格局类型---色彩、尺度、整形、字符串类型
     <attr name="size" format="dimension"/>
     <attr name="AttrFirst" format="integer"/>
     <attr name="AttrSecond" format="string"/>
   </declare-styleable>
</resources>12345678

2、在res/layout/activity_main.xml文件里运用自界说view

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools" 
   xmlns:custom="http://schemas.android.com/apk/res-auto" // 自界说的命名空间
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   tools:context="com.example.custom.MainActivity">
   <com.example.custom.CustomView
     android:id="@+id/custom_view"
     android:layout_width="100dp"
     android:layout_height="100dp"
     custom:background="@color/colorPrimary"
     custom:size="20dp"
     custom:AttrFirst="1"
     custom:AttrSecond="CustomView"/>
</LinearLayout>123456789101112131415161718

3、构造CustomView称号的view,读取自界说特点

//经过AttributeSet直接获取

public CustomView(Context context, AttributeSet attrs) {
   super(context, attrs);
   int n = attrs.getAttributeCount(); //取得自界说view的特点数量
   for (int i =0; i < n; i++) {
    Log.d("CustomView", "attrs No " + i + ", name: " + attrs.getAttributeName(i) + ", value: " + attrs.getAttributeValue(i));
   }
   int custon_background = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto", "background", Color.BLUE);
   int custom_size = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto", "size", 10);
   int AttrFirst = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto", "AttrFirst", 100));
   String AttrSecond = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "AttrSecond"));
}1234567891011

//getAttributeName来获取对应index处的特点称号,以sting回来。 //getAttributeValue来获取相应index处特点的值,以string回来。 //getAttributeIntValue来获取相应attribute特点的值 public String getAttributeValue(String namespace, String attribute); public int getAttributeIntValue(String namespace, String attribute, int defaultValue); namespace代表自界说特点的命名空间(与xml中的运用办法相同)

public class CustomView extends View {
   private Paint mPaint;
   private int mHeight;
   private int mWidth;
   private int custom_size;
   private float scale = 1f;
   private final int SIZE = 30;
​
   public CustomView(Context context) {
     this(context, null);
   }
​
   public CustomView(Context context, AttributeSet attrs) {
     this(context, attrs, 0);
   }
​
   public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, R.style.AppTheme); //经过obtainStyledAttributes()办法获取特点值
​
     int custon_background = a.getColor(R.styleable.CustomView_background, Color.BLUE); 
     custom_size = a.getDimensionPixelSize(R.styleable.CustomView_size, 10);
     int AttrFirst = arr.getInt(R.styleable.CustomView_AttrFirst, 100);
     String AttrSecond = arr.getString(R.styleable.CustomView_AttrSecond);
​
     a.recycle();mPaint = new Paint();
     mPaint.setColor(custom_background);
     mPaint.setAntiAlias(true);
     mPaint.setStyle(Paint.Style.FILL);
   }
​
   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //核算出实践的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()办法完结单个View的丈量
     int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     int widthSize = MeasureSpec.getSize(widthMeasureSpec);
     int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     int heightSize = MeasureSpec.getSize(heightMeasureSpec);
​
     int measuredHeight, measuredWidth;
     if (widthMode == MeasureSpec.EXACTLY) {
       measuredWidth = widthSize;
     } else {
       measuredWidth = SIZE;
     }
     if (heightMode == MeasureSpec.EXACTLY) {
       measuredHeight = heightSize;
     } else {
       measuredHeight = SIZE;
     }
     setMeasuredDimension(measuredWidth, measuredHeight);
   }
​
   @Override
   protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //onLayout决议详细View的巨细和方位, 对当前视图和其一切子View设置它们在父视图中详细方位
     super.onLayout(changed, left, top, right, bottom);
     mHeight = getHeight(); //getWidth,和getLeft等这些函数都是View相关于其父View的方位。而getMeasuredWidth,getMeasuredHeight是丈量后该View的实践值
     mWidth = getWidth();
   }
​
   @Override
   protected void onDraw(Canvas canvas) {
     canvas.drawCircle(mWidth/2, mHeight/2, custom_size * scale, mPaint);
   }
​
   private ValueAnimator mAnimator;
​
   public void startAnimation() {
     mAnimator = ValueAnimator.ofFloat(1, 2);
     mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
       @Override
       public void onAnimationUpdate(ValueAnimator animation) {
         scale = (float) animation.getAnimatedValue();
         postInvalidate(); 
       }
     });
     mAnimator.setRepeatCount(-1); //重复次数,-1表明无限循环  
     mAnimator.setRepeatMode(ValueAnimator.REVERSE); //重复形式:RESTART---从头开端,REVERSE---恢复初始状况再开端
     mAnimator.start();
   }
​
   @Override
   protected void onAttachedToWindow() {
     super.onAttachedToWindow();
   }
​
   @Override
   protected void onDetachedFromWindow() {
     super.onDetachedFromWindow();
     mAnimator.end();  //关闭动画
   }
​
   @Override
   protected Parcelable onSaveInstanceState() {
     return super.onSaveInstanceState();
   }
​
   @Override
   protected void onRestoreInstanceState(Parcelable state) {
     super.onRestoreInstanceState(state);
   }
}  123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103

4、自界说view应用特点、刷新显现

Android里,一个view的制作流程包含: Measure、Layout、Draw 丈量——onMeasure():决议View的巨细,确认view及它一切子节点需求的尺度 布局——onLayout():决议View在ViewGroup中的方位, 当view需求为它的一切子节点指定巨细和布局时,调用此办法 制作——onDraw():制作View。 自界说View中,onLayout配合onMeasure办法一起运用,能够完成自界说View的杂乱布局。

自界说view,刷新view的办法: requestLayout()、invalidate()、postInvalidate(), 其实invalidate和postInvalidate这两个办法作用是一样的,仅有不同: invalidate用在主线程 postInvalidate用在异步线程,它最后会经过handler调用invalidate完成

requestLayout和invalidate的内部完成: requestLayout 会调用measure和layout 等一系列操作,然后根据布局是否发生改动,surface是否被毁掉,来决议是否调用draw,也就是说requestlayout肯定会调用measure和layout, 但不一定调用draw,读者能够试着改下我上面写的那个小程序,将postInvalidate改成requestlayout,动画作用就消失了,因为布局没有发生改动。 invalidate 只会调用draw,并且肯定会调,即便什么都没有发生改动,它也会从头制作。 所以假如有布局需求发生改动,需求调用requestlayout办法,假如仅仅刷新动画,则只需求调用invalidate办法

Android如何自定义一个view ViewRootImpl绘制流程
postInvalidate()办法程序调用流程,从View.java到ViewRootImpl.java,终究由performTraversals()完成 postInvalidate => postInvalidateDelayed => dispatchInvalidateDelayed => invalidate => scheduleTraversals() => doTraversal => performTraversals

requestLayout()办法程序调用流程 requestLayout() => scheduleTraversals() => doTraversal => performTraversals

Android如何自定义一个view ViewRootImpl绘制流程
performTraversals()办法调用:(真正完成view的measure、layout、draw) performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performMeasure() => measure() => onMeasure() 调用该办法来确认view及它一切子节点需求的尺度 performLayout(lp, desiredWindowWidth, desiredWindowHeight); performLayout() => layout() => onLayout() 当view需求为它的一切子节点指定巨细和布局时,调用此办法 performDraw(); performDraw() => draw() => onDraw() => dispatchDraw() (drawSoftware())

onMeasure(int widthMeasureSpec, int heightMeasureSpec) 功能:经过丈量知道一个view要占的巨细, 参数:宽高丈量标准,int型的值(java中int型由4个字节(32bit)组成),在MeasureSpce中,其前两位表明mode,后30位表明size MeasureSpce的mode有三种:EXACTLY、AT_MOST、UNSPECIFIED 1、当父布局是EXACTLY时, 子控件确认巨细或者match_parent,mode都是EXACTLY, 子控件是wrap_content时,mode为AT_MOST; 2、当父布局是AT_MOST时, 子控件确认巨细,mode为EXACTLY, 子控件wrap_content或者match_parent时,mode为AT_MOST。 所以在确认控件巨细时,需求判断MeasureSpec的mode,不能直接用MeasureSpec的size

View.java:

public class View implements Drawable.Callback, KeyEvent.Callback,
     AccessibilityEventSource {
   ...
​
   public void requestLayout() {
     if (mMeasureCache != null) mMeasureCache.clear();
​
     if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
       // Only trigger request-during-layout logic if this is the view requesting it,
       // not the views in its parent hierarchy
       ViewRootImpl viewRoot = getViewRootImpl();
       if (viewRoot != null && viewRoot.isInLayout()) {
         if (!viewRoot.requestLayoutDuringLayout(this)) {
           return;
         }
       }
       mAttachInfo.mViewRequestingLayout = this;
     }
​
     mPrivateFlags |= PFLAG_FORCE_LAYOUT;
     mPrivateFlags |= PFLAG_INVALIDATED;
​
     if (mParent != null && !mParent.isLayoutRequested()) {
       mParent.requestLayout(); //ViewParent接口的requestLayout()办法
     }
     if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
       mAttachInfo.mViewRequestingLayout = null;
     }
   }
​
   public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //为整个View树核算实践的巨细, 需求递归的去核算每一个子视图的巨细
     boolean optical = isLayoutModeOptical(this);
     if (optical != isLayoutModeOptical(mParent)) {
       Insets insets = getOpticalInsets();
       int oWidth  = insets.left + insets.right;
       int oHeight = insets.top  + insets.bottom;
       widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
       heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
     }
​
     // Suppress sign extension for the low bytes
     long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
     if (mMeasureCache == null) 
      mMeasureCache = new LongSparseLongArray(2);
​
     final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
     final boolean isExactly  = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
​
     final boolean matchingSize = isExactly &&
         getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) &&
         getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
​
     if (forceLayout || !matchingSize && (widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec)) {
       // first clears the measured dimension flag
       mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
       resolveRtlPropertiesIfNeeded();
​
       int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
       if (cacheIndex < 0 || sIgnoreMeasureCache) {
         // measure ourselves, this should set the measured dimension flag back
         onMeasure(widthMeasureSpec, heightMeasureSpec);
         mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
       } else {
         long value = mMeasureCache.valueAt(cacheIndex);
         // Casting a long to int drops the high 32 bits, no mask needed
         setMeasuredDimensionRaw((int) (value >> 32), (int) value);
         mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
       }
​
       // flag not set, setMeasuredDimension() was not invoked, we raise
       // an exception to warn the developer
       if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
         throw new IllegalStateException("onMeasure() did not set the"
             + " measured dimension by calling"
             + " setMeasuredDimension()");
       }
       mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
     }
​
     mOldWidthMeasureSpec = widthMeasureSpec;
     mOldHeightMeasureSpec = heightMeasureSpec;
     mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
   }
​
   //关于每个View的实践宽高都是由父视图和本身视图决议的
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //核算出实践的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()办法完结单个View的丈量
     setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
         getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
   }
​
   protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
     boolean optical = isLayoutModeOptical(this);
     if (optical != isLayoutModeOptical(mParent)) {
       Insets insets = getOpticalInsets();
       int opticalWidth  = insets.left + insets.right;
       int opticalHeight = insets.top  + insets.bottom;
​
       measuredWidth  += optical ? opticalWidth  : -opticalWidth;
       measuredHeight += optical ? opticalHeight : -opticalHeight;
     }
     setMeasuredDimensionRaw(measuredWidth, measuredHeight);
   }
​
   private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
     mMeasuredWidth = measuredWidth;
     mMeasuredHeight = measuredHeight;
     mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
   }
​
   public void layout(int l, int t, int r, int b) { //为整个View树核算实践的方位, 需求递归的去核算每一个子视图的方位 
     if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
       onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
       mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
     }
     int oldL = mLeft;
     int oldT = mTop;
     int oldB = mBottom;
     int oldR = mRight;
​
     boolean changed = isLayoutModeOptical(mParent) ?  setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
​
     if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
       //调用onLayout回调办法,详细完成由重写了onLayout办法的ViewGroup的子类去完成
       onLayout(changed, l, t, r, b); //l,t,r,b四个值是子View相关于父View的值
       mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
​
       ListenerInfo li = mListenerInfo;
       if (li != null && li.mOnLayoutChangeListeners != null) {
         ArrayList<OnLayoutChangeListener> listenersCopy =
             (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
         int numListeners = listenersCopy.size();
         for (int i = 0; i < numListeners; ++i) {
           listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
         }
       }
     }
     mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
     mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
   }
​
   //onLayout被界说为笼统办法,继承ViewGroup时必须要重写该办法
   protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   }
​
   public void draw(Canvas canvas) {
     final int privateFlags = mPrivateFlags;
     final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
         (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
     mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
​
     /*
     * Draw traversal performs several drawing steps which must be executed
     * in the appropriate order:
     *
     *    1. Draw the background
     *    2. If necessary, save the canvas' layers to prepare for fading
     *    3. Draw view's content
     *    4. Draw children
     *    5. If necessary, draw the fading edges and restore layers
     *    6. Draw decorations (scrollbars for instance)
     */// Step 1, draw the background, if needed
     int saveCount;
​
     if (!dirtyOpaque) {
       drawBackground(canvas);
     }
​
     // skip step 2 & 5 if possible (common case)
     final int viewFlags = mViewFlags;
     boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
     boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
     if (!verticalEdges && !horizontalEdges) {
       // Step 3, draw the content
       if (!dirtyOpaque) onDraw(canvas);
​
       // Step 4, draw the children
       dispatchDraw(canvas);
​
       // Step 6, draw decorations (scrollbars)
       onDrawScrollBars(canvas);
​
       if (mOverlay != null && !mOverlay.isEmpty()) {
         mOverlay.getOverlayView().dispatchDraw(canvas);
       }
​
       // we're done...
       return;
     }
​
     ...
​
     // Step 3, draw the content
     if (!dirtyOpaque) onDraw(canvas);
​
     // Step 4, draw the children
     dispatchDraw(canvas);
​
     // Step 5, draw the fade effect and restore layers
     final Paint p = scrollabilityCache.paint;
     final Matrix matrix = scrollabilityCache.matrix;
     final Shader fade = scrollabilityCache.shader;
​
     if (drawTop) {
       matrix.setScale(1, fadeHeight * topFadeStrength);
       matrix.postTranslate(left, top);
       fade.setLocalMatrix(matrix);
       p.setShader(fade);
       canvas.drawRect(left, top, right, top + length, p);
     }
​
     if (drawBottom) {
       ...
     }
     if (drawLeft) {
       ...
     }
     if (drawRight) {
       ...
     }
​
     canvas.restoreToCount(saveCount);
​
     // Step 6, draw decorations (scrollbars)
     onDrawScrollBars(canvas);
​
     if (mOverlay != null && !mOverlay.isEmpty()) {
       mOverlay.getOverlayView().dispatchDraw(canvas); // 中心 
     }
   }
​
   protected void dispatchDraw(Canvas canvas) {
   }
​
   public void postInvalidate() {
     postInvalidateDelayed(0);
   }
​
   public void postInvalidateDelayed(long delayMilliseconds) {
     // We try only with the AttachInfo because there's no point in invalidating
     // if we are not attached to our window
     final AttachInfo attachInfo = mAttachInfo;
     if (attachInfo != null) {
       attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
     }
   }
   ...
} 

ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
     View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
   ...
​
   final class ViewRootHandler extends Handler {
     ...
     @Override
     public void handleMessage(Message msg) {
       switch (msg.what) {
       case MSG_INVALIDATE:
         ((View) msg.obj).invalidate();
         break;
       case MSG_INVALIDATE_RECT:
         final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
         info.target.invalidate(info.left, info.top, info.right, info.bottom);
         info.recycle();
         break;
       case MSG_PROCESS_INPUT_EVENTS:
         mProcessInputEventsScheduled = false;
         doProcessInputEvents();
         break;
       case MSG_DISPATCH_APP_VISIBILITY:
         handleAppVisibility(msg.arg1 != 0);
         break;
       case MSG_DISPATCH_GET_NEW_SURFACE:
         handleGetNewSurface();
         break;
       case MSG_RESIZED: {
         // Recycled in the fall through...
         SomeArgs args = (SomeArgs) msg.obj;
         if (mWinFrame.equals(args.arg1)
             && mPendingOverscanInsets.equals(args.arg5)
             && mPendingContentInsets.equals(args.arg2)
             && mPendingStableInsets.equals(args.arg6)
             && mPendingVisibleInsets.equals(args.arg3)
             && args.arg4 == null) {
           break;
         }
         } // fall through...
       case MSG_RESIZED_REPORT:
         if (mAdded) {
           ...
           requestLayout();
         }
         break;
       case MSG_WINDOW_MOVED:
         if (mAdded) {
           ...
           requestLayout();
         }
         break;
       case MSG_WINDOW_FOCUS_CHANGED: {
         if (mAdded) {
           boolean hasWindowFocus = msg.arg1 != 0;
           mAttachInfo.mHasWindowFocus = hasWindowFocus;
​
           profileRendering(hasWindowFocus);
​
           if (hasWindowFocus) {
             boolean inTouchMode = msg.arg2 != 0;
             ensureTouchModeLocally(inTouchMode);
​
             if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
               mFullRedrawNeeded = true;
               try {
                 final WindowManager.LayoutParams lp = mWindowAttributes;
                 final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
                 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                     mWidth, mHeight, mSurface, surfaceInsets);
               } catch (OutOfResourcesException e) {
                 Log.e(TAG, "OutOfResourcesException locking surface", e);
                 try {
                   if (!mWindowSession.outOfMemory(mWindow)) {
                     Slog.w(TAG, "No processes killed for memory; killing self");
                     Process.killProcess(Process.myPid());
                   }
                 } catch (RemoteException ex) {
                 }
                 // Retry in a bit.
                 sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
                 return;
               }
             }
           }
​
           mLastWasImTarget = WindowManager.LayoutParams
               .mayUseInputMethod(mWindowAttributes.flags);
​
           InputMethodManager imm = InputMethodManager.peekInstance();
           if (mView != null) {
             if (hasWindowFocus && imm != null && mLastWasImTarget &&
                 !isInLocalFocusMode()) {
               imm.startGettingWindowFocus(mView);
             }
             mAttachInfo.mKeyDispatchState.reset();
             mView.dispatchWindowFocusChanged(hasWindowFocus);
             mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
           }
​
           // Note: must be done after the focus change callbacks,
           // so all of the view state is set up correctly.
           if (hasWindowFocus) {
             if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
               imm.onWindowFocus(mView, mView.findFocus(),
                   mWindowAttributes.softInputMode,
                   !mHasHadWindowFocus, mWindowAttributes.flags);
             }
             // Clear the forward bit.  We can just do this directly, since
             // the window manager doesn't care about it.
             mWindowAttributes.softInputMode &=
                 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
             ((WindowManager.LayoutParams)mView.getLayoutParams())
                 .softInputMode &=
                   ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
             mHasHadWindowFocus = true;
           }
​
           if (mView != null && mAccessibilityManager.isEnabled()) {
             if (hasWindowFocus) {
               mView.sendAccessibilityEvent(
                   AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             }
           }
         }
       } break;
       ...
       }
     }
   }
​
   final ViewRootHandler mHandler = new ViewRootHandler();
​
   public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
     Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
     mHandler.sendMessageDelayed(msg, delayMilliseconds);
   }
​
   @Override
   public void requestLayout() {
     if (!mHandlingLayoutInLayoutRequest) {
       checkThread();
       mLayoutRequested = true; // 
       scheduleTraversals(); // mTraversalRunnable => doTraversal => performTraversals
     }
   }
​
   @Override
   public boolean isLayoutRequested() {
     return mLayoutRequested;
   }
​
   void invalidate() {
     mDirty.set(0, 0, mWidth, mHeight);
     if (!mWillDrawSoon) {
       scheduleTraversals(); // mTraversalRunnable => doTraversal => performTraversals
     }
   }
​
   final class TraversalRunnable implements Runnable {
     @Override
     public void run() {
       doTraversal();
     }
   }
​
   final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 
   void scheduleTraversals() {
     if (!mTraversalScheduled) {
       mTraversalScheduled = true;
       mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
       mChoreographer.postCallback(
           Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
       if (!mUnbufferedInputDispatch) {
         scheduleConsumeBatchedInput();
       }
       notifyRendererOfFramePending();
     }
   }
​
   void unscheduleTraversals() {
     if (mTraversalScheduled) {
       mTraversalScheduled = false;
       mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
       mChoreographer.removeCallbacks(
           Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
     }
   }
​
   void doTraversal() {
     if (mTraversalScheduled) {
       mTraversalScheduled = false;
       mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
​
       if (mProfile) {
         Debug.startMethodTracing("ViewAncestor");
       }
​
       Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
       try {
         performTraversals();
       } finally {
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
       }
​
       if (mProfile) {
         Debug.stopMethodTracing();
         mProfile = false;
       }
     }
   }
​
   private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
     Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
     try {
       mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用用View.java的measure()办法
     } finally {
       Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
   }
​
   private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
       int desiredWindowHeight) {
     mLayoutRequested = false;
     mScrollMayChange = true;
     mInLayout = true;
​
     final View host = mView;
     if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
       Log.v(TAG, "Laying out " + host + " to (" +
           host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
     }
​
     Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
     try {
       host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); // 调用用View.java的layout()办法mInLayout = false;
       int numViewsRequestingLayout = mLayoutRequesters.size();
       if (numViewsRequestingLayout > 0) {
         // requestLayout() was called during layout.
         // If no layout-request flags are set on the requesting views, there is no problem.
         // If some requests are still pending, then we need to clear those flags and do
         // a full request/measure/layout pass to handle this situation.
         ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
             false);
         if (validLayoutRequesters != null) {
           // Set this flag to indicate that any further requests are happening during
           // the second pass, which may result in posting those requests to the next
           // frame instead
           mHandlingLayoutInLayoutRequest = true;
​
           // Process fresh layout requests, then measure and layout
           int numValidRequests = validLayoutRequesters.size();
           for (int i = 0; i < numValidRequests; ++i) {
             final View view = validLayoutRequesters.get(i);
             Log.w("View", "requestLayout() improperly called by " + view +
                 " during layout: running second layout pass");
             view.requestLayout();
           }
           measureHierarchy(host, lp, mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight);
           mInLayout = true;
           host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());mHandlingLayoutInLayoutRequest = false;
​
           // Check the valid requests again, this time without checking/clearing the
           // layout flags, since requests happening during the second pass get noop'd
           validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
           if (validLayoutRequesters != null) {
             final ArrayList<View> finalRequesters = validLayoutRequesters;
             // Post second-pass requests to the next frame
             getRunQueue().post(new Runnable() {
               @Override
               public void run() {
                 int numValidRequests = finalRequesters.size();
                 for (int i = 0; i < numValidRequests; ++i) {
                   final View view = finalRequesters.get(i);
                   Log.w("View", "requestLayout() improperly called by " + view +
                       " during second layout pass: posting in next frame");
                   view.requestLayout(); 
                 }
               }
             });
           }
         }
​
       }
     } finally {
       Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
     mInLayout = false;
   }
​
   //performDraw() => draw() => drawSoftware()
   private void performDraw() {
     if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
       return;
     }
​
     final boolean fullRedrawNeeded = mFullRedrawNeeded;
     mFullRedrawNeeded = false;mIsDrawing = true;
     Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
     try {
       draw(fullRedrawNeeded); 
       // draw()调用 drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)
       // private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty)
     } finally {
       mIsDrawing = false;
       Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
​
     // For whatever reason we didn't create a HardwareRenderer, end any
     // hardware animations that are now dangling
     if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
       final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
       for (int i = 0; i < count; i++) {
         mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
       }
       mAttachInfo.mPendingAnimatingRenderNodes.clear();
     }
​
     if (mReportNextDraw) {
       mReportNextDraw = false;
       if (mAttachInfo.mHardwareRenderer != null) {
         mAttachInfo.mHardwareRenderer.fence();
       }
​
       if (LOCAL_LOGV) {
         Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
       }
       if (mSurfaceHolder != null && mSurface.isValid()) {
         mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
         if (callbacks != null) {
           for (SurfaceHolder.Callback c : callbacks) {
             if (c instanceof SurfaceHolder.Callback2) {
               ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                   mSurfaceHolder);
             }
           }
         }
       }
       try {
         mWindowSession.finishDrawing(mWindow);
       } catch (RemoteException e) {
       }
     }
   }
   ...
}

转载:版权声明:本文为keen_zuxwang原创文章,遵从 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文链接:blog.csdn.net/keen_zuxwan…

本文出于转载,其他Android海量技能知识整理:《Android开发进阶电子书籍》

更多Android高档开发技能,可点击前往获取;敞开你的Android进阶之路,知识在于积累,你的每一行代码都是在为你的人生铺路,Android路漫漫其修远兮,吾将带你走巅峰!