【Android】简易自定义控件流程

android自定义组件一般有三种实现方式:
一、组合控件:组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。
二、自绘控件: 何为自绘控件,就是完全用Paint和canvas画出来的,就是在onDraw()方法里面绘画,在onMeasure()方法里面进行测量,如果是容器在onLayout()方法中定位每个子组件。
三、继承控件: 就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。
自绘控件也分两种,自定义组件自定义容器,自定义组件是继承View类,自定义容器时继承ViewGrounp;今天主要分析下自定义组件;还是举个例子来的实际些,假如我们要画一个最简单的TextView,首先想到的就是canvas.drawText()方法,怎么画了?还是得一步一步来:
(1) )写一个MyTextView类继承View,重写三个构造方法,当然还有onDraw()和onMeasure()方法,如下代码:

public class MyTextView extends View{
/**
* 文本颜色
*/
private int textColor;
/**
* 字体大小
*/
private float textSize;
/**
* 文本
*/
private String text;

/**
* 绘制时控制
*/
private Paint mPaint;
private Rect mBoud;

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

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

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

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

(2) 接下来就要想既然是TextView,那肯定是有text,color,textSize等属性。在values目录下的attrs.xml中定义好这些属性,以便在xml中引入MyTextView能够直接操作这些值,然后在有三个参数的构造方法里面将这些属性与控件关联,如下:

attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="textColor" format="color|reference"/>
<attr name="textSize" format="dimension|reference"/>
<attr name="text" format="string|reference"/>
</declare-styleable>
</resources>
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
	super(context, attrs, defStyleAttr);
	TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.MyTextView, 0, 0);
	textColor = typedArray.getColor(R.styleable.MyTextView_textColor, 0);
	textSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_textSize, 0);
	text = typedArray.getString(R.styleable.MyTextView_text);
	typedArray.recycle();
}

(3) 已经定义好了属性,那接下来在构造方法里用Paint画笔设置好这些属性,然后用onDraw方法里面的canvas画一个textview,如下:

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
	super(context, attrs, defStyleAttr);
	TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyTextView, 0, 0);
	textColor = typedArray.getColor(R.styleable.MyTextView_textColor, 0);
	textSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_textSize, 0);
	text = typedArray.getString(R.styleable.MyTextView_text);
	typedArray.recycle();

 

	mPaint = new Paint();
	// 设置画笔为抗锯齿
	mPaint.setAntiAlias(true);
	mPaint.setColor(textColor);
	mPaint.setTextSize(textSize);
	//获取绘制文本的宽和高
	mBoud = new Rect();
	mPaint.getTextBounds(text, 0, text.length(), mBoud);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	int width = measureWidth(widthMeasureSpec);
	int height = measureHeight(heightMeasureSpec);
	setMeasuredDimension(width, height);
}

private int measureWidth(int widthMeasureSpec){
	int mode = MeasureSpec.getMode(widthMeasureSpec);
	int size = MeasureSpec.getSize(widthMeasureSpec);
	int width = 0;

	if(mode == MeasureSpec.EXACTLY){
	//如果是math_parent或确定尺寸
		width = size;
	}else if(mode == MeasureSpec.AT_MOST){
	//如果是wrap_parent
		width = getPaddingLeft() + mBound.width() + getPaddingRight();
	}
	return width;
}

private int measureHeight(int heightMeasureSpec){
	int mode = MeasureSpec.getMode(heightMeasureSpec);
	int size = MeasureSpec.getSize(heightMeasureSpec);
	int height = 0;
	if(mode == MeasureSpec.EXACTLY){
	//如果是math_parent或确定尺寸
		height = size;
	}else if(mode == MeasureSpec.AT_MOST){
		//如果是wrap_parent
		height = getPaddingTop() + mBound.height() + getPaddingBottom();
	}
	return height;
}

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	//canvas.drawText(text, x, y, paint); //第一个参数是文本,第四个参数是画笔,第二个参数x默认是字符串的左边在屏幕的位置, 第三个参数y是这个字符文本baseline基线在屏幕上的位置,不是这个字符的中心在屏幕的位置
	Log.v("MyTextView", "getWidth:"+getWidth()/2);
	Log.v("MyTextView", "mBoud.width:"+mBoud.width()/2);
	Log.v("MyTextView", "getHeight"+getHeight()/2);
	Log.v("MyTextView", "mBoud.height:"+mBoud.height()/2);
	Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
	int baseline = (getMeasuredHeight() / 2) - ((fontMetrics.bottom + fontMetrics.top) / 2);
	Log.v("MyTextView", "baseline:"+baseline);
	Log.v("MyTextView", "getMeasuredHeight:"+getMeasuredHeight());
	Log.v("MyTextView", "fontMetrics.top:"+fontMetrics.top);
	Log.v("MyTextView", "fontMetrics.bottom:"+fontMetrics.bottom);
	Log.v("MyTextView", "fontMetrics.ascent:"+fontMetrics.ascent);
	Log.v("MyTextView", "fontMetrics.descent:"+fontMetrics.descent);
	canvas.drawText(text, getWidth()/2 - mBoud.width()/2, baseline, mPaint);
}

说明一下onMeasure方法

1.首先要了解测量组件的宽高时,是由组件的父容器调用的,首先容器他会遍历容器内所有的子组件,一个一个的测量宽高,这里的我们自定义textView的父容器就是最外层RelativeLayout
2.然后要了解MeasureSpec类,这个类为我们提供了两个方法,getMode(int measureSpec)和getSize(int measureSpec)可以获得宽和高的尺寸和模式,这个类还为我们提供了三个常量MeasureSpec.EXACTLY、MeasureSpec.AT_MOST、MeasureSpec.UNSPECIFIED;
MeasureSpec.EXACTLY:已经为TextView指定确定尺寸或者为math_parent时
MeasureSpec.AT_MOST:当TextView为wrap_content时
MeasureSpec.UNSPECIFIED:TextView未定义宽高

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页