android 代码绘制,Android绘制源码分析(下)

Android绘制源码分析上介绍了这个屏幕的架构,以及相关的系统类Android绘制源码分析中介绍了整个绘制的启动,绘制过程 ,绘制 完毕的一个流程,这篇的终点就是介绍我们平常自定的三步骤

2565142f027e

image.png

View的生命周期

2565142f027e

image.png

1.创建Activity时View的状态

2565142f027e

image.png

2.退出Activity时View的状态

2565142f027e

image.png

3.按住Home键时View的状态

2565142f027e

image.png

生命周期介绍

onFinishInflate() 当View中所有的子控件均被映射成xml后触发

onMeasure( int , int ) 确定所有子元素的大小

onLayout( boolean , int , int , int , int ) 当View分配所有的子元素的大小和位置时触发

onSizeChanged( int , int , int , int ) 当view的大小发生变化时触发

onDraw(Canvas) view渲染内容的细节

onKeyDown( int , KeyEvent) 有按键按下后触发

onKeyUp( int , KeyEvent) 有按键按下后弹起时触发

onTrackballEvent(MotionEvent) 轨迹球事件

onTouchEvent(MotionEvent) 触屏事件

onFocusChanged( boolean , int , Rect) 当View获取或失去焦点时触发

onWindowFocusChanged( boolean ) 当窗口包含的view获取或失去焦点时触发

onAttachedToWindow() 当view被附着到一个窗口时触发

onDetachedFromWindow() 当view离开附着的窗口时触发,Android123提示该方法和 onAttachedToWindow() 是相反的。

onWindowVisibilityChanged( int ) 当窗口中包含的可见的view发生变化时触发

1.View确定大小

如何确定大小?肯定就需要测量,从根 View 递归调用每一级子 View 的 measure() 方法,对它们进行测量。

所以测量是指父View对子View测量,父View收到子View宽高参数,然后结合自身的宽高,一起商量测量出给宽高传递给子View

测量情况一

ViewRootImpl

int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);

int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

private static int getRootMeasureSpec(int windowSize, int rootDimension) {

int measureSpec;

switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:

// Window can't resize. Force root view to be windowSize.

measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);

break;

case ViewGroup.LayoutParams.WRAP_CONTENT:

// Window can resize. Set max size for root view.

measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);

break;

default:

// Window wants to be an exact size. Force root view to be that size.

measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);

break;

}

return measureSpec;

}

测量情况二

ViewRootImpl

if (lp.horizontalWeight > 0.0f) {

width += (int) ((mWidth - width) * lp.horizontalWeight);

childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,

MeasureSpec.EXACTLY);

measureAgain = true;

}

if (lp.verticalWeight > 0.0f) {

height += (int) ((mHeight - height) * lp.verticalWeight);

childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,

MeasureSpec.EXACTLY);

measureAgain = true;

}

public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,

@MeasureSpecMode int mode) {

if (sUseBrokenMakeMeasureSpec) {

return size + mode;

} else {

return (size & ~MODE_MASK) | (mode & MODE_MASK);

}

}

传递给子View mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

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);

}

if (forceLayout || needsLayout) {

// 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;

}

}

mOldWidthMeasureSpec = widthMeasureSpec;

mOldHeightMeasureSpec = heightMeasureSpec;

}

通过onMeasure父view告诉子View的宽高和测量的模式

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

}

子类拿到父类给的高度后,可能需要重新要求高度,但是高度是由父类共同决定,所以需要校验自己设定的高度合不合理

public static

int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {

final int specMode = MeasureSpec.getMode(measureSpec);

final int specSize = MeasureSpec.getSize(measureSpec);

final int result;

switch (specMode) {

case MeasureSpec.AT_MOST:

if (specSize < size) {

result = specSize | MEASURED_STATE_TOO_SMALL;

} else {

result = size;

}

break;

case MeasureSpec.EXACTLY:

result = specSize;

break;

case MeasureSpec.UNSPECIFIED:

default:

result = size;

}

return result | (childMeasuredState & MEASURED_STATE_MASK);

}

2.确定子View位置onLayout()

performLayout(lp, mWidth, mHeight);

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,

int desiredWindowHeight) {

final View host = mView;

try {

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

........................................................

}

}

host 就是DecorView,host.layout确定自身的位置

public void layout(int l, int t, int r, int b) {

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(changed, l, t, r, b);

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnLayoutChangeListeners != null) {

ArrayList listenersCopy =

(ArrayList)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);

}

}

}

}

setFrame(l, t, r, b)这个方法用于确认View四个点的位置,及初始化left,top,right,bottom的位置,这是ViewGroup布局完成

protected boolean setFrame(int left, int top, int right, int bottom) {

boolean changed = false;

if (DBG) {

Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","

+ right + "," + bottom + ")");

}

if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {

changed = true;

// Remember our drawn bit

int drawn = mPrivateFlags & PFLAG_DRAWN;

int oldWidth = mRight - mLeft;

int oldHeight = mBottom - mTop;

int newWidth = right - left;

int newHeight = bottom - top;

boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

// Invalidate our old position

invalidate(sizeChanged);

mLeft = left;

mTop = top;

mRight = right;

mBottom = bottom;

mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

mPrivateFlags |= PFLAG_HAS_BOUNDS;

if (sizeChanged) {

sizeChange(newWidth, newHeight, oldWidth, oldHeight);

}

if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {

// If we are visible, force the DRAWN bit to on so that

// this invalidate will go through (at least to our parent).

// This is because someone may have invalidated this view

// before this call to setFrame came in, thereby clearing

// the DRAWN bit.

mPrivateFlags |= PFLAG_DRAWN;

invalidate(sizeChanged);

// parent display list may need to be recreated based on a change in the bounds

// of any child

invalidateParentCaches();

}

}

return changed;

}

mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);原来这个就是设置View位置的最终方法,点进去就是native代码了

sizeChange(newWidth, newHeight, oldWidth, oldHeight);这个就是回调给子类确定最终宽高的onSizeChanged(newWidth, newHeight, oldWidth, oldHeight)

以上就是确定View位置的大概方法和流程

3.开始绘制ViewRootlmpl->draw

performDraw()

private void performDraw() {

if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {

return;

} else if (mView == null) {

return;

}

//TODO 省略

try {

boolean canUseAsync = draw(fullRedrawNeeded);

if (usingAsyncReport && !canUseAsync) {

mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);

usingAsyncReport = false;

}

} finally {

mIsDrawing = false;

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

//TODO 省略

ViewRootlmpl->boolean canUseAsync = draw(fullRedrawNeeded);

private boolean draw(boolean fullRedrawNeeded) {

Surface surface = mSurface;

if (!surface.isValid()) {

return false;

}

//TODO 省略

if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,

scalingRequired, dirty, surfaceInsets)) {

return false;

}

}

}

//TODO 省略

}

ViewRootlmpl->drawSoftware()

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,

boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

// Draw with software renderer.

final Canvas canvas;

try {

canvas = mSurface.lockCanvas(dirty);

try {

if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {

canvas.drawColor(0, PorterDuff.Mode.CLEAR);

}

dirty.setEmpty();

mIsAnimating = false;

mView.mPrivateFlags |= View.PFLAG_DRAWN;

try {

mView.draw(canvas);

drawAccessibilityFocusedDrawableIfNeeded(canvas);

}

}

return true;

}

canvas 是由图表Surface 赋值传值给 mView.draw(canvas);

View->draw()

public void draw(Canvas canvas) {

if (!dirtyOpaque) {

drawBackground(canvas);

}

if (!verticalEdges && !horizontalEdges) {

// Step 3, draw the content

if (!dirtyOpaque) onDraw(canvas);

// Step 4, draw the children

dispatchDraw(canvas);

drawAutofilledHighlight(canvas);

// Overlay is part of the content and draws beneath Foreground

if (mOverlay != null && !mOverlay.isEmpty()) {

mOverlay.getOverlayView().dispatchDraw(canvas);

}

// Step 6, draw decorations (foreground, scrollbars)

onDrawForeground(canvas);

// Step 7, draw the default focus highlight

drawDefaultFocusHighlight(canvas);

int solidColor = getSolidColor();

if (solidColor == 0) {

if (drawTop) {

canvas.saveUnclippedLayer(left, top, right, top + length);

}

if (drawBottom) {

canvas.saveUnclippedLayer(left, bottom - length, right, bottom);

}

if (drawLeft) {

canvas.saveUnclippedLayer(left, top, left + length, bottom);

}

if (drawRight) {

canvas.saveUnclippedLayer(right - length, top, right, bottom);

}

} else {

scrollabilityCache.setFadeColor(solidColor);

}

// Step 3, draw the content

if (!dirtyOpaque) onDraw(canvas);

// Step 4, draw the children

dispatchDraw(canvas);

// Step 6, draw decorations (foreground, scrollbars)

onDrawForeground(canvas);

}

最后由子类在onDraw()自定义绘制

总结一下自定义控件吧,自定义控件分几种

1.单独一个子控件,我的目的就是绘制,所以重点就是draw里面的逻辑,不需要去管layout,或者measure

2.自定义时,里面包含多个控件,这时需要去测量 (measure),布局(layout),绘制(draw)

3.自定义时明确功能,可能只是需要在TextView中加一个功能,所以可以直接继承TextView

3.自定义时可能是父布局里,如RelativeLayout,只是需要改变某个需求,可以直接继承RelativeLayout

4.自定义组合控件,某个布局块的一部分控件采用父控件统一管理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值