概述
上一篇讲解了自定义控件初始化的相关步骤,本篇来讲解自定义控件的宽高测量。
重写onMeasure方法
我们从布局文件上获取到了相关的参数,现在需要测量这个控件的宽高,重写如下方法。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
先来讲讲widthMeasureSpec 和 heightMeasureSpec。
我们在布局文件设置的layout_width 和 layout_height尺寸数据分别被封装到widthMeasureSpec 和 heightMeasureSpec里面了。它由两部分组成。
int是32的,其中0 ~ 29位封装了具体的尺寸值(像素个数),30 ~ 31位封装了模式。
模式分为3种,EXACTLY,AT_MOST 和 UNSPECIFIED
EXACTLY:使用具体的尺寸值(如10dp)或match_parent
AT_MOST:使用wrap_content
UNSPECIFIED:父布局不对本控件尺寸作要求,可以参考这篇文章
举两个例子。
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hello world"/>
<Button
android:layout_width="100px"
android:layout_height="50px"/>
在onMeasure里,TextView的宽度为0,宽度模式为EXACTLY,高度为某个固定值,高度模式为AT_MOST。
而Button的宽度为100,宽度模式为EXACTLY,高度为50,高度模式也为EXACTLY。
通过这两个例子,我们能明白EXACTLY和AT_MOST的区别了。那么该怎么获取尺寸和模式呢?可以通过MeasureSpec.getSize方法 和 MeasureSpec.getMode方法获得
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
然后根据需求来决定控件宽高,如下是一个常用的模板。
int widht;
int height;
if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
//宽高都为wrap_content
width = ...
height = ...
...
}else if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.AT_MOST){
//宽为具体尺寸或match_parent,高为wrap_content
width = ...
height = ...
...
}else if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.EXACTLY){
//宽高为具体尺寸或match_parent
width = ...
height = ...
...
}else{
width = ...
height = ...
...
}
计算好了宽高值后把他们调用setMeasuredDimension方法就可以完成尺寸的初步测量了,如下。
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width,widthMode),MeasureSpec.makeMeasureSpec(height,heightMode));
为什么说是初步测量呢?因为onMeasure只是量出控件自己的宽高,如果是在写自定义容器的话还得考虑子控件的宽高,以及它们的在容器里的摆放位置(这里需要重写onLayout方法),不过由于WaveLoadingView不是容器所以可以不用考虑这么多。
最后来看看WaveLoadingView的onMeasure做了什么吧。
WaveLoadingView的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int elementWidth = 0;
int elementHeight= 0;
switch(mType){
case IMAGE_TYPE_TEXT:
elementWidth = mTextWidth;
elementHeight = mTextHeight;
break;
case IMAGE_TYPE_CIRCLE:
case IMAGE_TYPE_SQUARE:
case IMAGE_TYPE_RECT:
case IMAGE_TYPE_NOISE:
elementWidth = elementHeight = mImageSize;
break;
case IMAGE_TYPE_CUSTOM:
elementWidth = elementHeight = mImageSize;
break;
}
onMeasureSelf(widthMeasureSpec,heightMeasureSpec,elementWidth,elementHeight);
}
首先,这里获取了WaveLoadingView的显示模式,根据模式决定元素的宽高。这里的元素指小方块或字符,每一个小方块或字符就是一个元素。
接着看看onMeasureSelf方法做了什么。
protected void onMeasureSelf(int widthMeasureSpec, int heightMeasureSpec,int elementWidth,int elemwntHeight){
//1
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//2
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
//3
int wrapWidth = mLength * elementWidth +
(mLength - 1) * mInterval +
paddingLeft + paddingRight;
//4
int waveHeight = (mWaveLength % 2) != 0 ?
(int) (elemwntHeight + (mWaveLength / 2 + 1) * elemwntHeight * mWaveOffset) :
(int) (elemwntHeight + mWaveLength / 2 * elemwntHeight * mWaveOffset);
int wrapHeight = waveHeight + paddingTop + paddingBottom;
if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
width = wrapWidth;
height = wrapHeight;
}else if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.AT_MOST){
height = wrapHeight;
}else if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.EXACTLY){
width = wrapWidth;
}
//5
setMeasuredDimension(width,height);
}
WaveLoadingView在使用的时候推荐宽高都写wrap_content。
注释1下面的四行代码获取宽高值及它们的模式,上文刚刚讲过。
注释2下面的四行代码获取到控件的内间距,也就是布局文件里的android:paddingRight,android:paddingLeft,android:paddingTop及android:paddingBottom,单位是像素。
代码3计算wrap_content下的宽度,mLength是指元素个数,计算的各尺寸可以看看下面的示意图。
代码4计算wrap_content下的高度,这里解释一下,mWaveLength指波的占用的元素个数,mLength指显示的元素总数,下图mWaveLength=5,mLength=11。
完成了计算最后在代码5设置计算结果onMeasure就完成了。
最后
限于文章篇幅,文章只贴了关键代码,想深入了解此控件测量流程的朋友可以到Github项目上阅读完整代码。
下一篇讲解控件的绘制及动画效果的实现原理,感兴趣的朋友可以看看。
最后
以上就是贤惠乐曲为你收集整理的波形自定义控件(三):原理解析之测量宽高的全部内容,希望文章能够帮你解决波形自定义控件(三):原理解析之测量宽高所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复