个人进阶之路——自定义控件(2)
2016-02-24 00:55
309 查看
今天继续昨天没有完成的内容。
昨天写得到怎么定义并使用
今天要去别人那里总结一下了。
**onMeasure()决定——View本身大小多少
onLayout()决定——View在ViewGroup中的位置如何
onDraw()决定——如何绘制该View**
首先是onMeasure方法,我们知道,系统帮我们测量的宽高都为match_parent,如果没有明确宽高时,系统帮我们测量的结果就是咱设置的结果,当我们设置为wrap_content之后或者match_parent时,系统帮我们测量的结果就是match_parent的长度。所以当我们设置为wrap_content时,要自己进行测量,即重写onMeasure方法。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
重写onMeasure(),并调用父类onMeasure()时
自定义View的layout_width以及layout_height属性值 match_parent 或者 wrap_content显示大小由其父容器控件决定。
自定义View设置为固定的值,就显示该设定的值
MeasureSpec的specModed三种类型:
**EXACTLY:设置明确值 or MATCH_PARENT
AT_MOST:子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:子布局想多大就多大,很少使用**
· 理解这一段,widthMeasureSpec和 heightMeasureSpec两参数是从vg中传入,因为onMeasure()由包含该View的具体ViewGroup所调用,子类View的这两参数,由vg中的width,height和padding以及View自身margin共同决定。weight也需考虑(较复杂)
· 两参数作用:取heightMeasureSpec作说明,该值由高32位(specMode,可由MeasureSpec.getMode()获取)和低16位(specSize,可由MeasureSpec.getSize获取)组成。
· 所有的View的onMeasure()最后一行都会调用setMeasureDimension()函数——该函数调用中传进去的值是View最终的视图大小,之前所有工作为了最后这一句话服务的。
vg中,给View分配的空间大小不确定,可能随具体变化而变化,变化条件是传到specMode中决定
specMode有三种可能:
**·**EXACTLY:父视图希望子视图的大小应该是specSize中指定的。
**·**AT_MOST:子视图的大小最多是specSize中指定的值,不建议子视图的大小超过specSize中给定的值。
**·**UNSPECIFIED:可随意指定视图的大小。
**·**specMode能根据vg中具体能够提供的空间大小来指定子View的视图大小。
·视图最终的大小由父视图,子视图以及程序员根据需要决定
·良好的设计一般会根据子视图的measureSpec设置合适的布局大小。
·重写onMeasure()方法是为了自定义View尺寸的规则
·如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法
举例几个重写onMeasure来看看
(1)例子1
(2)例子2
//widthMeasureSpec 和 heightMeasureSpec的值 由父容器决定
定义一个方法处理 widthMeasureSpec,heightMeasureSpec的值
说明:
MeasureSpec.getSize()会解析MeasureSpec值得到父容器width或者height。
MeasureSpec.getMode()会得到三个int类型的值分别为:EXACTLY ,AT_MOST,UNSPECIFIED。
MeasureSpec.UNSPECIFIED 未指定,故可以设置任意大小。
MeasureSpec.AT_MOST 自定义View可以为任意大小,但是有一个上限。
(3)例子3
(4)例子4
//负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高
(5)例子5
(6)例子6
昨天写得到怎么定义并使用
今天要去别人那里总结一下了。
**onMeasure()决定——View本身大小多少
onLayout()决定——View在ViewGroup中的位置如何
onDraw()决定——如何绘制该View**
首先是onMeasure方法,我们知道,系统帮我们测量的宽高都为match_parent,如果没有明确宽高时,系统帮我们测量的结果就是咱设置的结果,当我们设置为wrap_content之后或者match_parent时,系统帮我们测量的结果就是match_parent的长度。所以当我们设置为wrap_content时,要自己进行测量,即重写onMeasure方法。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
重写onMeasure(),并调用父类onMeasure()时
自定义View的layout_width以及layout_height属性值 match_parent 或者 wrap_content显示大小由其父容器控件决定。
自定义View设置为固定的值,就显示该设定的值
MeasureSpec的specModed三种类型:
**EXACTLY:设置明确值 or MATCH_PARENT
AT_MOST:子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:子布局想多大就多大,很少使用**
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; ... if (widthMode == MeasureSpec.EXACTLY) { // Parent has told us how big to be. So be it. width = widthSize; } else { if (mLayout != null && mEllipsize == null) { des = desired(mLayout); } ... setMeasuredDimension(width, height); //此前做的所有操作都是为了服务于它 }
· 理解这一段,widthMeasureSpec和 heightMeasureSpec两参数是从vg中传入,因为onMeasure()由包含该View的具体ViewGroup所调用,子类View的这两参数,由vg中的width,height和padding以及View自身margin共同决定。weight也需考虑(较复杂)
· 两参数作用:取heightMeasureSpec作说明,该值由高32位(specMode,可由MeasureSpec.getMode()获取)和低16位(specSize,可由MeasureSpec.getSize获取)组成。
· 所有的View的onMeasure()最后一行都会调用setMeasureDimension()函数——该函数调用中传进去的值是View最终的视图大小,之前所有工作为了最后这一句话服务的。
vg中,给View分配的空间大小不确定,可能随具体变化而变化,变化条件是传到specMode中决定
specMode有三种可能:
**·**EXACTLY:父视图希望子视图的大小应该是specSize中指定的。
**·**AT_MOST:子视图的大小最多是specSize中指定的值,不建议子视图的大小超过specSize中给定的值。
**·**UNSPECIFIED:可随意指定视图的大小。
**·**specMode能根据vg中具体能够提供的空间大小来指定子View的视图大小。
·视图最终的大小由父视图,子视图以及程序员根据需要决定
·良好的设计一般会根据子视图的measureSpec设置合适的布局大小。
·重写onMeasure()方法是为了自定义View尺寸的规则
·如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法
举例几个重写onMeasure来看看
(1)例子1
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height ; if (widthMode == MeasureSpec.EXACTLY){ width = widthSize; } else{ mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); float textWidth = mBounds.width(); int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight()); width = desired; } if (heightMode == MeasureSpec.EXACTLY){ height = heightSize; } else{ mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); float textHeight = mBounds.height(); int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom()); height = desired; } setMeasuredDimension(width, height); }
(2)例子2
//widthMeasureSpec 和 heightMeasureSpec的值 由父容器决定
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = measureDimension(DEFAULT_WIDTH, widthMeasureSpec); int height = measureDimension(DEFAULT_HEIGHT, heightMeasureSpec); setMeasuredDimension(width, height); }
定义一个方法处理 widthMeasureSpec,heightMeasureSpec的值
private int measureHanlder(int measureSpec){ int result = defaultSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else if (specMode == MeasureSpec.AT_MOST) { result = Math.min(defaultSize, specSize); } else { result = defaultSize; } return result; }
说明:
MeasureSpec.getSize()会解析MeasureSpec值得到父容器width或者height。
MeasureSpec.getMode()会得到三个int类型的值分别为:EXACTLY ,AT_MOST,UNSPECIFIED。
MeasureSpec.UNSPECIFIED 未指定,故可以设置任意大小。
MeasureSpec.AT_MOST 自定义View可以为任意大小,但是有一个上限。
(3)例子3
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); }
private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text result = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } return result; }
private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaint.ascent(); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text (beware: ascent is a negative number) result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } return result; }
(4)例子4
//负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); //获得父容器,设置测量模式和大小 int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果是warp_content情况下,记录宽和高 int width = 0; int height = 0; int lineWidth = 0;//记录每一行的宽度,width不断取最大宽度 int lineHeight = 0;// 每一行的高度,累加至height int cCount = getChildCount(); for (int i = 0; i < cCount; i++){// 遍历每个子元素 View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec);// 测量每一个child的宽和高 MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();// 得到child的lp // 当前子空间实际占据的宽高 int childWidth = child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin; int childHeight = child.getMeasuredHeight() + lp.topMargin+ lp.bottomMargin; // 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行 if (lineWidth + childWidth > sizeWidth) { width = Math.max(lineWidth, childWidth);// 取最大的 lineWidth = childWidth; // 重新开启新行,开始记录 height += lineHeight; // 叠加当前高度 lineHeight = childHeight; // 开启记录下一行的高度 } else{ // 否则累加值lineWidth,lineHeight取最大高度 lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); } // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较 if (i == cCount - 1){ width = Math.max(width, lineWidth); height += lineHeight; } } setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth: width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight: height); }
(5)例子5
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY){ mWidth = specSize; } else{ int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();//图片决定宽 int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width();//字体决定宽 if (specMode == MeasureSpec.AT_MOST) { int desire = Math.max(desireByImg, desireByTitle); mWidth = Math.min(desire, specSize); } } // 设置高度 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); if (specMode == MeasureSpec.EXACTLY){ mHeight = specSize; } else{ int desire = getPaddingTop() + getPaddingBottom()+ mImage.getHeight() + mTextBound.height(); if (specMode == MeasureSpec.AT_MOST){ mHeight = Math.min(desire, specSize); } } setMeasuredDimension(mWidth, mHeight); }
(6)例子6
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); mRadius = width - getPaddingLeft() - getPaddingRight(); // 获取圆形的直径 mPadding = getPaddingLeft(); // padding值 mCenter = width / 2; // 中心点 setMeasuredDimension(width, width); }
相关文章推荐
- kidd风的IOS日志之UIApplication详解
- 【数据结构与算法分析 】第三章总结
- exchange 2010 初始化失败一例
- 【Java基础学习】DecimalFormat用法
- 【数据结构与算法分析】第一章、第二章总结
- (java)Container With Most Water
- struts2标签与jstl标签的混合使用
- 修复gnome3下chrome鼠标主题不正确的问题
- android 成长日记 6.ListView详解
- JSunpack-n模拟WireShark拦截文件传输
- JSunpack-n模拟WireShark拦截文件传输
- 异常01
- ubuntu12.04安装tftp服务
- 【数据结构与算法分析】第一章学习总结
- ZCTF-Restaurant-Pwn500
- 1107. Social Clusters (30)
- Linux教程:如何查找并移除Ubuntu上陈旧的PPA仓库
- 汇编日记
- 【阶段总结】2015.12——2016.02
- 1106. Lowest Price in Supply Chain (25)