最近一直在通过看上大牛的博客学自定义view,而且面试中也经常问道怎么自定义view,通过一段时间的学习,算是初步掌握了自定义view的步骤和注意事项,所以特此想总结一下目前阶段所学到的关于自定义view的知识;
自定义view三部曲:
一).在values文件夹下创建attr.xml文件,内容如下:
<declare-styleable name="CustomImageView">
<attr name="customimageview" format="reference"/>
</declare-styleable>
这里name一般指定为自定义view的类名,当然也可以是其他任意的字符串,如:test等;然后定义一个属性,这里的属性代表引用一张图片;
format的取值可以为:string,color,demension,integer,enum,reference,float,boolean,fraction,flag,假如属性
定义都为<attr name="attr1" format="xxxtype"/>,那么:
(1) boolean表示布尔值,调用如 xx:attr1="false"
(2) integer表示整型,调用如 xx:attr1="1"
(3) dimension表示尺寸值,调用如 xx:attr1="42dp"
(4) float表示浮点型,调用如 xx:attr1="0.7"
(5) color表示颜色值,调用如 xx:attr1="#00FF00"
(6) string表示字符串,调用如 xx:attr1="#adbddd"
(7) reference表示参考某一资源id,调用如 xx:attr1 = "@drawable/图片ID"
(8) fraction表示百分数,调用如 xx:attr1="30%"
(9)enum:枚举类型
<attr name="ImageScaleType">
<enum name="fitxy" value="0"/>
<enum name="center" value="1"/>
</attr>
(10)flag:表示位或运算
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
调用如:xx:attr1="stateUnspecified | stateUnchanged | stateHidden"
(11) 混合类型,定义为
<declare-styleable name = "combine_type">
<attr name = "background" format = "reference|color" />
</declare-styleable>
调用如 xx:attr1 = "@drawable/图片ID|#DDFF00"
二)、在布局文件中使用定义的属性,在使用之前,需要引入命名空间:
xmlns:custom="http://schemas.android.com/apk/res/com.example.bottomtabline"
然后就可以使用了,如:
<com.example.bottomtabline.CustomImageView
android:id="@+id/id1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0000"
custom:customimageview="@drawable/ic_action_favor_normal"/>
三)、编写一个类继承自view,然后在构造函数中获取定义的属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomImageView);
for(int i=0;i<ta.getIndexCount();i++){
int attr = ta.getIndex(i);
switch(attr){
case R.styleable.CustomImageView_customimageview :
imageview = BitmapFactory.decodeResource(getResources(), ta.getResourceId(attr,0));
break;
}
}ta.recycle();
最后就是重写onDraw方法和onMeasure方法
onMeasure方法就是规定组件的大小,它和MeasureSpec有紧密的联系,而MeasureSpec是由两部分组成,分别为模型mode和大小size;
mode的取值最常用的有两种:MeasureSpec.EXACTLY代表布局文件中宽或者高中的match_parent和固定值;
MeasureSpec.AT_MOST,代表warp_parent;这个时候布局的大小就需要根据实际情况在代码中决定。
最后调用setMeasuredDimension()方法决定组件的大小。只有调用了这个方法之后,getMeasuredWidth()方法才有值,不然就是零。getWidth()(获取组件的大小)和getMeasuredWidth()方法好像一直是相等的,那是因为在onLayout方法中,对这个组件布局的时候,右坐标传入的值就是getMeasuredWidth(),而左坐标为0,两者相减之后还是原来的值,所以它们两个的值是一直相等的。
onDraw方法就是各种直接的画图、画文本的操作
画图片:
protected void onDraw(Canvas canvas) {
rect.left = 0;
rect.right = getMeasuredWidth();
rect.top = 0;
rect.bottom = getMeasuredHeight();
canvas.drawBitmap(imageview, null, rect, mPaint);
}画半弧:
RectF oval = new RectF(xleft,xtop,xright,xbottom); //一个矩形
canvas.drawArc(oval, -90, mProgress, false, mPaint); //画的弧在这个矩形之中 -90代表画弧的起始位置,mProgress代表画的弧有多大
画圆:
drawCircle(float cx, float cy, float radius, Paint paint) cx、cy代表坐标点,radius代表半径
画文本:
drawText(String text, int start, int end, float x, float y, Paint paint)
画矩形:
drawRect(float left, float top, float right, float bottom, Paint paint)
若现在有一个需求,就是使用多次这个自定义的view的组件,然后让其点击不同的组件来实现不同的业务操作,应该怎么办?这里可以在自定义的view的类里面定义一个接口,然后获取不同组件的id,实现这个接口。
自定义的view类里面:
public void setClickListener(ClickListener l){
this.listener = l;
}
public ClickListener getClickListener(){
return listener;
}
public interface ClickListener{
void onClickListener();
}
在Activity中:
write.setClickListener(new ClickListener() {
@Override
public void onClickListener() {//在这里面做具体的业务逻辑,比如调转到指定的activity,或者显示提示信息等
// TODO Auto-generated method stub
Intent intent = new Intent(MainActivity.this,WriteActivity.class);
startActivity(intent);
Toast.makeText(getApplicationContext(), "跳转到编辑界面的Activity", Toast.LENGTH_SHORT).show();
}
});
write.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
write.getClickListener().onClickListener();
}
});
大体自定义view的步骤就这些吧。