自定义View一般需要重写onDraw()方法进行界面的绘制
如画一个矩形:
@Override
protected void
onDraw(Canvas canvas) {
super.onDraw(canvas)
;
canvas.drawRect(getLeft()
,getTop()
,getRight()
,getBottom()
,
mPanit)
;
}
其他具体的绘制方法请参见API
在布局中引入自定义的View时,设置layout_width和layout_height时,默认是支持具体值的(如
100p,100px),同时也支持match_parent(其实也是一个具体值,是父布局的高度和宽度),即EXACTLY模式。
如果要支持wrap_content 属性,需要重写onMeasure方法。如果不重写,wrap_content的效果是会填充整个父布局。因为他根本不知道自己的content有多大,所以只能去填充父布局。
通过代码验证一下:
MainActivity布局是一个纵向的LinerLayout ,里面放了一个高为300px的小LinerLayout,背景设为红色,以这个小LinerLayout作为自定义View的父布局。MyTextView是自定义View高度和宽度都设置为wrap_content
<?
xml version=
"1.0"
encoding=
"utf-8"
?>
<LinearLayout
xmlns:
android
=
"http://schemas.android.com/apk/res/android"
android
:layout_width=
"match_parent"
android
:layout_height=
"match_parent"
android
:orientation=
"vertical"
android
:fitsSystemWindows=
"true"
>
<LinearLayout
android
:layout_width=
"match_parent"
android
:layout_height=
"300px"
android
:background=
"#ff0000"
>
<com.example.david.myview.myTextview
android
:layout_width=
"wrap_content"
android
:layout_height=
"wrap_content"
/>
<TextView
android
:layout_width=
"wrap_content"
android
:layout_height=
"wrap_content"
android
:text=
"hello "
/>
</LinearLayout>
</LinearLayout>
MyTextView是一个自定义的View,通过重写onDraw()绘制一个和MyTextView一样大的绿色矩形,重写onMeasure()完成测量,同时支持wrap_content属性(这里用高度进行演示,宽度使用默认)。
public class myTextview
extends View {
Paint
mPanit=
new Paint()
;
public myTextview(Context context) {
super(context)
;
}
public myTextview(Context context
, AttributeSet attrs) {
super(context
, attrs)
;
}
@Override
protected void
onDraw(Canvas canvas) {
super.onDraw(canvas)
;
mPanit.setColor(
0XFF00FF00)
;
canvas.drawRect(getLeft()
,getTop()
,getRight()
,getBottom()
,
mPanit)
;
}
@Override
protected void
onMeasure(
int widthMeasureSpec
, int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth()
, widthMeasureSpec)
,measureHeight(heightMeasureSpec))
;
}
private int
measureHeight(
int heightMeasureSpec) {
int result
;
/**获取测量模式
* wrap_content为 AT_MOST
* match_parent和具体值为 EXACTLY*/
int specMode=MeasureSpec.
getMode(heightMeasureSpec)
;
int specSize=MeasureSpec.
getSize(heightMeasureSpec)
;
if(specMode==MeasureSpec.
EXACTLY){
result=specSize
;
}
else{
/**如果设置了wrap_content 则高度设置为200px 和 父布局 的最小值*/
result =
200
;
if(specMode==MeasureSpec.
AT_MOST){
result=Math.
min(result
,specSize)
;
}
}
return result
;
}
}
我们首先把onMeasure()方法注释掉,即不去重写,看一下效果
MyTextView即使使用了wrap_content,但是因为没有重写onMeasure(),仍然充满了父布局。
取消onMeasure()的注释
看下正常的效果:
MyTextView占据了200px ,父布局300px。
总结:一般重写onMeasure时需要给出对应情况(如内容多少)的具体大小,一般需要根据测量模式进行区分,就是说判断别人在使用这个View的时候使用的是wrap_content,match_parent还是具体值。
补充View的测量模式:
①、
AT_MOST(最大值模式),子元素至多达到指定大小的值。控件使用warp_content时,大小随子空间的变化而变化,
只要不超过父布局的尺寸即可。
②、
EXACTLY(精确模式),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
控件使用具体值(如:100dp,100px),或者match_parent时,使用这种模式
③、
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小,不常见