您的位置:首页 > 移动开发 > Android开发

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

2016-04-08 00:00 851 查看
效果图



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

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

[code=plain]<?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

[code=plain]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()方法

[code=plain]public CustomTitleView(Context context) {
this(context, null);
}
public CustomTitleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

主角构造器:

[code=plain]  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()方法

[code=plain]    /**
* 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());
}

[code=plain]   private int getFontHeight(Paint paint) {
Paint.FontMetrics fm = paint.getFontMetrics();
return (int) (Math.ceil(fm.descent - fm.ascent));
}

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

[code=plain]    @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的整体代码

[code=plain]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));
}
}


布局文件:

[code=plain]<?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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息