最近一直在通过看上大牛的博客学自定义view,而且面试中也经常问道怎么自定义view,通过一段时间的学习,算是初步掌握了自定义view的步骤和注意事项,所以特此想总结一下目前阶段所学到的关于自定义view的知识;

android 自定义 view 求view的宽高 android自定义view流程_android

自定义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的步骤就这些吧。