Android进阶学习-使用Canvas自定义简单TextView(1)

    效果图

194850_Ie6i_2697209.png

    说起View这个东西,一直都是令人头疼的事情,用起来好使,但有时候系统带的View不够用时,就要自己去编写了,这时候就呵呵了.下面我们来学习一下吧.

    1.首先在res/values下面新建attr.xml,代码:    

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="mTitleText" format="string" />
    <attr name="mTitleTextColor" format="color" />
    <attr name="mTitleTextSize" format="dimension" />

    <declare-styleable name="CustomTitleView">
        <attr name="mTitleText" />
        <attr name="mTitleTextColor" />
        <attr name="mTitleTextSize" />
    </declare-styleable>
</resources>

    其中上面三行声明了一些属性,还有属性所对应的类型

自定义样式 

format是值该属性的取值类型一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;

"reference" //引用,ResourceID

"color" //颜色  

"boolean" //布尔值  

"dimension" //尺寸值  

"float" //浮点值  

"integer" //整型值  

"string" //字符串  

"fraction" //百分数,比如200% 

    declare-stylable声明了一个自定义属性的集合,name是对应的名字.可以想象成declare-stylable是一个箱子,里面放了很多属性.

    1.自定义View

public class CustomTitleView extends View {
    private String mTitleText;
    private int mTitleTextColor;
    private int mTitleTextSize;
    /**
     * 分别对应我们的自定义属性的三个属性值,等下用来接收那些属性值
     */
    private Rect mBonds;
    private Paint mPaint;
    /**
     * Rect是我们要占据的空间,一个矩形嘛
     * Paint是我们用来绘画View的画笔
     */
}

    2.然后是我们的View的主要代码,三个构造器+onMeasure()+onDraw()方法   

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

    主角构造器:

  public CustomTitleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyleAttr, 0);
        /**
         * TypedArray数组用于获取我们刚刚定义的attr属性文件
         */
        /**
         * 通过for循环,来遍历每个设置的属性,并用switch语句判断是哪个属性,用上面三个变量去接收
         */
        int n = ta.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = ta.getIndex(i);
            switch (attr) {
                case R.styleable.CustomTitleView_mTitleText:
                    mTitleText = ta.getString(attr);
                    break;
                case R.styleable.CustomTitleView_mTitleTextColor:
                    mTitleTextColor = ta.getColor(attr, Color.RED);
                    break;
                case R.styleable.CustomTitleView_mTitleTextSize:
                    mTitleTextSize = ta.getDimensionPixelSize(attr,
                            (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    /**
                     * mTitleTextSize把attr获取过来的值转换成对应的dp值
                     */
                    break;
            }
        }
        ta.recycle();
        mPaint = new Paint();
        mBonds = new Rect();
    }

    3.下面是OnMeasure()方法    

    /**
     * MeasureSpec的基础知识(必看):http://my.oschina.net/qiuhoude/blog/410809
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @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(mTitleTextSize);
            width = (int) mPaint.measureText(mTitleText, 0, mTitleText.length());
            /**
             * 该模式下为wrap_content,需要自己去测量View需要占据的空间,通过以上方法结合字体大小即可算出宽度
             */
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            height = getFontHeight(mPaint);
            /**
             * 与宽度类似,但是需要获取字体的高度去测量
             */
        }
        setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
    }
   private int getFontHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return (int) (Math.ceil(fm.descent - fm.ascent));
    }

4.最后就是View的绘制onDraw()了

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        /**
         * 画那个View矩形
         */
        mPaint.setColor(mTitleTextColor);
        mPaint.setTextAlign(Paint.Align.LEFT);  //设置基准线,与下下面drawText的Text绘制位置有很大关系,请看下面链接
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBonds);   //测量字体所占的宽高,赋值给mBounds
        canvas.drawText(mTitleText, getWidth() / 2 - mBonds.width() / 2, getHeight() / 2 + mBonds.height() / 2, mPaint);
        //看http://blog.csdn.net/hursing/article/details/18703599
        super.onDraw(canvas);
    }

 View的整体代码

package com.example.august.customview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
/**
 * Created by August on 16/4/8.
 */
public class CustomTitleView extends View {
    private String mTitleText;
    private int mTitleTextColor;
    private int mTitleTextSize;
    private Rect mBonds;
    private Paint mPaint;
    public CustomTitleView(Context context) {
        this(context, null);
    }
    public CustomTitleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CustomTitleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyleAttr, 0);
        int n = ta.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = ta.getIndex(i);
            switch (attr) {
                case R.styleable.CustomTitleView_mTitleText:
                    mTitleText = ta.getString(attr);
                    break;
                case R.styleable.CustomTitleView_mTitleTextColor:
                    mTitleTextColor = ta.getColor(attr, Color.RED);
                    break;
                case R.styleable.CustomTitleView_mTitleTextSize:
                    mTitleTextSize = ta.getDimensionPixelSize(attr,
                            (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        ta.recycle();
        mPaint = new Paint();
        mBonds = new Rect();
    }
    @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(mTitleTextSize);
            width = (int) mPaint.measureText(mTitleText, 0, mTitleText.length());
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            height = getFontHeight(mPaint);
        }
        setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
    }
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(mTitleTextColor);
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBonds);
        canvas.drawText(mTitleText, getWidth() / 2 - mBonds.width() / 2, getHeight() / 2 + mBonds.height() / 2, mPaint);
        super.onDraw(canvas);
    }
    private int getFontHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return (int) (Math.ceil(fm.descent - fm.ascent));
    }
}

 

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.august.customview.CustomTitleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
        app:mTitleText="Hello world!"
        app:mTitleTextColor="#FF0000"
        app:mTitleTextSize="28sp" />
</RelativeLayout>

参考链接:    http://my.oschina.net/qiuhoude/blog/410809

                http://blog.csdn.net/hursing/article/details/18703599

转载于:https://my.oschina.net/august1996/blog/655391

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值