自定义view的概括在自定义view(一)之概述中已经讲过,不清楚的朋友可以去看看,接下来讲解自定义view主要实现方式中的一种绘制view。

一、View绘制基本流程
    1、了解绘制view的基本方法。
    2、思考自己自定义View需要用到的属性

二、基本方法介绍
    基本方法由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法
1、measure()方法
    在view中定义为final类型,要求子类不能修改。measure操作主要用于计算视图的大小,即视图的宽度和长度。measure()函数中又会调用onMeasure()。
    onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个封装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout()方法
    在view中定义为final类型,要求子类不能修改。layout操作用于设置视图在屏幕中显示的位置。layout()函数中有两个重要方法setFrame(l,t,r,b)和onLayout()。
    setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来
   onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw()方法
    在view中定义为final类型,要求子类不能修改,其内部定义了绘图的基本操作。draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。该函数中有个重要的方法onDraw()。
    注意:view中onDraw()是个空函数,具体的视图都要覆写该函数来实现自己的显示。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容”的,其包含了多个子view,而子view已经实现了自己的绘制方法,ViewGroup只需要告诉子view绘制自己就可以了,也就是的dispatchDraw()(绘制子视图)方法;dispatchDraw()在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法。

三、示例
1.自定义view属性。首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="text" format="string" />
    <attr name="textColor" format="color" />
    <attr name="textSize" format="dimension" />

    <declare-styleable name="DefineTextView">
        <attr name="text" />
        <attr name="textColor" />
        <attr name="textSize" />
    </declare-styleable>

</resources>

定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;

2.在View的构造方法中,获得我们的自定义的样式

public class DefineTextView extends View {

    private String mText;
    private int mTextColor;
    private int mTextSize;

    /**
     * 绘制时控制文本绘制的范围
     */
    private Rect mRect;
    private Paint mPaint;

    public DefineTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DefineTextView(Context context) {
        this(context, null);
    }

    public DefineTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DefineTextView, defStyleAttr, 0);

        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.DefineTextView_text:
                    mText = typedArray.getString(attr);
                    break;
                case R.styleable.DefineTextView_textSize:
                    mTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 18, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.DefineTextView_textColor:
                    mTextColor = typedArray.getColor(attr, Color.BLACK);
                    break;
            }

        }
        typedArray.recycle();

        /**
         * 获得绘制文本的宽和高
         */
        mPaint = new Paint();
        // mPaint.setColor(mTextColor);
        mPaint.setTextSize(mTextSize);
        mRect = new Rect();
        mPaint.getTextBounds(mText, 0, mText.length(), mRect);

    }

    //重写onDraw,onMesure调用由系统提供
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.GREEN);
        canvas.drawRect(10, 10, getMeasuredWidth(), getMeasuredHeight(), mPaint);

        mPaint.setColor(mTextColor);
        canvas.drawText(mText, getWidth()/3, getHeight()/3 + mRect.height()/3, mPaint);
    }

3.然后在layout布局中声明自定义View

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <demo.view.cn.viewtest.DefineTextView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:text="@string/hello_world"
        custom:textColor="@android:color/black"
        custom:text="我是小蜜蜂"
        custom:textSize="10dp" />

</RelativeLayout>

注意:记得要引入xmlns:custom=”http://schemas.android.com/apk/res-auto”。

运行效果:

android view 设置视图 android view的绘制_自定义view

如上图,我并没有对onMesure方法进行操作,系统自动实现的测量高度和宽度都是MATCH_PARNET,当我们需要设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。当设置了WRAP_CONTENT时,就需要自己进行测量,这时就需要重写onMesure方法。

代码如下:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height ;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if(widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else{
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mText, 0, mText.length(), mRect);
            float textWidth = mRect.width();
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }

        if (heightMode == MeasureSpec.EXACTLY)
        {
            height = heightSize;
        } else
        {
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mText, 0, mText.length(), mRect);
            float textHeight = mRect.height();
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }
        setMeasuredDimension(width, height);
    }

MeasureSpec的specMode,共有三种类型:
EXACTLY:MATCH_PARENT或者是一般是设置了明确的值
AT_MOST:一般为WARP_CONTENT,表示子布局限制在一个最大值内
UNSPECIFIED:较少使用。表示子布局想要多大就多大。

xml修改如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <demo.view.cn.viewtest.DefineTextView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:text="@string/hello_world"
        custom:textColor="@android:color/black"
        android:layout_centerInParent="true"
        custom:text="我是小蜜蜂"
        custom:textSize="10dp" />

</RelativeLayout>

运行效果:

android view 设置视图 android view的绘制_xml_02

现在我们可以对高度、宽度进行随便的设置了,符合我们的预期。

好了。整个自定义view基本完毕了,大家也可以根据自己的需求添加一些事件。