android自定义控件(一) 入门

来源:互联网 发布:韩国网友评价lz知乎 编辑:程序博客网 时间:2024/06/15 10:35

转自:鸿洋的博客,正在学习,非常感谢!
自定义View的步骤:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

[ 3、重写onMesure ]

4、重写onDraw

我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。
1.自定义View的属性,首先在res/values/ 下建立一个styleable.xml , 在里面定义我们的属性和声明我们的整个样式。

<?xml version="1.0" encoding="utf-8"?><resources>    <!--定义一些基本的属性-->    <attr name="textTitle" format="string"/>    <attr name="textColor" format="color"/>    <attr name="textSize" format="dimension"/>    <declare-styleable name="CustomeView">        <attr name="textTitle"/>        <attr name="textColor"/>        <attr name="textSize"/>    </declare-styleable></resources>

我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:

一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;

2.然后在布局中声明我们的自定义View:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.down.customeviewdemo_1.MainActivity">    <com.example.down.customeviewdemo_1.CustomeView        android:id="@+id/img"        android:layout_width="100dp"        android:layout_height="100dp"        app:textTitle="哈哈哈哈哈哈哈哈"        app:textColor="#009933"        app:textSize="10sp"        /></RelativeLayout>

**一定要引入 xmlns:custom=”http://schemas.android.com/apk/res/res-auto”我们的命名空间,后面的包路径指的是项目的package或者res-auto
**
3.自定义view代码:

  //画笔    Paint mPaint =null;    //可视区域    Rect mRect =null;    //自己定义的基本属性    private  int textSize;    private String textTitle;    private  int textColor;    public void setTextColor(int textColor) {        this.textColor = textColor;    }    public void setTextSize(int textSize) {        this.textSize = textSize;    }    public void setTextTitle(String textTitle) {        this.textTitle = textTitle;    }    public CustomeView(Context context) {        this(context,null);    }    public CustomeView(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public CustomeView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //获取styleable.xml中定义的基本属性        TypedArray a=context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomeView,defStyleAttr,0);        int  n=a.getIndexCount();        for (int i=0;i<n;i++){            int attr=a.getIndex(i);            switch (attr){                case R.styleable.CustomeView_textTitle:                    textTitle=a.getString(attr);                    break;                case R.styleable.CustomeView_textColor:                    textColor=a.getColor(attr, Color.BLACK);//默认黑色字体                    break;                case R.styleable.CustomeView_textSize://这里用的是px                    textSize=a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));//默认16sp                    break;            }        }        a.recycle();//释放回收        //基本初始化        mPaint =new Paint();        mRect =new Rect();        mPaint.setTextSize(textSize);        mPaint.getTextBounds(textTitle, 0, textTitle.length(), mRect);    }    //计算控件的绘制位置    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);            }    //绘制控件    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPaint.setColor(textColor);        //绘制一个rectangle显示text        canvas.drawRect(0, 0, getMeasuredWidth(),getMeasuredHeight(), mPaint);        //在onMeasure没有做处理的时候,+getWidth()+"-"+getMeasuredWidth()是相等的//        Log.i("yqy", "" + getWidth() + "-" + getMeasuredWidth() + "---" + getHeight() + "," + getMeasuredHeight());        //绘制text        mPaint.setColor(Color.WHITE);        Log.i("yqy","要绘制的文本=="+textTitle+","+textSize+","+textColor+","+getWidth()+","+getHeight());        canvas.drawText(textTitle, getWidth() / 2-mRect.width()/2 , getHeight() / 2 + mRect.height() / 2, mPaint);    }

现在的效果是:
这里写图片描述
很明显文字不是在中间位置,原因是:

         mPaint.setTextSize(textSize);这句话的位置,在ondraw里面就是这个样子,放在构造函数中就是显示中间位置1.构造函数中:        mPaint.setTextSize(textSize);        mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);         Log.i("yqy",mRect.width()+"----"+getWidth());//315----7682.构造函数中:  mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);        mPaint.setTextSize(textSize);        Log.i("yqy",mRect.width()+"----"+getWidth());//95----7683.onDraw()中是一样的结果       Log.i("yqy",mRect.width()+"----"+getWidth());//95----768 总结:大小一定要在ondraw方法之前就计算好

当我们在xml中将width或者height设置成warp_content时会全屏铺展,感觉是match_parent的效果
这时的处理是在onMeasure中重新计算要绘制的宽高
首先要了解的是,我们怎么知道用户设置的width是什么样子的?
这个可以通过一个类MeasureSpec的specMode来判断
MeasureSpec的specMode有三种类型:
EXACTLY:一般是设置了明确的值或者Match_parent
AT_MOST:表示子布局限制在一个最大值内,一般是wrap_content布局
UNSPECIFIED:表示子布局想要多大就有多大,很少使用

所以当用户设置match_parent的时候就让其自动显示,除此之外我们来设置它要绘制的范围;
所以重写onMeasure:

 int widthSise=MeasureSpec.getSize(widthMeasureSpec);        int heightSize=MeasureSpec.getSize(heightMeasureSpec);        int widthMode=MeasureSpec.getMode(widthMeasureSpec);        int heightMode=MeasureSpec.getMode(heightMeasureSpec);        //最终要显示的宽和高        int width;        int height;        if(widthMode==MeasureSpec.EXACTLY){            width=widthSise;        }else{//自定义            mPaint.setTextSize(textSize);            mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);            width=getPaddingLeft()+mRect.width()+getPaddingRight();        }        if(heightMode==MeasureSpec.EXACTLY){            height=heightSize;        }else{            mPaint.setTextSize(textSize);            mPaint.getTextBounds(textTitle,0,textTitle.length(),mRect);            height=getPaddingTop()+mRect.height()+getPaddingBottom();        }        //这句话一定要写        setMeasuredDimension(width,height);

最终效果如下:
这里写图片描述

0 0
原创粉丝点击