柳州网站定制WordPress做推广
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:28
当前位置: 首页 > news >正文
柳州网站定制,WordPress做推广,很多网站的微信登录怎么做,仙游住房与城乡建设局网站View 是Android在视觉上的呈现在界面上Android提供了一套GUI库#xff0c;里面有很多控件#xff0c;但是很多时候我们并不满足于系统提供的控件#xff0c;因为这样就意味这应用界面的同类化比较严重。那么怎么才能做出与众不同的效果呢#xff1f;答案是自定义View#…View 是Android在视觉上的呈现在界面上Android提供了一套GUI库里面有很多控件但是很多时候我们并不满足于系统提供的控件因为这样就意味这应用界面的同类化比较严重。那么怎么才能做出与众不同的效果呢答案是自定义View也可以叫自定义控件通过自定义View我们可以实现各种五花八门的效果。
除了 View 的三大流程以外 View 常见的回调方法也是需要熟练掌握的比如构造方法、onAttach、onViVisibilityChanged、onDetach等。
ViewRoot和DecorView
ViewRoot对应于ViewRootImpl类它是连接WindowManager和DecorView的纽带View的三大流程均是通过ViewRoot来完成的。
在ActivityThread中当Activity对象被创建完毕后会将DecorView添加到Window中同时会创建ViewRootImpl对象并将ViewRootImpl对象和DecorView建立关联。
View的绘制流程是从ViewRoot的performTraversals方法开始的它经过measure、layout和draw三个过程。measure用来测量View的宽和高layout用来确定View在父容器中的放置位置而draw则负责将View绘制在屏幕上。 performTraversals会依次调用performMeasure、performLayout和performDraw三个方法这三个方法分会别完成顶级View的measure、layout和draw在performMeasure中会去调用measure方法measure中又会去调用onMeasure方法然后在onMeasure方法中会对所有的子元素进行measure过程这样就完成了一次measure过程。然后子元素又会重复父容器的measure过程最后完成整个View树的遍历。
另外两个的传递流程和performMeasure是类似的唯一不同的是performDraw的传递过程是在draw方法中通过dispatchDraw来实现的。
measure过程决定了View的宽/高Measure完成以后可以通过getMeasuredWidth和getMeasuredHeight方法来获取到View测量后的宽/高Layout过程决定了View的四个顶点的坐标和实际的View的宽/高完成以后可以通过getTop、getBottom、getLeft和getRight来拿到View的四个顶点的位置并可以通过getWidth和getHeight方法来拿到View的最终宽/高Draw过程决定了View的显示只有draw方法完成以后View的内容才能呈现在屏幕上。
理解MeasureSpec
MeasureSpec
MeasureSpec 是一个用于测量视图大小的概念它通常与 View 和 ViewGroup 的测量过程密切相关。MeasureSpec 通过指定一个视图的宽度和高度模式以及实际大小来控制视图的布局过程。
在测量过程中系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec然后再根据这个measureSpec来测量出View的宽/高。
public static class MeasureSpec {private static final int MODE_SHIFT 30;private static final int MODE_MASK 0x3 MODE_SHIFT;IntDef({UNSPECIFIED, EXACTLY, AT_MOST})Retention(RetentionPolicy.SOURCE)public interface MeasureSpecMode {}public static final int UNSPECIFIED 0 MODE_SHIFT;public static final int EXACTLY 1 MODE_SHIFT;public static final int AT_MOST 2 MODE_SHIFT;public static int makeMeasureSpec(IntRange(from 0, to (1 View.MeasureSpec.MODE_SHIFT) - 1) int size,MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size mode;} else {return (size ~MODE_MASK) | (mode MODE_MASK);}}UnsupportedAppUsagepublic static int makeSafeMeasureSpec(int size, int mode) {if (sUseZeroUnspecifiedMeasureSpec mode UNSPECIFIED) {return 0;}return makeMeasureSpec(size, mode);}MeasureSpecModepublic static int getMode(int measureSpec) {//noinspection ResourceTypereturn (measureSpec MODE_MASK);}public static int getSize(int measureSpec) {return (measureSpec ~MODE_MASK);}static int adjust(int measureSpec, int delta) {final int mode getMode(measureSpec);int size getSize(measureSpec);if (mode UNSPECIFIED) {// No need to adjust size for UNSPECIFIED mode.return makeMeasureSpec(size, UNSPECIFIED);}size delta;if (size 0) {Log.e(VIEW_LOG_TAG, MeasureSpec.adjust: new size would be negative! ( size ) spec: toString(measureSpec) delta: delta);size 0;}return makeMeasureSpec(size, mode);}public static String toString(int measureSpec) {int mode getMode(measureSpec);int size getSize(measureSpec);StringBuilder sb new StringBuilder(MeasureSpec: );if (mode UNSPECIFIED)sb.append(UNSPECIFIED );else if (mode EXACTLY)sb.append(EXACTLY );else if (mode AT_MOST)sb.append(AT_MOST );elsesb.append(mode).append( );sb.append(size);return sb.toString();}
}
MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配为了方便操作其提供了打包和解包方法。
SpecMode和SpecSize也是一个int值一组SpecMode和SpecSize可以打包为一个MeasureSpec而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode和SpecSize。
SpecMode
SpecMode 是 MeasureSpec 中的一个概念用来描述测量时的约束类型。在 Android 中MeasureSpec 被用来决定视图的宽度和高度而 SpecMode 就是 MeasureSpec 的模式部分它定义了视图的大小如何受到父视图的影响。
SpecMode 可以取以下三种值
MeasureSpec.EXACTLY 说明表示父视图已经为子视图指定了一个精确的尺寸子视图必须严格遵循这个尺寸。 使用场景当父视图为子视图设定了明确的尺寸时通常是通过 match_parent 或者指定固定尺寸来实现的。在这种模式下子视图无法改变尺寸。 举例 TextViewandroid:layout_width200dpandroid:layout_height50dp/这里的宽度和高度会被视为 EXACTLY模式子视图 TextView 必须使用指定的 200dp 和 50dp 尺寸。
MeasureSpec.AT_MOST 说明表示父视图指定了一个最大尺寸子视图可以根据自身内容来确定尺寸但不能超过父视图给定的最大尺寸。 使用场景这种模式通常出现在 wrap_content 的情况下父视图给定了一个最大值而子视图的实际大小是根据内容决定的但不会超过最大值。 举例 TextViewandroid:layout_widthwrap_contentandroid:layout_height50dp/这里TextView 的宽度是 wrap_content因此它会根据内容来决定宽度但高度被固定为 50dp。
MeasureSpec.UNSPECIFIED 说明表示父视图没有为子视图的尺寸设置任何限制子视图可以根据自己的需求来决定尺寸通常不受任何约束。 使用场景这种模式通常用于 ListView 或 GridView 中的子项布局它们的父视图允许子视图自适应尺寸。 举例 LinearLayoutandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_content/
/LinearLayout在这种情况下LinearLayout的宽度和高度都是 wrap_content父视图不会给 TextView 设置具体尺寸TextView会根据内容自适应。
MeasureSpec和LayoutParams对应关系
系统内部是通过MeasureSpec来进行View的测量但是正常情况下我们使用View指定MeasureSpec尽管如此但是我们可以给View设置LayoutParams 。
在View测量的时候系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec然后再根据这个MeasureSpec来确定View测量后的宽/高。
LayoutParams需要和父容器一起才能决定View的MeasureSpec从而进一步决定View的宽/高。
对于DecorView其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定对于普通View其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。
在视图测量过程中LayoutParams 主要影响 MeasureSpec 中的模式。例如
如果 LayoutParams 的宽度和高度被设置为 MATCH_PARENT则对应的 MeasureSpec 模式通常会是 EXACTLY并且 MeasureSpec 中的尺寸会是父容器的尺寸。如果 LayoutParams 的宽度和高度被设置为 WRAP_CONTENT则对应的 MeasureSpec 模式通常会是 AT_MOST并且 MeasureSpec 中的尺寸会是父容器可用的最大空间。如果 LayoutParams 中的宽高是某个固定值例如 dp则对应的 MeasureSpec 模式通常会是 EXACTLY并且 MeasureSpec 中的尺寸会是该固定值。
LayoutParams 提供了视图布局的一些基本参数而 MeasureSpec 是视图测量过程中实际计算和传递尺寸的工具。 LayoutParams.MATCH_PARENT精确模式大小就是窗口的大小LayoutParams.WRAP_CONTENT最大模式大小不定但是不能超过窗口的大小固定大小比如100dp精确模式大小为LayoutParams中指定的大小。 对于普通View来说View的measure过程由ViewGroup传递而来
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft mPaddingRight lp.leftMargin lp.rightMargin widthUsed, lp.width);final int childHeightMeasureSpec getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop mPaddingBottom lp.topMargin lp.bottomMargin heightUsed, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}该方法会对子元素进行measure在调用子元素的measure之前会先通过getChildMeasureSpec()得到子元素的MeasureSpec。
可以再看一下ViewGroup的getChildMeasureSpec()方法 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode MeasureSpec.getMode(spec);int specSize MeasureSpec.getSize(spec);int size Math.max(0, specSize - padding);int resultSize 0;int resultMode 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension 0) {resultSize childDimension;resultMode MeasureSpec.EXACTLY;} else if (childDimension LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize size;resultMode MeasureSpec.EXACTLY;} else if (childDimension LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It cant be// bigger than us.resultSize size;resultMode MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension 0) {// Child wants a specific size… so be itresultSize childDimension;resultMode MeasureSpec.EXACTLY;} else if (childDimension LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize size;resultMode MeasureSpec.AT_MOST;} else if (childDimension LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It cant be// bigger than us.resultSize size;resultMode MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension 0) {// Child wants a specific size… let them have itresultSize childDimension;resultMode MeasureSpec.EXACTLY;} else if (childDimension LayoutParams.MATCH_PARENT) {// Child wants to be our size… find out how big it should// beresultSize View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode MeasureSpec.UNSPECIFIED;} else if (childDimension LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size…. find out how// big it should beresultSize View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode MeasureSpec.UNSPECIFIED;}break;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}主要作用是根据父容器的MeasureSpec同时结合View本身的LayoutParams来确定子元素的MeasureSpec。
参数中的padding是指父容器中已占用的空间大小因此子元素可用的大小为父容器的尺寸减去padding。 View的工作流程
View的工作流程主要是指measure、layout、draw这三大流程。
measure过程
View的measure过程
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}上面是View的onMeasure方法setMeasuredDimension方法会设置View宽/高的测量值因此我们只需要看getDefaultSize这个方法即可
public static int getDefaultSize(int size, int measureSpec) {int result size;int specMode MeasureSpec.getMode(measureSpec);int specSize MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result specSize;break;}return result;}getDefaultSize返回的大小就是measureSpec中的specSize而这个specSize就是View测量后的大小。
UNSPECIFIED这种情况一般用于系统内部的测量过程。
这种情况下View的大小为getDefaultSize的第一个参数size即宽/高分别为getSuggestedMinimumWidth和getSuggestedMinimumHeight这两个方法的返回值
protected int getSuggestedMinimumWidth() {return (mBackground null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());}
protected int getSuggestedMinimumHeight() {return (mBackground null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());}从getSuggestedMinimumWidth的代码可以看出如果View没有设置背景那么View的宽度为mMinWidth。而 mMinWidth 对 应 于
android:minWidth这个属性所指定的值因此View的宽度即为android:minWidth属性所指定的值。如果View指定了背景则View的宽度为max。
我们看一下Drawable的getMinimumWidth方法
public int getMinimumHeight() {final int intrinsicHeight getIntrinsicHeight();return intrinsicHeight 0 ? intrinsicHeight : 0;}getMinimumWidth返回的就是Drawable的原始宽度前提是这个Drawable有原始宽度否则就返回0。
ViewGroup的measure过程
ViewGroup除了完成自己的measure过程以外还会遍历去调用所有子元素的measure方法各个子元素再递归去执行这个过程。
ViewGroup提供了一个叫measureChildren的方法 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {final int size mChildrenCount;final View[] children mChildren;for (int i 0; i size; i) {final View child children[i];if ((child.mViewFlags VISIBILITY_MASK) ! GONE) {measureChild(child, widthMeasureSpec, heightMeasureSpec);}}}ViewGroup在measure时会对每一个子元素进行measure。
里面的measureChild方法
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp child.getLayoutParams();final int childWidthMeasureSpec getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft mPaddingRight, lp.width);final int childHeightMeasureSpec getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop mPaddingBottom, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}思想就是取出子元素的LayoutParams然后再通过getChildMeasureSpec来创建子元素的MeasureSpec接着将MeasureSpec直接传递给View的measure方法来进行测量。
layout过程
Layout的作用是ViewGroup用来确定子元素的位置当ViewGroup的位置被确定后它在onLayout中会遍历所有的子元素并调用其layout方法在layout方法中onLayout方法又会被调用。
public void layout(int l, int t, int r, int b) {if ((mPrivateFlags3 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) ! 0) {if (isTraversalTracingEnabled()) {Trace.beginSection(mTracingStrings.onMeasureBeforeLayout);}onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);if (isTraversalTracingEnabled()) {Trace.endSection();}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) {if (isTraversalTracingEnabled()) {Trace.beginSection(mTracingStrings.onLayout);}onLayout(changed, l, t, r, b);if (isTraversalTracingEnabled()) {Trace.endSection();}if (shouldDrawRoundScrollbar()) {if(mRoundScrollbarRenderer null) {mRoundScrollbarRenderer new RoundScrollbarRenderer(this);}} else {mRoundScrollbarRenderer null;}mPrivateFlags ~PFLAG_LAYOUT_REQUIRED;ListenerInfo li mListenerInfo;if (li ! null li.mOnLayoutChangeListeners ! null) {ArrayListOnLayoutChangeListener listenersCopy (ArrayListOnLayoutChangeListener)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);}}}final boolean wasLayoutValid isLayoutValid();mPrivateFlags ~PFLAG_FORCE_LAYOUT;mPrivateFlags3 | PFLAG3_IS_LAID_OUT;if (!wasLayoutValid isFocused()) {mPrivateFlags ~PFLAG_WANTS_FOCUS;if (canTakeFocus()) {// We have a robust focus, so parents should no longer be wanting focus.clearParentsWantFocus();} else if (getViewRootImpl() null || !getViewRootImpl().isInLayout()) {// This is a weird case. Most-likely the user, rather than ViewRootImpl, called// layout. In this case, theres no guarantee that parent layouts will be evaluated// and thus the safest action is to clear focus here.clearFocusInternal(null, /* propagate / true, / refocus / false);clearParentsWantFocus();} else if (!hasParentWantsFocus()) {// original requestFocus was likely on this view directly, so just clear focusclearFocusInternal(null, / propagate / true, / refocus / false);}// otherwise, we let parents handle re-assigning focus during their layout passes.} else if ((mPrivateFlags PFLAG_WANTS_FOCUS) ! 0) {mPrivateFlags ~PFLAG_WANTS_FOCUS;View focused findFocus();if (focused ! null) {// Try to restore focus as close as possible to our starting focus.if (!restoreDefaultFocus() !hasParentWantsFocus()) {// Give up and clear focus once weve reached the top-most parent which wants// focus.focused.clearFocusInternal(null, / propagate / true, / refocus / false);}}}if ((mPrivateFlags3 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) ! 0) {mPrivateFlags3 ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;notifyEnterOrExitForAutoFillIfNeeded(true);}notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}首先会通过setFrame方法来设定View的四个顶点的位置即初始化mLeft、mRight、mTop和mBottom这四个值View的四个顶点一旦确定那么View在父容器中的位置也就确定了 。
接着会调用onLayout方法这个方法的用途是父容器确定子元素的位置,和onMeasure方法类似onLayout的具体实现同样和具体的布局有关所以View和ViewGroup均没有真正实现onLayout方法。
draw过程
Draw过程就比较简单了它的作用是将View绘制到屏幕上面。
View的绘制过程遵循如下几步
绘制背景background.draw(canvas)绘制自己onDraw绘制childrendispatchDraw绘制装饰onDrawScrollBars public void draw(NonNull Canvas canvas) {final int privateFlags mPrivateFlags;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 views content* 4. Draw children* 5. If necessary, draw the fading edges and restore layers* 6. Draw decorations (scrollbars for instance)* 7. If necessary, draw the default focus highlight/// Step 1, draw the background, if neededint saveCount;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 contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay ! null !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (isShowingLayoutBounds()) {debugDrawFocus(canvas);}// were done…return;}/** Here we do the full fledged routine… (this is an uncommon case where speed matters less,* this is why we repeat some of the tests that have been* done above)*/boolean drawTop false;boolean drawBottom false;boolean drawLeft false;boolean drawRight false;float topFadeStrength 0.0f;float bottomFadeStrength 0.0f;float leftFadeStrength 0.0f;float rightFadeStrength 0.0f;// Step 2, save the canvas layersint paddingLeft mPaddingLeft;final boolean offsetRequired isPaddingOffsetRequired();if (offsetRequired) {paddingLeft getLeftPaddingOffset();}int left mScrollX paddingLeft;int right left mRight - mLeft - mPaddingRight - paddingLeft;int top mScrollY getFadeTop(offsetRequired);int bottom top getFadeHeight(offsetRequired);if (offsetRequired) {right getRightPaddingOffset();bottom getBottomPaddingOffset();}final ScrollabilityCache scrollabilityCache mScrollCache;final float fadeHeight scrollabilityCache.fadingEdgeLength;int length (int) fadeHeight;// clip the fade length if top and bottom fades overlap// overlapping fades produce odd-looking artifactsif (verticalEdges (top length bottom - length)) {length (bottom - top) / 2;}// also clip horizontal fades if necessaryif (horizontalEdges (left length right - length)) {length (right - left) / 2;}if (verticalEdges) {topFadeStrength Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));drawTop topFadeStrength * fadeHeight 1.0f;bottomFadeStrength Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));drawBottom bottomFadeStrength * fadeHeight 1.0f;}if (horizontalEdges) {leftFadeStrength Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));drawLeft leftFadeStrength * fadeHeight 1.0f;rightFadeStrength Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));drawRight rightFadeStrength * fadeHeight 1.0f;}saveCount canvas.getSaveCount();int topSaveCount -1;int bottomSaveCount -1;int leftSaveCount -1;int rightSaveCount -1;int solidColor getSolidColor();if (solidColor 0) {if (drawTop) {topSaveCount canvas.saveUnclippedLayer(left, top, right, top length);}if (drawBottom) {bottomSaveCount canvas.saveUnclippedLayer(left, bottom - length, right, bottom);}if (drawLeft) {leftSaveCount canvas.saveUnclippedLayer(left, top, left length, bottom);}if (drawRight) {rightSaveCount canvas.saveUnclippedLayer(right - length, top, right, bottom);}} else {scrollabilityCache.setFadeColor(solidColor);}// Step 3, draw the contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Step 5, draw the fade effect and restore layersfinal Paint p scrollabilityCache.paint;final Matrix matrix scrollabilityCache.matrix;final Shader fade scrollabilityCache.shader;// must be restored in the reverse order that they were savedif (drawRight) {matrix.setScale(1, fadeHeight * rightFadeStrength);matrix.postRotate(90);matrix.postTranslate(right, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor 0) {canvas.restoreUnclippedLayer(rightSaveCount, p);} else {canvas.drawRect(right - length, top, right, bottom, p);}}if (drawLeft) {matrix.setScale(1, fadeHeight * leftFadeStrength);matrix.postRotate(-90);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor 0) {canvas.restoreUnclippedLayer(leftSaveCount, p);} else {canvas.drawRect(left, top, left length, bottom, p);}}if (drawBottom) {matrix.setScale(1, fadeHeight * bottomFadeStrength);matrix.postRotate(180);matrix.postTranslate(left, bottom);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor 0) {canvas.restoreUnclippedLayer(bottomSaveCount, p);} else {canvas.drawRect(left, bottom - length, right, bottom, p);}}if (drawTop) {matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor 0) {canvas.restoreUnclippedLayer(topSaveCount, p);} else {canvas.drawRect(left, top, right, top length, p);}}canvas.restoreToCount(saveCount);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay ! null !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (isShowingLayoutBounds()) {debugDrawFocus(canvas);}}View绘制过程的传递是通过dispatchDraw来实现的dispatchDraw会遍历调用所有子元素的draw方法如此draw事件就一层层地传递了下去。
View有一个特殊的方法setWillNotDraw
public void setWillNotDraw(boolean willNotDraw) {setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);}如果一个View不需要绘制任何内容那么设置这个标记位为true以后系统会进行相应的优化。
默认情况下View没有启用这个优化标记位但是ViewGroup会默认启用这个优化标记位。 已经到底啦
- 上一篇: 柳州网站seowordpress模糊搜索插件
- 下一篇: 柳州网站建设服务域名使用费用一年多少钱
相关文章
-
柳州网站seowordpress模糊搜索插件
柳州网站seowordpress模糊搜索插件
- 技术栈
- 2026年03月21日
-
柳州市住房和城乡建设部网站网络设置网站
柳州市住房和城乡建设部网站网络设置网站
- 技术栈
- 2026年03月21日
-
柳州关键词优化网站军事新闻今天
柳州关键词优化网站军事新闻今天
- 技术栈
- 2026年03月21日
-
柳州网站建设服务域名使用费用一年多少钱
柳州网站建设服务域名使用费用一年多少钱
- 技术栈
- 2026年03月21日
-
柳州网站建设推荐怎么网站代备案
柳州网站建设推荐怎么网站代备案
- 技术栈
- 2026年03月21日
-
柳州正规网站建设加盟网易企业邮箱登录口入口
柳州正规网站建设加盟网易企业邮箱登录口入口
- 技术栈
- 2026年03月21日
