最近在看徐老师的《Android群英传》,里面有一章是讲的如何自定义View,之前一直觉得自定义挺复杂的,感觉像座大山一直压在自己的Android之路上,看了书中讲解之后,发现原来一直神秘的自定义View也没那么神秘,下面就和我一起来通过一个简单的小Demo一起来体验一下吧。
重写TextView实现一个简单的动态渐变效果的TextView
首先讲一下思路和知识准备,实现渐变需要使用到一个渲染对象LinearGradient,只有一个渲染对象还不行,因为这样知识一个静态的效果,要实现动态还需要一个矩阵位移对象Matrix。
1、首先我们重写View的onSizeChanged方法,在里面执行一些初始化的操作,比如为TextView的Paint画笔对象设置渲染对象等,代码如下:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 初始化操作
if (mViewWidth == 0) {
// 设置自定义textview的宽度值
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0) {
// 获取到TextView的paint对象
mPaint = getPaint();
// 设置自定义的线性渲染
// CLAMP重复最后一个颜色至最后
// MIRROR重复着色的图像水平或垂直方向已镜像方式填充会有翻转效果
// REPEAT重复着色的图像水平或垂直方向
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{colorOne, colorTwo, colorThree}, null, Shader.TileMode.CLAMP);
// 为textview的paint设置渲染器
mPaint.setShader(mLinearGradient);
// 初始化全局矩阵
mGradientMatrix = new Matrix();
}
}
}
2、重写onDraw方法,在里面实现动态的渐变效果,代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
// 每次执行一次,就将偏移增加一个值
mTranslate += mViewWidth / 5;
Log.e("gu", "mTranslate==>" + mTranslate);
// 如果偏移值大于了两倍的textview宽度,则将偏移值置为-mViewWidth
if (mTranslate > 2 * mViewWidth) {
mTranslate = -mViewWidth;
}
// 为矩阵设置偏移
mGradientMatrix.setTranslate(mTranslate, 0);
// 为渲染器加上偏移矩阵
mLinearGradient.setLocalMatrix(mGradientMatrix);
// 设置延迟重新提交绘制View
postInvalidateDelayed(delayTime);
}
}
3、我们有时也需要自定义一些属性,使我们的控件用起来更加的人性化,这个其实也简单,首先在values目录下新建一个attrs.xml文件,然后在里面自定义一些自定义控件想要的属性:
<declare-styleable name="CustomTextViewTwo">
<attr name="shaperColorOne" format="color"></attr>
<attr name="shaperColorTwo" format="color"></attr>
<attr name="shaperColorThree" format="color"></attr>
<attr name="delayTime" format="integer"></attr>
</declare-styleable>
4、设置好上面的属性列表之后,就可以在布局文件中使用上面的参数了,只不过要先加一个命名空间xmlns:app="http://schemas.android.com/apk/res-auto"
,这样编译器就可以找到我们自己定义的属性了:
<com.gu.customviewtest.CustomTextViewTwo
android:layout_width="100dp"
android:layout_height="50dp"
android:gravity="center"
android:text="我是测试的文字"
app:delayTime="1000"
app:shaperColorOne="#cc0e0e"
app:shaperColorThree="#cc0e0e"
app:shaperColorTwo="#f6d605" />
5、现在万事具备,只欠东风了,我们还要在自定义View的构造器中,获取到上面xml布局中设置的这些属性值:
private void init(Context context, AttributeSet attrs) {
//获取到ta对象
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomTextViewTwo);
//从ta对象中,获取到xml中对象属性配置值
colorOne = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorOne, Color.BLACK);
colorTwo = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorTwo, Color.BLACK);
colorThree = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorThree, Color.BLACK);
delayTime = ta.getInteger(R.styleable.CustomTextViewTwo_delayTime, 100);
//TypeArray 对象用完以后必须得回收
ta.recycle();
}
6、至此,整个一个简单的自定义View代码就实现了,下面我贴一下完整的代码供大家参考:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
/**
* Created by Nate on 2015/10/22.
*/
public class CustomTextViewTwo extends TextView {
private int mViewWidth = 0;
private Paint mPaint;
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private int mTranslate;
private int colorOne;
private int colorTwo;
private int colorThree;
private int delayTime;
/**
* 在java代码创建视图的时候被调用,如果是从xml填充的视图,就不会调用这个
*
* @param context
*/
public CustomTextViewTwo(Context context) {
super(context);
}
/**
* 这个是在xml创建但是没有指定style的时候被调用
*
* @param context
* @param attrs
*/
public CustomTextViewTwo(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
/**
* 这个是在xml创建,并且指定style的时候被调用
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomTextViewTwo(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//获取到ta对象
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomTextViewTwo);
//从ta对象中,获取到xml中对象属性配置值
colorOne = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorOne, Color.BLACK);
colorTwo = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorTwo, Color.BLACK);
colorThree = ta.getColor(R.styleable.CustomTextViewTwo_shaperColorThree, Color.BLACK);
delayTime = ta.getInteger(R.styleable.CustomTextViewTwo_delayTime, 100);
//TypeArray 对象用完以后必须得回收
ta.recycle();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 初始化操作
if (mViewWidth == 0) {
// 设置自定义textview的宽度值
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0) {
// 获取到TextView的paint对象
mPaint = getPaint();
// 设置自定义的线性渲染
// CLAMP重复最后一个颜色至最后
// MIRROR重复着色的图像水平或垂直方向已镜像方式填充会有翻转效果
// REPEAT重复着色的图像水平或垂直方向
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{colorOne, colorTwo, colorThree}, null, Shader.TileMode.CLAMP);
// 为textview的paint设置渲染器
mPaint.setShader(mLinearGradient);
// 初始化全局矩阵
mGradientMatrix = new Matrix();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
// 每次执行一次,就将偏移增加一个值
mTranslate += mViewWidth / 5;
Log.e("gu", "mTranslate==>" + mTranslate);
// 如果偏移值大于了两倍的textview宽度,则将偏移值置为-mViewWidth
if (mTranslate > 2 * mViewWidth) {
mTranslate = -mViewWidth;
}
// 为矩阵设置偏移
mGradientMatrix.setTranslate(mTranslate, 0);
// 为渲染器加上偏移矩阵
mLinearGradient.setLocalMatrix(mGradientMatrix);
// 设置延迟重新提交绘制View
postInvalidateDelayed(delayTime);
}
}
}