自定义View三部曲:onMeasure()\onDraw()\onTouch()
一、实现分析
1.1 固定不动的大圆弧 color borderWidth
1.2 可以变化的小圆弧 color borderWidth
1.3 中间的步数文字 coloe textSIze
自定义View,为了调用者方便可以使用自定义属性
二、实现步骤:
2.1 自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="QQTepView">
<attr name="outerColor" format="color"/>
<attr name="indoorColor" format="color"/>
<attr name="borderWidth" format="dimension"/>
<attr name="mTextSize" format="dimension"/>
<attr name="stepColor" format="color"/>
</declare-styleable>
</resources>
2.2 使用属性
<com.example.qqview.QQTepView
android:id="@+id/qqTepView"
android:layout_width="match_parent"
android:layout_height="366dp"
app:borderWidth="30dp"
app:indoorColor="@color/design_default_color_error"
app:mTextSize="15sp"
app:outerColor="@color/design_default_color_primary"
app:stepColor="@color/design_default_color_error">
</com.example.qqview.QQTepView>
2.3 通过构造函数获取属性值、初始化画笔
public QQTepView(Context context) {
this(context,null);
}
public QQTepView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public QQTepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public QQTepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.QQTepView);
indoorColor = typedArray.getColor(R.styleable.QQTepView_indoorColor,indoorColor);
outColor = typedArray.getColor(R.styleable.QQTepView_outerColor,outColor);
stepColor = typedArray.getColor(R.styleable.QQTepView_stepColor,stepColor);
//设计到字体大小的需要进行类型强转,dp————》sp,这里没有进行强转
mTextSize = typedArray.getDimensionPixelSize(R.styleable.QQTepView_mTextSize,mTextSize);
borderWidth = typedArray.getDimensionPixelSize(R.styleable.QQTepView_borderWidth,borderWidth);
typedArray.recycle();
//1.分析效果
//2.确定自定义属性 编写attrs.xml
//3.在布局中使用
//4.在自定义View中获取属性
//5.onMeasure():确定 宽高
//6画图
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(outColor);
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
indoorpaint = new Paint();
indoorpaint.setStrokeWidth(borderWidth);
indoorpaint.setColor(indoorColor);
indoorpaint.setStyle(Paint.Style.STROKE);
indoorpaint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(stepColor);
textPaint.setTextSize(mTextSize);
}
2.4 确定宽高、进行绘制
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//1.调用着在布局文件中可能出现的布局为 Wrap_content,但这种情况不允许
//获取模式进行判断
//高度和宽度不一致,取最小值,确保是个正方形
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST){
width = 80;
Log.d(TAG,"不允许");
}
if(heightMode == MeasureSpec.AT_MOST){
height = 80;
Log.d(TAG,"不允许");
}
//三元运算
setMeasuredDimension(width>height?height:width,width>height?height:width);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect = new RectF(borderWidth,borderWidth,getWidth()- borderWidth,getHeight()-borderWidth);
canvas.drawArc(rect,135,270,false,paint);
// if(stepMax == 0)return;
if(stepMax != 0) {
float sweepAngle = currentStep / (float)stepMax*270;
Log.d(TAG, "sweepAngle:" + currentStep * stepMax);
Log.d(TAG, "sweepAngle:"+sweepAngle );
canvas.drawArc(rect,135,sweepAngle,false,indoorpaint);
}
// canvas.drawArc(rect,135,sweepAngle,false,indoorpaint);
canvas.drawText(String.valueOf(currentStep),getWidth()/2-String.valueOf(currentStep).length()/2,getHeight()/2,textPaint);
Log.d(TAG,"currentStep"+currentStep);
}
2.5 添加动画效果
//设置步数,让步数充满变化
public synchronized void setCurrentStep(int currentStep) {
this.currentStep = currentStep;
invalidate();
}
public synchronized void setStepMax(int stepMax) {
this.stepMax = stepMax;
}
public int getCurrentStep() {
return currentStep;
}
public int getStepMax() {
return stepMax;
}
//使用属性动画,让步数充满变化
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0,3000);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
qqTepView.setCurrentStep((int) animatedValue);
}
});
valueAnimator.start();
三、附件
package com.example.qqview;
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.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
public class QQTepView extends View{
private static final String TAG ="QQStepView" ;
private int indoorColor = Color.RED;
private int outColor = Color.BLUE;
private int stepColor = Color.GREEN;
private int mTextSize;
private int borderWidth;
private Paint paint,indoorpaint,textPaint;
private int stepMax = 0;
private int currentStep = 0;
public QQTepView(Context context) {
this(context,null);
}
public QQTepView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public QQTepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public QQTepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.QQTepView);
indoorColor = typedArray.getColor(R.styleable.QQTepView_indoorColor,indoorColor);
outColor = typedArray.getColor(R.styleable.QQTepView_outerColor,outColor);
stepColor = typedArray.getColor(R.styleable.QQTepView_stepColor,stepColor);
//设计到字体大小的需要进行类型强转,dp————》sp,这里没有进行强转
mTextSize = typedArray.getDimensionPixelSize(R.styleable.QQTepView_mTextSize,mTextSize);
borderWidth = typedArray.getDimensionPixelSize(R.styleable.QQTepView_borderWidth,borderWidth);
typedArray.recycle();
//1.分析效果
//2.确定自定义属性 编写attrs.xml
//3.在布局中使用
//4.在自定义View中获取属性
//5.onMeasure():确定 宽高
//6画图
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(outColor);
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
indoorpaint = new Paint();
indoorpaint.setStrokeWidth(borderWidth);
indoorpaint.setColor(indoorColor);
indoorpaint.setStyle(Paint.Style.STROKE);
indoorpaint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(stepColor);
textPaint.setTextSize(mTextSize);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//1.调用着在布局文件中可能出现的布局为 Wrap_content,但这种情况不允许
//获取模式进行判断
//高度和宽度不一致,取最小值,确保是个正方形
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST){
width = 80;
Log.d(TAG,"不允许");
}
if(heightMode == MeasureSpec.AT_MOST){
height = 80;
Log.d(TAG,"不允许");
}
//三元运算
setMeasuredDimension(width>height?height:width,width>height?height:width);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect = new RectF(borderWidth,borderWidth,getWidth()-borderWidth,getHeight()-borderWidth);
canvas.drawArc(rect,135,270,false,paint);
// if(stepMax == 0)return;
if(stepMax != 0) {
float sweepAngle = currentStep / (float)stepMax*270;
Log.d(TAG, "sweepAngle:" + currentStep * stepMax);
Log.d(TAG, "sweepAngle:"+sweepAngle );
canvas.drawArc(rect,135,sweepAngle,false,indoorpaint);
}
// canvas.drawArc(rect,135,sweepAngle,false,indoorpaint);
canvas.drawText(String.valueOf(currentStep),getWidth()/2-String.valueOf(currentStep).length()/2,getHeight()/2,textPaint);
Log.d(TAG,"currentStep"+currentStep);
}
//设置步数,让步数充满变化
public synchronized void setCurrentStep(int currentStep) {
this.currentStep = currentStep;
invalidate();
}
public synchronized void setStepMax(int stepMax) {
this.stepMax = stepMax;
}
public int getCurrentStep() {
return currentStep;
}
public int getStepMax() {
return stepMax;
}
}
package com.example.qqview;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
public class MainActivity extends AppCompatActivity {
private QQTepView qqTepView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
qqTepView = findViewById(R.id.qqTepView);
qqTepView.setStepMax(4000);
//使用属性动画,让步数充满变化
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0,3000);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
qqTepView.setCurrentStep((int) animatedValue);
}
});
valueAnimator.start();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.qqview.QQTepView
android:id="@+id/qqTepView"
android:layout_width="match_parent"
android:layout_height="366dp"
app:borderWidth="30dp"
app:indoorColor="@color/design_default_color_error"
app:mTextSize="15sp"
app:outerColor="@color/design_default_color_primary"
app:stepColor="@color/design_default_color_error"></com.example.qqview.QQTepView>
</LinearLayout>
附言:平时在向别人前辈学习,案例可能相同,但都是自己手敲的,欢迎一起学习~