package com.example.mybutton;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Button;
public class MyButton extends Button {
private Paint mTextPaint, mPaint;
private String mText;
private int mAscent;
private Shader mShader;
private Matrix mMatrix = new Matrix();// 一个3*3的矩阵,通过该矩阵可以完成平移旋转
private float mStart;//下一次画椭圆的起点
private float mSweep;// 用来表示绘制的角度,也就是扫描(抹掉)的角度
private float mRotate;// 用来表示闪动的频率
private static final float SWEEP_INC = 2;
private static final float START_INC = 15;
/**
* 2个构造函数初始化Paint画笔,以及padding
*
* @param context
*/
public MyButton(Context context) {
super(context);
initLabelView();
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
initLabelView();
}
private final void initLabelView() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xff000000);
setPadding(15, 15, 15, 15);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(4);
mPaint.setAntiAlias(true);// 抗锯齿,图像边缘稍微清晰一点,没有锯齿,是图像的边缘
mPaint.setStyle(Paint.Style.STROKE);
// colors The colors to be distributed between around the
// center,第三个参数表示圆圈的颜色变化,第一个颜色是主要的颜色
// 最后一个颜色会闪动,中间的颜色不显示
mShader = new SweepGradient(this.getMeasuredWidth(),
this.getMeasuredHeight(), new int[] { Color.GREEN,
Color.DKGRAY, Color.BLUE }, null);
mPaint.setShader(mShader);// 设置颤抖效果
}
/**
* setText和setTextSize都可能会改变大小,这就需要requestLayout
* setText和setTextSize、setTextColor调用后都需要重绘
*
* @param text
*/
public void setText(String text) {
mText = text;
requestLayout();
invalidate();
}
public void setTextSize(int size) {
mTextPaint.setTextSize(size);
requestLayout();
invalidate();
}
public void setTextColor(int color) {
mTextPaint.setColor(color);
invalidate();
}
// 自定义视图关键一步,必须调用setMeasuredDimension(int width, int
// height)来存储测量得到的宽度和高度值,如果没有这么去做会触发异常IllegalStateException。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),// dimension:尺寸
measureHeight(heightMeasureSpec));
}
/**
* 获取宽度
*
* @param measureSpec
* @return
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// we were told how big to be.
result = specSize;
} else {
// measure the text
// 这是UNSPECIFIED情况下的result
result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
+ getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {// at most至多的意思
result = Math.min(result, specSize);// 使用的不能超过specSize,如果小于specSize就想用多少用多少
}
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
mAscent = (int) mTextPaint.ascent();
if (specMode == MeasureSpec.EXACTLY) {
// we were told how big to be
result = specSize;
} else {
// Measure the Text(beware: ascent is a negative number)
result = (int) (mTextPaint.descent() - mAscent) + getPaddingTop()
+ getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
Log.v("Measure Height", "At Most Height:" + specSize);
result = Math.min(specSize, result);
}
}
return result;
}
private void drawArcs(Canvas canvas, RectF oval, boolean useCenter,
Paint paint) {
canvas.drawArc(oval, mStart, mSweep, useCenter, paint);// mStart和mSweep都没有初始化就是0咯
}
// onDraw方法的调用后除了开始调用还会在每次的click后调用,还会在invalidate()里面调用,所以invalidate和onDraw一直互相调用
@Override
protected void onDraw(Canvas canvas) {
measure(0, 0);//由于调用过this.getMeasuredWidth所以得先测量下
mMatrix.setRotate(mRotate, this.getMeasuredWidth() / 2,
this.getMeasuredHeight() / 2);// 设置闪动的的频率和中心
mShader.setLocalMatrix(mMatrix);// mShader最后会被设置到mPaint上
mRotate += 10;// 设置闪动的频率,越来越快直到360
if (mRotate >= 360) {
mRotate = 0;
}
RectF drawRect = new RectF();//容纳4个点的容器
drawRect.set(this.getWidth() - mTextPaint.measureText(mText),
(this.getHeight() - mTextPaint.getTextSize()) / 2,
mTextPaint.measureText(mText),
this.getHeight()
- (this.getHeight() - mTextPaint.getTextSize()) / 2);
drawArcs(canvas, drawRect, false, mPaint);// 这里该画笔控制每次画多少逐渐的画完360
mSweep += SWEEP_INC;
if (mSweep > 360) {
mSweep -= 360;
mStart += START_INC;// 每次移动15等24次移动一周,此时已经画了24个椭圆
if (mStart > 360) {
mStart -= 360;
}
}
if (mSweep > 180) {
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
mTextPaint);
}
invalidate();
/*if(b){
b=false;
invalidate();
}*/
}
boolean b=true;
}
上面的Button这样设置到布局文件里面
<com.example.mybutton.MyButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/mybutton1"/>
在主Activity里面:
mButton=(MyButton) findViewById(R.id.mybutton1);
mButton.setText("Custom Button");
mButton.setTextSize(40f);
mButton.setTextColor(Color.RED);
于是我们可以看见一个动态的Button,如何让它慢点或者停止呢,只需要不让onDraw里面持续调用invalidate就就行了
注释掉invalidate,然后取消下面if(){}外面的注释,运行一下,每次点击后就重新invalidate一次,这样就看到慢点的Button了