网站建设定制公司,广东模板建站平台,郑州网站建设怎样,中文搜索引擎有哪些文章目录 入口DecorView如何加载到Window中MeasureSpec MeasureView的测量ViewGroup的测量 LayoutView的layout() Draw1、绘制背景3、绘制View内容4、绘制子View6、绘制装饰 入口
DecorView如何加载到Window中 MeasureSpec
该类是View的内部类#xff0c;封装View的规格尺寸… 文章目录 入口DecorView如何加载到Window中MeasureSpec MeasureView的测量ViewGroup的测量 LayoutView的layout() Draw1、绘制背景3、绘制View内容4、绘制子View6、绘制装饰 入口
DecorView如何加载到Window中 MeasureSpec
该类是View的内部类封装View的规格尺寸。 他就是一个32位的int值高2为代表 specMode测量模式低30位代表specSize测量大小 specModeUNSPECIFIED AT_MOST EXACTLY 对于每个View都有对应的MeasureSpec在测量流程中通过makeMeasureSpec() 来保存宽和高通过 getMode() 和 getSize() 得到模式和宽高 MeasureSpec受自身的布局参数和父容器的测量规格共同影响 那么顶层View的DecorView没有父容器怎么得到测量规格呢 通过getRootMeasureSpec()
/*** 根据窗口大小和根视图尺寸获取根视图的MeasureSpec** param windowSize 窗口大小* param rootDimension 根视图尺寸* return 根视图的MeasureSpec*/
private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT:// 如果根视图尺寸为MATCH_PARENT即填充父窗口窗口无法调整大小。// 强制根视图尺寸为窗口大小使用MeasureSpec.EXACTLY模式。measureSpec MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);break;case ViewGroup.LayoutParams.WRAP_CONTENT:// 如果根视图尺寸为WRAP_CONTENT即自适应内容窗口可以调整大小。// 设置根视图最大尺寸为窗口大小使用MeasureSpec.AT_MOST模式。measureSpec MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);break;default:// 如果根视图尺寸为具体的数值窗口希望有确定的大小。// 强制根视图尺寸为指定的大小使用MeasureSpec.EXACTLY模式。measureSpec MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);break;}return measureSpec;
} Measure 在某些极端情况下系统可能需要多次measure才能确定最终的测量宽/高 View的测量 ViewGroup的测量
ViewGroup没有onMeasure(),用measureChildren()去递归调用子元素的测量方法measureChild() Layout
View的layout() Draw 官方文档阐述为 如果需要则绘制背景保存当前canvas层可以不执行绘制View的内容绘制子View如果需要则绘制View的褪色边缘类似于阴影效果可以不执行绘制装饰例如滚动条如果有必要绘制默认的焦点高亮显示可以不执行 1、绘制背景
调用View的drawBackground()来执行
/*** Draws the background onto the specified canvas.** param canvas Canvas on which to draw the background*/
UnsupportedAppUsage
private void drawBackground(Canvas canvas) {final Drawable background mBackground; // 获取背景Drawable对象if (background null) { // 如果背景Drawable为nullreturn; // 直接返回不进行绘制}setBackgroundBounds(); // 设置背景Drawable的边界矩形...final int scrollX mScrollX; // 获取View的当前水平滚动偏移量final int scrollY mScrollY; // 获取View的当前垂直滚动偏移量if ((scrollX | scrollY) 0) { // 如果水平和垂直滚动偏移量都为0background.draw(canvas); // 直接绘制背景Drawable在画布上} else { // 如果有滚动偏移量canvas.translate(scrollX, scrollY); // 将画布平移至滚动偏移量的位置background.draw(canvas); // 绘制背景Drawable在平移后的画布上canvas.translate(-scrollX, -scrollY); // 恢复画布的原始位置}
}3、绘制View内容
onDraw() 需要去自己进行重写实现
4、绘制子View
dispathchDraw() 需要去自己进行重写实现
ViewGroup进行了重写
Override
protected void dispatchDraw(Canvas canvas) {...for (int i 0; i childrenCount; i) { // 遍历子Viewwhile (transientIndex 0 mTransientIndices.get(transientIndex) i) { // 如果当前索引为临时索引final View transientChild mTransientViews.get(transientIndex);if ((transientChild.mViewFlags VISIBILITY_MASK) VISIBLE || transientChild.getAnimation() ! null) { // 如果临时子View可见或者临时子View有动画more | drawChild(canvas, transientChild, drawingTime); // 在画布上绘制临时子View并返回是否还有更多绘制}transientIndex; // 增加临时索引if (transientIndex transientCount) { // 如果临时索引超过了临时子View的数量transientIndex -1; // 重置临时索引}}final int childIndex getAndVerifyPreorderedIndex(childrenCount, i, customOrder); // 获取并验证预排序的子View索引final View child getAndVerifyPreorderedView(preorderedList, children, childIndex); // 根据索引找到对应Viewif ((child.mViewFlags VISIBILITY_MASK) VISIBLE || child.getAnimation() ! null) { // 如果子View可见或者有动画more | drawChild(canvas, child, drawingTime); // 在画布上绘制子View并返回是否还有更多绘制}}...
}
最后调用了drawChild()方法而该方法其实返回的是child的draw()方法即View的draw():
/*** This method is called by ViewGroup.drawChild() to have each child view draw itself.** This is where the View specializes rendering behavior based on layer type,* and hardware acceleration.*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {...if (!drawingWithDrawingCache) { // 1. 没有使用绘制缓存if (drawingWithRenderNode) { // 使用RenderNode进行绘制mPrivateFlags ~PFLAG_DIRTY_MASK;((RecordingCanvas) canvas).drawRenderNode(renderNode);} else {// 对于没有背景的布局快速路径if ((mPrivateFlags PFLAG_SKIP_DRAW) PFLAG_SKIP_DRAW) { // 子View标记为不需要被绘制mPrivateFlags ~PFLAG_DIRTY_MASK;dispatchDraw(canvas); // 调用dispatchDraw()方法进行绘制} else {draw(canvas); // 调用draw()方法进行绘制}}} else if (cache ! null) { // 2. 存在绘制缓存mPrivateFlags ~PFLAG_DIRTY_MASK;if (layerType LAYER_TYPE_NONE || mLayerPaint null) {// 没有图层画笔使用临时画笔绘制位图...} else {// 使用图层画笔绘制位图合并两个透明度并恢复...}}...return more; // 返回是否还有更多需要绘制的内容
} 6、绘制装饰
View的DrawForeground()
/*** 绘制视图的前景内容。** p前景内容可以包括滚动条、前景绘制或其他视图特定的装饰。前景绘制在主视图内容之上。/p** param canvas 用于绘制的画布*/
public void onDrawForeground(Canvas canvas) {onDrawScrollIndicators(canvas); // 调用绘制滚动指示器的方法onDrawScrollBars(canvas); // 调用绘制滚动条的方法final Drawable foreground mForegroundInfo ! null ? mForegroundInfo.mDrawable : null;if (foreground ! null) {// 如果存在前景就绘制...foreground.draw(canvas);}
}