
//在代码中设置控件大小的方法
private Button mbtn;
mbtn = (Button) findViewById(Ridbtn_test);
LayoutParams lp;
lp=mbtngetLayoutParams();
lpwidth=100;
lpheight=200;
mbtnsetLayoutParams(lp);
//在代码中设置界面大小的方法:
Display display = getWindowManager()getDefaultDisplay(); // 为获取屏幕宽、高
Window window = getWindow();
LayoutParams windowLayoutParams = windowgetAttributes(); // 获取对话框当前的参数值
windowLayoutParamswidth = (int) (displaygetWidth() 07); // 宽度设置为屏幕的095
windowLayoutParamsheight = (int) (displaygetHeight() 01); // 高度设置为屏幕的06
windowLayoutParamsalpha = 05f;// 设置透明度
//获取孩子 如果多个自己遍历设置
View mChildAt = getChildAt(0);
//测量孩子大小
mChildAtmeasure(0,0);
//获取孩子的宽高要用getMeasuredWidth、getMeasuredHeight这两个方法
mChildAtlayout(l, t, mChildAtgetMeasuredWidth(), mChildAtgetMeasuredHeight());
//以上是自定义View的内容 如果想实现需要在子Textview 加上这两个属性就行
minWidth
minHeight
方法一:
<ImageView
android:id="@+id/zb_piclist_item_iamge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="@mipmap/ic_launcher"
android:visibility="gone" />
//获取屏幕宽度
WindowManager m = (WindowManager) mContextgetSystemService(ContextWINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
mgetDefaultDisplay()getMetrics(outMetrics);
//乘以2是因为左右两侧的宽度
//bobaoArcWidth bobaoArcHeight 原始宽高
int height = (int) (width / (FloatparseFloat(bobaoArcWidth)) (FloatparseFloat(bobaoArcHeight)));
//设置参数
ViewGroupLayoutParams layoutParams = viewHolderzbPiclistItemIamgegetLayoutParams();
// layoutParamswidth = ViewGroupLayoutParamsMATCH_PARENT;
layoutParamswidth = width;
layoutParamsheight = height;
viewHolderzbPiclistItemIamgesetLayoutParams(layoutParams);
方发二
public class ResizableImageView extends ImageView {
}
<LinearLayout
xmlns:android=" >
每个view的坐标系原点为左上角那个点,水平方向为x轴,右正左负,竖直方向为y轴,下正上负。
canvasdrawColor //绘制区域涂上颜色(设置底色/蒙层)
canvasdrawCircle(float centerX(圆心X坐标),float centerY(圆心Y坐标),float radius(圆的半径,单位像素),Paint paint)
canvasdrawBitmap
canvasdrawRect(float left,float top,float right,float bottom,Paint paint) //画矩形
canvasdrawRect(RecF rect,Paint paint)
canvasdrawRect(Rect rect,Paint paint)
canvasdrawPoint(float x(点X轴坐标),float y(点Y轴坐标),Paint paint)//画点
点的大小 ->paintsetStrokeWidth(width)
点的形状 ->paintsetStrokeCap(cap)
ROUND(圆形),BUTT(平头),SQUARE(方头)
canvasdrawPoints()//批量画点
canvasdrawOval(float left(左边界点),float top(上边界点),float right(右边界点),float bottom(下边界点),Paint paint) //画椭圆
canvasdrawLine(float startX(起点X轴坐标),float startY(起点Y轴坐标),float stopX(终点X轴坐标),float stopY(终点X轴坐标),Paint paint) (setStyle对直线没有影响)
canvasdrawLines(批量画线)
canvasdrawRoundRect(float left,float top,float right,float bottom,float rx(圆角的横向半径),float ry(圆角的纵向坐标),Paint paint)//画圆角矩形
canvasdrawRoundRect(RectF rect,float rx, float ry,Paint paint)
canvasdrawArc(float left, float top, float right, float bottom, float startAngle(起始角度,顺时针为正,逆时针为负), float sweepAngle(弧形划过角度), boolean useCenter(是否连接到圆心), Paint paint) //绘制弧形或扇形 根据弧形所在椭圆进行绘制
canvasdrawPath() //通过描述路径的方式来绘制图形
pathaddXxx() —添加子图形
pathaddCircle(x,y,radius,dir(路径方向:顺时针/逆时针))
pathxxxTo —画线
pathlineTo()
pathrLineTo()
pathclose() —封闭当前图形
pathsetFillType(PathFillType ft) //设置填充模式
canvasdrawBitmap(Bitmap bitmap,float left,float top,Paint paint);//画bitmap
canvasdrawBitmap(Bitmap bitmap,Rect src,RectF dst,Paint paint)
canvasdrawBitmap(Bitmap bitmap,Rect src,Rect dst,Paint paint)
canvasdrawBitmap(Bitmap bitmap,Matrix matrix,Paint paint)
canvasdrawText(String text,float x(起点x坐标),float y(起点y坐标),Paint paint) //绘制文字
PaintsetStyle //设置绘制模式
FILL 填充模式(默认)
STROKE 画线模式
FILL_AND_STROKE 既画线又填充
PaintsetStrokeWidth //设置线条宽度 (仅在style:Stroke、FILL_AND_STROLE下有效)
PaintsetTextSize //设置文字大小
PaintsetAntiAlias //设置抗锯齿开关
PaintsetTextSize(float textSize)//设置文字大小
PaintsetStrokeJoin(PaintJoin join) //设置拐角的形状
MITER//尖角(默认)
BEVEL//平角
ROUND//圆角
PaintsetStokeMiter(float miter)//设置MITER型拐角的延长线的最大值
设置颜色
直接设置颜色
PaintsetColor(int color)
PaintsetARGB(int a,int r,int g,int b)
PaintsetShader(Shader shader) //设置shader
LinearGradient 线性渐变
RadialGradient 辐射渐变
SweepGradient 扫描渐变
BitmapShader 用bitmap的像素来作为图形或文字的填充
ComposeShader 混合着色器,多个shader混合使用
PaintsetColorFilter(ColorFilter colorFilter) //设置颜色过滤
PaintsetXfermode(Xfermode xfermode) //以要绘制的内容为源图像,以View中已有内容作为目标图像,选取一个PorterDuffMode作为绘制内容的颜色处理方案。
色彩优化
PaintsetDither(boolean dither) //设置抖动来优化色彩深度降低时的绘制效果
PaintsetFilterBitmap(boolean filter) //设置双线性过滤优化Bitmap放大绘制的效果
可以理解为 由马赛克变成模糊状态
PaintsetPathEffect(PathEffect effect)//使用PathEffect设置形状的轮廓效果
CornerPathEffect//把所有的拐角变成圆角
DiscretePathEffect//把线条进行随机的偏离
DashPathEffect//使用虚线
PathDashPathEffect//使用一个Path来绘制虚线
SumPathEffect//组合效果
ComposePathEffect//组合效果,组合有先后顺序
PaintsetShadowLayer(float radius,float dx,float dy,int shadowColor)//添加阴影
PaintsetMaskFilter(MaskFilter maskfilter)//在绘制层上方的附加效果
BlurMaskFilter //模糊效果
new BlurMaskFilter(float radius(模糊范围),BlurMaskFilterBlur style(模糊类型))
EmbossMaskFilter//浮雕效果
new EmbossMaskFilter(float[] direction(光源的方向),float ambient(环境光强度),float specular(炫光系数),float blurRadius(光线范围))
获取绘制的Path
getFillPath(Path src,Path dst)//实际path
getTextPath(Stirng text,int start,int end,float x,float y,Path)/getTextPath(char[] text,int index,int count,float x,float y,Path path)//文字的path
drawTextOnPath()//沿一条Path来绘制文字
StaticLayout //绘制文字,支持换行
paintsetFakeBoldText(booleab fakeBoldText)//是否使用伪粗体
paintsetStrikeThruText()//是否加删除线
paintsetUnderLineText(boolean underlineText)//是否加下划线
paintsetTextSkewX(float skewX)//设置文字横向错切角度
paintsetTextScaleX(float scaleX)//设置文字横向放缩
paintsetLetterSpacing(float letterSpacing)//设置字符间距,默认为0
paintsetTextAlign(PaintAlign align)//LEFT、CENTER、RIGHT默认为LEFT
paintsetTextLocale(Locale locale)/paintsetTextLocales(LocaleList locales) //设置绘制所用的地域
paintsetHinting(int mode)//是否启用字体微调
测量文字尺寸类:
paintgetFontSpacing();//获取推荐的行距
paintgetFontMetrics();//获取point的FontMetrics
baseline:基准线
ascent/descent:普通字符的顶部和底部范围
top/bottom:限制字型的顶部和底部
leading:行的额外间距,即上一行字的bottm与下一行字的top距离
paintgetTextBounds(String text(测量的文字),int start(文字的起始位置),int end(文字的结束位置),Rect bounds(文字显示范围的对象))//获取文字的显示范围
paintmeasureText(String text)//测量文字占用的宽度
measureText()>getTextBounds()
paintgetTextWidths(String text,float[] widths)//获取字符串中每个字符的宽度,并把结果填入参数widths
paintbreakText(String text((要测量的文字),boolean measureForwards(测量的方向),float maxWidth(宽度上限(超出上限会截断文字)),float[] measuredWidth(用于接受数据))//测量完成后会把文字宽度赋给measureWidth[0]
paintgetRunAdvance(CharSequence text,int start(文字的起始坐标),int end(文字的结束坐标),int contextStart(上下文的起始坐标),int ContextEnd(上下文的结束坐标),boolean isRtl(文字的方向),int offset(字数的偏移))//计算某个字符处光标的x坐标
paintgetOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)//计算出文字中最接近这个位置的字符偏移量
painthasGlyph(String s)//检查指定的字符串是否是一个单独的字型
canvasclipRect()//范围裁剪
canvasclipPath()//根据范围裁剪
canvastranslate(float dx,float dy)//位移
canvasrotate(float degrees,float px,float py)//旋转
canvasscale(float sx(横向缩放倍数),float sy(纵向缩放倍数),float px,float py)//缩放
canvasskew(float sx(x轴的错切系数),float sy(y轴的错切系数))//错切
canvassetMatrix(matrix)//用Matrix直接替换Canvas当前的变换矩阵
canvasconcat(matrix)//用Canvas当前的变换矩阵和Matrix相乘
Camerarotate()//三维旋转
1、superdraw()//总调度方法
2、superonDraw()
3、dispatchDraw()//绘制子View的方法
绘制顺序:
draw()总调度方法,view的绘制过程都发生在draw()方法里
1、背景(drawBackground()不能重写)-------android:background:/ViewsetBackgroundXxx()
2、主体(onDraw())
3、子View(dispatchDraw())
4、滑动边缘渐变和滑动条(onDrawForeground())-------android:scrollbarXxx/ViewsetXXXScrollBarXXX()
5、前景(onDrawForeground())-------android:foreground/ViewsetForeground()
viewanimate()translationX()//x轴偏移
1、如果是自定义控件,需要添加setter、getter方法
2、ObjectAnimatorofXXX()创建ObjectAnimator对象
3、用start()方法执行动画
setDuration(int duration)//设置动画时长
setInterpolator(Interpolator interpolator)//设置插值器
ViewPropertyAnimatorsetListener()/ObjectAnimatoraddListener()
ViewPropertyAnimatorsetUpdateListener()/ObjectAnimatoraddUpdateListener()
ObjectAnimatoraddPauseListener()
ViewPropertyAnimatorwithStartAction/EndAction()
ArgbEvaluator//颜色渐变动画
PropertyValuesHolder//同一个动画中改变多个属性
PropertyValuesHoldersofKeyframe()//把同一个属性拆分
AnimatorSet//多个动画配合执行
targetSdkVersion>=14,硬件加速默认开启
viewsetLayerType()
LAYER_TYPE_SOFTWARE:使用软件来绘制View Layer,绘制到Bitmap,并顺便关闭硬件加速
LAYER_TYPE_HARDWARE:使用GPU来绘制View Layer,绘制到OpenGL texture(如果硬件加速关闭,那么行为和LAYER_TYPE_SOFTWARE一致)
LAYER_TYPE_NONE:关闭View Layer
View Layer可以加速无invalidate()(例如动画)时的刷新效率,但对于需要调用invalidate()的刷新无法加速
硬件加速并不支持所有的绘制 *** 作
1、测量(measure)
View:View在onMeasuer中会计算自己的尺寸然后保存
ViewGroup:ViewGroup在onMeasure中会调用所有子View的measure让它们进行自我测量,并根据子View
计算出的期望尺寸来计算他们的事迹尺寸和位置然后保存。
2、布局(layout)
View:无子View所以onLayout不做任何处理
ViewGroup:ViewGroup在onLayout中会调用自己所有子View的layout方法,把他们的尺寸、位置传给他们, 让他们完成自我布局。
MeasureSpec = mode + size :父类传递过来给当前View的一个建议值
MeasureSpecgetMode(int spec)//获取模式
MeasureSpecgetSize(int spec)//获取数值
限制分类:
UNSPECIFIED(不限制)
AT_MOST(限制上限)->wrap_content
EXACTLY(限制固定值)->match_parent/具体值
1、重写onMeasure来修改已有的View尺寸
(1)、重写onMeasure方法,调用superonMeasure触发原有的自我测量。
(2)、在superonMeasure下用getMeasureWidth与getMeasureHeigh获取之前测量的结果,使用自己的算法计算新结果。
(3)、调用setMeasureDimension保存新结果。
2、重写onMeasure来全新定制自定义View的尺寸
与1区别,保证计算的同时,保证结果满足父View给出的尺寸限制
(1)重写onMeasure,计算出View的尺寸
(2)使用resolve让子View的计算结果符合父View的限制,也可不使用该方法自己定义
3、重写onMeasure和onLayout来全新定制自定义ViewGroup的内部布局
两个注意点:
子控件间的margin值
1、重写generateLayoutParams()和generateDefaultLayoutParams()
2、获取margin值 MarginLayoutParams lp = (MarginLayoutParams )childgetLayoutParams()
子控件间的padding值
1、测量后直接getPaddingLeft、getPaddingTop、getPaddingRight、getPaddingBottom
重写onMeasure来计算内部布局
(1)调用每个子View的measure来计算子View的尺寸
结合layout_xxx和自己可用空间
(2)计算子View的位置并保存子View的尺寸和位置
(3)计算自己的尺寸并用setMeasureDimension保存
重写onLayout来摆放子View
(1)调用每个子View的layout,让他们保存自己的位置和尺寸
view工作原理
触摸事件
1、ACTION_DOWN:手指刚接触屏幕,按下去的那一瞬间
2、ACTION_MOVE:手指在屏幕上移动
3、ACTION_UP:手指从屏幕上松开的瞬间
事件序列:从ACTION_DOWN -> ACTION_UP
ViewGroup:
DispatchTouchEvent
• return true:表示该View内部消化掉了所有事件
• return false:表示事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费
• return superdispatchTouchEvent(ev):默认事件将分发给本层的事件拦截onInterceptTouchEvent方法 进行处理
OnInterceptTouchEvent
• return true:表示将事件进行拦截,并将拦截到的事件交由本层控件的onTouchEvent进行处理
• return false:表示不对事件进行拦截,事件得以成功分发到子View
• return superonInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的 dispatchTouchEvent
OnTouchEvent 默认false
• return true:表示onTouchEvent处理完事件后消费了此次事件
• return fasle:表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的 onTouchEvent方法返回true
• return superdispatchTouchEvent(ev):表示不响应事件,结果与return false一样
子View不存在分发:
• DispatchTouchEvent 事件分发
• OnTouchEvent 默认true
如下图为事件分发流程图:
---------------------- 以上总结部分源自Hencoder教程 ------------------------------
在代码里修改,先获取这两个Button, 然后获取一个的宽,通过setLayoutParams(ViewGroupLayoutParams
params),修改第二个的宽高,注意,获取view的宽高的方法有3中,你要选择合适的。
layouts 的每个子 layout 信息都支持 margins。参考 ViewGroup Margin Layout Attributes 列举了该类支持的所有子 view 的属性。
android:layout_marginBotton
在该 view 的底边指定一个特定的空间。这个空间是在 view 边界的外部。margin 值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginBottom。
相关的方法:
setMargins(int,int,int,int)
android:layout_marginEnd
在该 view 的结束的一边指定一个特定的空间。这个空间是在 view 边界的外部。margin 的值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginEnd。
相关的方法:
setMarginEnd(int)
android_marginLeft
在该 view 的左边指定一个特定的空间。这个空间是在 view 边界的外部。margin 的值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginLeft。
相关的方法:
setMargins(int,int,int,int)
android:layout_marginRight
在该 view 的右边指定一个特定的空间。这个空间是在 view 边界的外部。margin 的值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginRight。
相关的方法:
setMargins(int,int,int,int)
android:layout_marginStart
在该 view 的开始的一边指定一个特定的空间。这个空间是在 view 边界的外部。margin 的值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginStart。
相关的方法:
setMarginStart(int)
android:layout_marginTop
在该 view 的上边指定一个特定的空间。这个空间是在 view 边界的外部。margin 的值应该是正数。
必须是一个具体的值,一个浮点数字加一个单位如『145sp』。可选的单位有:px(pixels 象素),dp(density-independent pixels 密度独立象素),sp(scaled pixels based on perferred font size 基于优选字体大小的缩放像素『一般用于设置字体大小』),mm(millimeters 毫米)。
也可能是对包含该类型值的资源或主题属性的引用。
这相当于全局属性资源符号 layout_marginTop。
相关的方法:
setMargins(int,int,int,int)
public int bottomMargin
以象素为单位的下边距。margin 值应该是正数。调用 setLayoutParams(LayoutParamsp) 方法后为这个值重新分配一个新的值。
public int leftMargin
以象素为单位的左边距。margin 值应该是正数。调用 setLayoutParams(LayoutParamsp) 方法后为这个值重新分配一个新的值。
public int rightMargin
以象素为单位的右边距。margin 值应该是正数。调用 setLayoutParams(LayoutParamsp) 方法后为这个值重新分配一个新的值。
public int topMargin
以象素为单位的上边距。margin 值应该是正数。调用 setLayoutParams(LayoutParamsp) 方法后为这个值重新分配一个新的值。
public ViewGroupMarginLayoutParams(Context c,AttributeSet attrs)
创建一个新的布局参数集合。这些值是从提供的属性集合和 context 中提取的。
参数
c 应用的环境
attrs 来自于布局参数的属性集合
public ViewGroupMarginLayoutParams(int width,int height)
public ViewGroupMarginLayoutParams(ViewGroupMarginLayoutParams source)
复制构造方法。克隆 source 的宽、高和 margin 值。
参数
source 克隆的对象
public int getLayoutDirection()
返回布局的方向。可以是 LAYOUT_DIRECTION_LTR 或是 LAYOUT_DIRECTION_RTL 。
返回值
int 布局的方向
public int getMarginEnd()
返回以象素为单位的结尾 margin 值。
相关的 XML 属性值
android:layout_marginEnd
返回值
int 以象素为单位的结尾 margin 值
public int getMarginStart()
返回以象素为单位的开始的 margin 值。
相关的 XML 属性值
android:layout_marginStart
返回值
int 以象素为单位的开始 margin 值
public boolean isMarginRelative()
检查 margin 是否相对。
相关的 XML 属性
android:layout_marginStart
android:layout_marginEnd
返回值
boolean marginStart 或 marginEnd 已经设定,返回 true
public void resolveLayoutDirection(int layoutDircetion)
由 requestLayout()方法调用。根据布局的方向可以重写左右 margin 的值。
参数
layoutDircetion 布局的方向值(int)
public void setLayoutDircetion(int layoutDirection)
设置布局的方向。
参数
layoutDirection 布局的方向。LAYOUT_DIRECTION_LTR 或是 LAYOUT_DIRECTION_RTL
public void setMarginEnd(int end)
设置相对结束 margin。margin 值应该是正数。
相关的 XML 属性
android:layout_marginEnd
参数
end 结束 margin 的值
public void setMarginStart(int start)
设置相对开始 margin。margin 值应该是正数。
相关的 XML 属性
android:layout_marginStart
参数
start 开始 margin 的值
public void setMargins(int left,int top,int right,int bottom)
设置 margin,以象素为单位。需要先调用 requestLayout() 方法,这样新的 margin 值才能被使用。根据布局的方向左右 margin 值可能会被重写。margin 值应该是正数。
相关的 XML 属性
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
参数
left 左 margin 值
top 上 margin 值
right 右 margin 值
bottom 下 margin 值
title: '深入理解android2-WMS,控件-图床版'
date: 2020-03-08 16:22:42
tags:
typora-root-url: /深入理解android2-WMS-控件
typora-copy-images-to: /深入理解android2-WMS-控件
WMS主要负责两个功能, 一是负责窗口的管理,如窗口的增加删除,层级二是负责全局事件的派发如触摸点击事件
先简单介绍几个重要的类
IWindowSession 进程唯一的是一个匿名binder通过他向WMS请求窗口 *** 作
surface 绘画时,canvas会把内容绘制到surface里surface是有surfaceFlinger提供给客户端的
WindowManagerLayoutParams 集成自ViewGroupLayoutParams用来指明client端的窗口的一些属性最重要的是type 根据这属性来对多个窗口进程ZOrder的排序
windowToken向WMS添加的窗口令牌每个窗口都要有一个令牌
IWindow 是client提供给WMS的继承自binderWMS通过IWindow对象来主动发起client端的事件
窗口的本周就是进行绘制所使用的surface,客户端向WMS添加窗口的过程,就是WMS为客户端分配surface的过程
ui框架层就是使用surface上绘制ui元素及响应输入事件
WMS负责surface的分配窗口的层级顺序
surfaceFlinger负责将多个Surface混合并输出
WMS有SystemServer 进程启动他和AMS其实是运行于一个进程中的只是分别有各自的线程
上边传入了两个handler这里就使用windowManager的handler来创建WMS也就是在一个handerThread线程中创建
用来管理每个窗口的事件输入也就是把输入事件转发到正确的窗口
能获取显示系统的同步信号用来驱动动画的渲染
所有窗口动画的总管,在mChoreographer的驱动下渲染所有动画
只有PhoneWindowManager一个实现定义了很多窗口相关的策略是最重要的成员,比如负责窗口的zorder顺序
zorder就是各个窗口在z轴的值越大越在屏幕上层窗口就是根据zorder值一层一层堆在一起
可以绘制的屏幕列表默认是只有1个
管理所以窗口的显示令牌token,每个窗口都要属于一个token这里的IBinder 是
表示所有Activity的token AppWindowToken是WindowToken的子类,这个list的顺序和AMS中对mHistory列表中activity的顺序是一样的 反应了系统中activity的叠加顺序也就是说所有窗口都有WindowToken而Activity对应的窗口则多了AppWindowToken
每个窗口都对应一个WindowState存储改窗口的状态信息(这就和AMS中对每个activity抽象成ActivityRecord一样)
这里的iBinder 是IWIndow类
Session 是WMS提供给客户端来与WMS进行交互的,这是匿名binder为了减轻WMS的负担客户端通过IWindowManageropenSession 拿到他的代理然后通过代理与WMS交互每个进程唯一
客户端通过IWindowSessionadd 来添加窗口 iWindowSession 是同aidl形成的最终到了WMSaddWindow
这里总的来说就是确立了客户窗口的WindowTokenWindowState和DisplayContent 并都保存了起来同时根据layoutparamstype进行了些窗口等级的判断
WindowToken将同一个应用组件的窗口安排在一起一个应用组件可以是Activity,InputMethod
WindowToken使应用组件在变更窗口时必须与自己的WindowToken匹配
这里主要是为了处理窗口的层级关系而设立的
只要是一个binder对象都可以作为token向wms声明wms会把这个binder对应起一个WindowToken其实就是把客户端的binder和wms里的一个WindowToken对象进行了绑定
因为Activity比较复杂,因此WMS为Activity实现了WindowToken的子类 appwindowtoken同时在AMS启动Activity的ActivityStackstartActivityLocked里声明token
然后在activityStackrealStartActivityLocked里在发给用户进程,然后用户在通过这个binder和WMS交互时带过来
activity则在 activityStack 线程的handleResumeActivity 里把Activity 对应的窗口,加入到wMS中
取消token 也是在AMS中 ,也就是说, AMS负责avtivity的token向WMS的添加和删除
当然Activity的 rappToken 是 IApplicationTokenStub ,他里边有一系列的窗口相关的通知回调
这里总结下 AMS在创建Activity的ActivityRecord时,创建了他的appToken,有把appToken传送给WMSWMS对应匹配为APPWindowToken,最后还把这个appToken发送给activity因此AMS就通过ActivityRecord就可有直接 *** 作WMS对该窗口的绘制如图
每个window在WMS里都抽象成了WindowState他包含一个窗口的所有属性WindowState在客户端对应的则是iWidowstub类iWidowstub有很多窗口通知的回调
WindowState被保存在mWindowMap里这是整个系统所有窗口的一个全集
HashMap<IBinder, WindowToken> mTokenMap 这里是 IApplicationToken(客户端)和WindowToken的映射
HashMap<IBinder, WindowState> mWindowMap 这里是IWidow(客户端)和WindowState的映射,并且WMS通过这个IWindow 来回调客户端的方法
上图可以看出每个activity 只有一个ActivityRecord也只有一个AppToken,也就只有一个WindowToken而一个acitvity可能有多个窗口每个窗口对应一个WindowState
WindowToken用来和AMS交换 而WindowState对应的iWindow则是WMS来与客户端交互的
窗口显示次序就是窗口在Z轴的排了因为窗口是叠加在一起的因此就需要知道哪些显示在上边,哪些在下边这个由WindowState构造时确定
可见分配规则是由WindowManagerPolicy mPolicy来决定的产生 mBaseLayer和mSubLayer mBaseLayer决定该窗口和他的子窗口在所有窗口的显示位置 mSubLayer决定子窗口在同级的兄弟窗口的显示位置值越高显示约靠上
WindowState 产生了他自己这个窗口的layer值后在添加窗口的时候就会把所有窗口按layer排序插入mWindows列表中,在通过 adjustWallpaperWindowsLocked();进行层级调整
当客户端通过IWindowsessionadd后,客户端还没有获得Surface只有在执行IWindowsessionrelayout后客户端才获得了一块Surface IWindowsessionrelayout根据客户端提供的参数,为客户端提供surface具体实现是WMSrelayoutWindow
总的来说就是根据用户传入的参数,更新WindowState然后遍历所有窗口布局在设置合适的Surface尺寸,在返回给用户端
performLayoutAndPlaceSurfacesLocked 会循环调用6次里边的逻辑大概如下
这里主要下,因为之前加了锁requestTraversalLocked他又会重复执行performLayoutAndPlaceSurfacesLocked();因此会重复循环执行布局
布局这部分就记个原理吧
布局完成后客户端的尺寸和surface都得到了就可以绘制 了WMS会通知客户端布局发送变化
总结,WMS 负责管理所有的窗口包括系统窗口和APP窗口,而窗口必须有一个WindowToken所为标识符同时WMS为每个窗口创建一个WindowState类,这是窗口在服务端的抽象WindowState则绑定了一个客户端的IWindow类,WMS通过这个IWindow 向APP发送消息
AMS在启动Activity的时候把ActivityRecordtoken 通过wmsaddtoken 注册到WMS又把这个token发送到APP端因此三方可以通过这个token正确找到对应的数据
WMS负责给所以窗口按ZOrder排序,确定窗口的尺寸,提供绘画用的surface
Activity的窗口是先wmsaddtoken 建立windowToken关系 wmsaddWindow 添加串口, WMSrelayout获取surface 完成
一个windowToken对应一个Activity 但是可能对应多个windowSatate也就是对应多个窗口
是view树的根实现类是viewRootImpl但是他不是view他是用来和WMS进行交流的管理者viewRootImpl内部有个IWindowSession,是WMS提供的匿名binder,同时还有个iWindow子类,用来让WMS给viewr发消息 view通过ViewRoot向WMS发消息WMS在通过IWIndow 向APP发消息 每个View树只有一个ViewRoot,每个Activity也只有一个ViewRoot UI绘制,事件传递都是通过ViewRoot
实现类是PhoneWindow Activity和View的沟通就是通过WindowActivity实现window的各种回调一个Activity也对应一个PhoneWindow也对应一个View树
Docerview 就是View树的根这是一个View 他由PhoneWindow管理 下文的WindowManager也由phoneWindow管理
他还管理window的属性 WindowManagerlayoutparams
他是一个代理类他集成自ViewManager他的实现是WindowManagerImpl这是每个Activity都有一个但是他只是把工作委托给了 WindowManagerGlobal来实现 他负责添加删除窗口,更新窗口并控制窗口的补件属性WindowManagerLayoutparams
是进程唯一的负责这个进程的窗口管理他里边有三个集合保存这个进程所有窗口的数据这里的每个数据根据index得到的是同一个Activity属性所有的WindowManager的 *** 作都转到他这里来
private final ArrayList<View> mViews 每个view是个跟节点
private final ArrayList<ViewRootImpl> mRoots view对应的viewRoot
private final ArrayList<WindowManagerLayoutParams> mParams 窗口的layoutparams属性每个窗口一个
对于一个acitivity对象永远对应一个PhoneWindow,一个WindowManagerImpl,一个WMS端的APPWindowToken,一个AMS里的ActivityRecord(但是如果一个activity在栈里有多个对象,就有多个ActivityRecord和AppWindowToken),acitvity 的默认窗口的view树是DocerView
一个窗口 对应一个ViewRoot,一个View树一个WindowManagerLayoutParams,一IWindow(WMS回调app)一个WSM端的WindowSatate
但是一个Activity可以有多个窗口,因此对应WMS里可能有多个WindowSatate这些WindowState都对应一个AppWindowToken
一个Activity可能被加载多次因此在AMS中可能有多个ActivityRecord对应这个activit的多个对象
但是一个进程则对应一个WindowManagerGlobal一个ActivityThread(主线程)一个ApplicationThread(AMS调用app)一个iWindowSession(viewroot向WMS发消息)
这里的区别就是 app与AMS 的交互是以进程之间进行通信而App与WMS的交互则是以窗口作为通信基础
当Activity由AMS启动时ActivityThread 通过handleResumeActivity执行resume相关的 *** 作这个函数首先是执行activityresume, 此时activity 对应的view树已经建立完成(oncreate中建立,PhoneWindow也创建了)需要把activity的窗口添加到WMS中去管理
这里的wm是WindowManager是每个activity一个他内部会调用WindowManagerGlobaladdView
WindowManagerGlobaladdView
这里会为窗口创建ViewRootImpl 并把viewViewRootImplWindowMaLayoutParams都保存在WindowManagerGlobal中, 并通过ViewRootImpl向WMS添加窗口
如果这个窗口是子窗口(wparamstype >= WindowManagerLayoutParamsFIRST_SUB_WINDOW &&
wparamstype <= WindowManagerLayoutParamsLAST_SUB_WINDOW)
就把子窗口的token设为父窗口的token否则就是所属activity的token
在来个图
在这里我们看到我们通过mWindowManager = (WindowManager) mContextgetSystemService(ContextWINDOW_SERVICE); 拿到的并不是远程的WMS而是本地的WindowManagerImpl 他又把请求转发给WindowManagerGlobal ,而WindowManagerGlobal作为进程单实例又是吧请求转给对应窗口的ViewRootImplViewRootImpl通过WMS的IWindowSession 把数据发给WMS
ViewRootImpl用来沟通View和WMS并接受WMS的消息这是双向的binder通信作为整个空间树的根部,控件的测量,布局,绘制,输入时间的派发都由ViewRootImpl来触发
ViewRootImpl由WindowManagerGlobal创建,是在activityThreadhandleResumeActivity时,先执行activityresume在调用wmaddView 就会执行WindowManagerGlobaladdView里创建ViewRootImpl,此时是在ui线程中
ViewRootImpl里的mView属性host属性,就是view树
添加窗口时通过requestLayout();向ui线程发送消息最后回调到ViewRootImplperformTraversals他是整个ui控件树,measurelayoutdraw的集合
这里分为五个阶段
预测量阶段进行第一次测量,获得viewgetMeasuredWitdh/Height,此时是控件树期望的尺寸会执行View的onMeasure
布局阶段,根据预测量的结果,通过IWindowSessionrelayout向WMS请求调整窗口的尺寸这会使WMS对窗口重新布局,并把结果返回给ViewRootImpl
最终测量阶段, 预测量的结果是view树期望的结果WMS可能会进行调整,在这里WMS已经把结果通知了ViewRootImpl因此这里会窗口实际尺寸performTraversals进行布局view及子类的onMeasure会被回调
布局阶段 测量完成后获得空间的尺寸,布局要确定控件的位置,View及子类的onLayout会被回调
绘制阶段,使用WMS提供的surface进行绘制,View及子类的onDraw会被回调
通常我们看到的都是 先measure,在layout在draw 这里看到其实measure先得到期望值,在和WMS沟通WMS在调整后,返回确定值,在根据确定值进行mesure
measureHierarchy里会通过三次协商执行performMeasure 来确认合适的尺寸
performMeasure 会调用view 的measure 优会调用onMeasure 我们可重写onMeasure来实现测量而measure 方法是final的onMeasure 的结果通过setMeasuredDimension方法保存
对于view onMeasure比较容易 对于ViewGroup则还要遍历调用他所以子view的measure 并且需要考虑padding和子view 的margin padding是控件外内边距 margin 是控件外边距
ViewGroup需要先测量完子view在根据子view的测量值得到自己的宽高举例,如果只有一个子view那么ViewGroup的宽= 子view的宽+子view的margin+viewg的padding 至少是这个值
继续回到performTraversals
这里就是提前测量了一下得到控件树希望的尺寸大小,
通过relayoutWindow来布局窗口 ViewRootImpl 通过IWindowSession 来通知WMS进行窗口布局
这里主要下 调用WMS后WMS会调整窗口的尺寸 同时会生成surface返回给ViewRootImpl 因此后续的绘画就有了画布了可以看到最后的参数是mSurface这是本地的surface 这里会和wms的进行绑定
接下来继续performTraversals,绑定WMS返回的surface然后更新尺寸
最后进行最终测量 上边过程太乱了 了解下就行还是看常见的控件绘制流程
绘制由viewRootImplperformTraversals触发, 抽取出来后,就是这样
就是直接调用view树的根的measure方法 传入到View
该方法是final 意味着无法重写这里又会调用onMeasure
因此对于view在onMeasure中调整好高度,通过setMeasuredDimension设置好自己的测量宽高就可以了
对应ViewGroup则在onMeasure中,先要遍历子view调用他们的measure(注意一定是调用子类的measure,measure又会调用onMeasure), 子view宽高都知道后,在根据子view的宽高来设置自己也就是ViewGroup的宽高受子view影响
可以看到view的measure又调用了onMeasure, 如果是view 则可以直接重新onMeasure来设定大小而对于ViewGroup, 则需要重写onMeasure来先遍历子view设定大小然后再设定viewGroup的大小 ViewGroup并没有重写onMeasure因为每个ViewGroup要实现的效果不同,需要自己完成但ViewGroup提供了几个方法供ViewGroup的继承类来遍历子view
view的宽高由自己的layoutParams和父view提供的 widthMeasureSpec|heightMeasureSpec共同决定
View 自己的宽高,是保存在LayoutParams中对,以宽举例 LayoutParamswidth 有三种情况,精确值(就是指定大小),MATCH_PARENT WRAP_CONTENT,模式则有fuview提供有 unspecified,exactly,at_most三种
匹配如下
其实这个很好理解 如果子view自己指定了宽高就用他的值就可以如果子view是match_parent那就使用父view提供的宽高 如果子view是wrap_content,那就不能超过父view的值
看下ViewGroup为子view绘制而提供的方法,可以看到ViewGroup会减去padding和margin,来提供子view的宽高
上步measure过程未完成后,整个view书的 测量宽高都得到了也就是viewgetMeasuredWidth()和getMeasuredHeight()
performLayout中会调用mViewlayout 这样就把事件从ViewRootImpl传递到了view而layout中又会调用onLayoutViewGroup需要重写onLayout为子view进行布局,遍历调用子view的layout因此就完成整个view树的laylut过程
竖向的实现, 竖向的就行把view从上到下一次排开
这里注意区分measure过程是先得到子view的测量值,在设定父ViewGroup的值而layout过程则是先传入父view的左上右下值,来计算子view的左上右下的位置值这里应该具有普遍性但不知道是否绝对
performDraw 中的调用draw又调用mViewdraw然后就进入view树的绘制了
view的draw 又会调用onDraw ,viewGroup又调用dispatchDraw()把draw分发到子view里 绘制的画布就是canvas 这是从surfacelockCanvas中获得的一个区域
而在ViewGroupdispatchDraw中重要的一点是getChildDrawingOrder 表示子view的绘制顺序默认是与ziview的添加顺序一样我们也可以改变他最后绘制的会显示在最上边,而这也影响view的事件传递顺序
viewdraw 就是一层一层的画内容先画北京,在onDraw在画装饰什么的
canvastranslate(100,300)通过平移坐标系使之后的内容可以直接在新坐标系中绘制
这就是ViewGroup在向子view传递canvas的时候方便多了 会之前先对其ziview的左上角那么子view就可以直接从自己坐标轴的(0,0)开始绘制, 绘制完成后ViewGroup在还原原有坐标系
canvassave canvasrestore 用来保存还原坐标系
viewinvalidate
当某个view发送变化需要重绘时,通过viewinvalidate向上通知到ViewRootImpl从这个view到ViewRootImpl的节点都标记为藏区域dirty area ViewRootimpl再次从上到下重绘时,只绘制这些脏区域效率高
本来安卓兼容使用键盘,也支持,触摸二者的输入事件派发不一样使用键盘时会有个控件处于获得焦点状态处于触摸模式则由用户决定 因此控件分为两类任何情况下都能获得焦点如输入文本框只有在键盘 *** 作时才能获得焦点如菜单,按钮
安卓里有触摸模式当发送任意触摸时进入触摸模式当发送方向键和键盘或者执行ViewrequestRocusFromTouch时,退出触摸模式
获取焦点 viewrequest
先检查是否能获取焦点,
然后设置获取简单的标记,
向上传递到ViewRootimpl保证只能有一个控件获取焦点
通知焦点变化的监听者
更新view的drawable状态,
requestChildFocus会把焦点事件层层上报取消原来有焦点的控件最后的效果就是从viewrootimpl中到最终有焦点的view构成一条 mFoucued 标识的链条来个图就明白了每个view的mFocused总是指向他的直接下级
获取focus的传递是从底层view到顶层的ViewRootImpl而取消focus测试从顶层的ViewRootimpl到底层原来那个获得焦点的view
而如果是ViewGroup请求获取焦点,会根据FLAG_MASK_FOCUSABILITY特性来做不同方式,分别有先让自己获取焦点,或者安卓view的索引递增或者递减来匹配view
ViewRootImpl 中的WindowInputEventReceiver接受输入事件他会把事件包装成一个QueuedInputEvent然后追加到一个单链表的末尾接着重头到尾的处理输入事件,并通过deliverInputEvent完成分发这里会把单链表所有事件都处理完
deliverInput中又会把触摸事件执行到通过 ViewPreImeInputStageprocessKeyEvent 转入mViewdispatchPointerEvent(event)这里又进入 dispatchTouchEvent
MotionEvent是触摸事件的封装getAction可以拿到动作的类型和触控点索引号
getX(),getY()拿到动作的位置信息通过getPointID拿到触控点的id 动作以down 开头跟多个move最后是up
,当事件返回true表示事件被消费掉了
以上就是关于Android中如何在代码中设置View的宽和高全部的内容,包括:Android中如何在代码中设置View的宽和高、自定义ViewGroup的TextView垂直居中没用、简单实现ImageView宽度填满屏幕,高度自适应的两种方式等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)