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
    评论
为了满足广大Android开发爱好者与从业者的学习需求,我们精心整理并上传了一份全面而实用的Android项目资源包。这份资源包内容丰富,涵盖了从基础知识到实战应用的全方位内容,旨在为开发者们提供一个便捷、高效的学习平台。 一、文件手册 资源包中的文件手册部分,详细记录了Android开发的核心知识点和常用技术。无论是初学者还是有一定经验的开发者,都能从中找到所需的学习资料。手册采用了简洁明了的排版方式,使得查阅更加方便快捷。同时,手册内容深入浅出,既适合新手入门,也能为老手提供有价值的参考。 二、项目实战与练习 为了让学习者能够将理论知识与实践相结合,我们特别准备了项目实战与练习部分。这部分内容包含了多个精心设计的Android项目案例,从需求分析、设计思路到实现过程,都有详细的讲解和代码示例。学习者可以通过实际操作,深入了解Android开发的整个流程,提升自己的实战能力。 此外,我们还提供了一系列练习题,旨在巩固所学知识,检验学习成果。这些练习题既有基础题,也有难度较高的挑战题,适合不同层次的学习行练习。 三、Android开发工具集 在Android开发过程中,选择合适的工具能够大大提高开发效率。因此,我们整理了常用的Android开发工具集,包括开发工具、测试工具、性能优化工具等。这些工具都是经过我们精心筛选和测试的,能够帮助开发者们更加高效地Android开发工作。 总的来说,这份Android项目资源包是一份不可多得的学习资料,无论你是初学者还是有一定经验的开发者,都能从中受益匪浅。我们希望通过这份资源包,为广大Android开发爱好者与从业者提供一个更加便捷、高效的学习平台,共同推动Android开发领域的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值