Android自定义View之绘制、测量

自定义View对于很多新手来说都是谈之色变,当然我也不例外,但是在某些情况下需要更加炫酷的效果,更加人性化的体验还是不得不自己去撸一些特定的View出来。自定义View是我们进阶路上的拦路虎,更是我们进阶必备的技能之一。本文主要是记录自身的学习,同时指导新手的学习,高手请避让撸代码去睡觉。。。

本文就从自定义最基础的部分开始聊起。

自定义View包含的主要内容如下:

1)自定义属性。

2)测量(onMeasure)、绘制(onDraw)。

下面看下主要实现的效果(实现中间显示文字的View,类似于TextView,可设置字体颜色,字体大小,显示内容等等):

实现过程如下:

1)继承自View,重写其构造方法:

如下:

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

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

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

2)自定义属性如下:

1,在values目录下创建attrs.xml文件

内容如下(也可将上面的定义直接写在下面的标签内):

    <attr name="text" format="string"/>
    <attr name="textColor" format="color"/>
    <attr name="textSize" format="dimension"/>


    <declare-styleable name="DemoTextView01">
        <attr name="text"/>
        <attr name="textColor"/>
        <attr name="textSize"/>
    </declare-styleable>
     2,在自定义View中获取相关的属性值如下(直接在初始化的时候完成,即构造器中):

 	// 获取到我们自定义的属性值
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DemoTextView01,defStyleAttr,0);
        // 遍历取到相关的值
        for (int i = 0; i < array.getIndexCount(); i++) {
            //取到相关属性的ID值,根据Id匹配取到相关的值
            int attr = array.getIndex(i);
            switch (attr){
                case R.styleable.DemoTextView01_text://指定的属性对应的ID值写法:R.styleable.类名_属性名
                    mText=array.getString(attr);
                    break;
                case R.styleable.DemoTextView01_textColor:
                    //取到设置的字体颜色的值,默认的字体颜色为黑色
                    mTextColor=array.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.DemoTextView01_textSize:
                    //设置默认值为16sp   TypeValue是对sp和dp进行相互的转换(各种尺寸的转换)
                    mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
                default:
                    break;

            }
        }
        array.recycle();

3)初始化画笔及绘制范围如下(也在构造器中完成):

  	mPaint = new Paint();
        mPaint.setTextSize(mTextSize);

        mBound = new Rect();
        mPaint.getTextBounds(mText,0,mText.length(),mBound);

4)重写onDraw方法进行绘制如下:

  @Override
    protected void onDraw(Canvas canvas) {

        //设置底色为灰色 并进行绘制
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);

        //设置字体颜色为设置的字体颜色,并进行内容的绘制
        mPaint.setColor(mTextColor);
        canvas.drawText(mText,getWidth()/2-mBound.width()/2,getHeight()/2+mBound.height()/2, mPaint);
    }
5)重写onMeasure进行测量(重点)如下:

Android控件测量MeasureSpec共三种模式,如下:

EXACTLY:指的是设置了明确的值或者是MATCH_PARENT

AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:不限制,很少使用

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        }else {
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mText,0,mText.length(),mBound);
            float textWidth = mBound.width();
            int desire = (int)(getPaddingLeft()+textWidth+ getPaddingRight());
            width = desire;
        }
        if (heightMode == MeasureSpec.EXACTLY){
            height  = heightSize;
        }else {
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mText,0,mText.length(),mBound);
            float textHeight = mBound.height();
            int desire = (int)(getPaddingTop()+textHeight+ getPaddingBottom());
            height = desire;
        }
        setMeasuredDimension(width,height);
    }
注意:此步骤非常重要如果缺失,则在宽度高度为wrap_content的时候会铺满整个屏幕,如下:



到此为止,自定义控件已经完成了。

自定义控件的使用如下:

xml文件内容:

<?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:text="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jinjin.viewstudy.viewstudy.MainActivity">


    <com.jinjin.viewstudy.viewstudy.view.DemoTextView01
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
        text:text="自定义View的内容"
        text:textColor="#ff0000"
        text:textSize="30sp"
        />


</RelativeLayout>
注意:红色字体部分为引入命名控件,缺失则不能正常使用。

到此即可实现上述的显示效果。


源码下载:Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值