文章目录
前言
关于自定义View,仿QQ运动步数,
提示:以下是本篇文章正文内容,下面案例可供参考
先看效果图
一、实现思路
由图片可以看出,主要分为三个部分,总进度条,已走进度条,已走步数。但是又有许多可自行设置的属性,如线条宽度,总进度条颜色,已走进度条颜色,字体颜色等等。所以需要自定义属性Attrs.xml。下来可以慢慢实现效果。
二、实现步骤
1、分析效果
代码如下(示例):
2、确定自定义属性,编写attrs.xml
<declare-styleable name="QQStepView">
<attr name="outerColor" format="color"/>
<attr name="innerColor" format="color"/>
<attr name="borderWidget" format="dimension"/>
<attr name="stepText" format="string"/>
<attr name="stepTextSize" format="dimension"/>
<attr name="stepTextColor" format="color"/>
</declare-styleable>
3、在布局中使用
<com.joekai.customview.views.QQStepView
android:id="@+id/step_view"
android:layout_width="150dp"
android:layout_height="150dp"
app:outerColor="@color/colorPrimary"
app:innerColor="@color/colorAccent"
app:textColor="@color/colorPrimaryDark"
app:borderWidget="5dp"
app:textSize="18sp"
android:background="@color/red"
/>
4、在自定义View中获取自定义属性
//获取attrs.xml中设置的属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mStepText = ta.getString(R.styleable.QQStepView_stepText);
mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
mBorderWidget = ta.getDimension(R.styleable.QQStepView_borderWidget, mBorderWidget);
mTextSize = ta.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, sp2px(mTextSize));
mTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mTextColor);
//回收
ta.recycle();
///在此处将所有画笔进行初始化
//创建画笔
mOuterPaint = new Paint();
//取消锯齿
mOuterPaint.setAntiAlias(true);
//设置颜色
mOuterPaint.setColor(mOuterColor);
//设置圆弧宽度
mOuterPaint.setStrokeWidth(mBorderWidget);
//设置圆弧Style
mOuterPaint.setStyle(Paint.Style.STROKE);
//设置起始,结束圆角
mOuterPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint = new Paint();
//取消锯齿
mOuterPaint.setAntiAlias(true);
//设置颜色
mInnerPaint.setColor(mInnerColor);
//设置圆弧宽度
mInnerPaint.setStrokeWidth(mBorderWidget);
//设置圆弧Style
mInnerPaint.setStyle(Paint.Style.STROKE);
//设置起始,结束圆角
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint = new Paint();
//抗锯齿
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
5、onMeasure 确定大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//在布局文件中,可能是wrap_content ,也可能宽高不一致
//宽高不一致时,取最小值,确保是个正方形
//获取宽高的模式
int widgetMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//获取宽高的大小
int widgetSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch (MeasureSpec.getMode(widthMeasureSpec)) {
}
setMeasuredDimension(widgetSize > heightSize ? heightSize : widgetSize, widgetSize > heightSize ? heightSize : widgetSize);
}
6、 画外圆弧,内圆弧,文字
6.1 画外圆弧(总进度)
遇到的问题: 边缘显示不全
原因: 描边的时候设置了画笔宽度mBorderWidget,所以导致边缘超出限制。
设置画布区域:
//因为区域超出画布,所以将画笔起始点从新设置。
RectF rectF = new RectF(mBorderWidget / 2, mBorderWidget / 2, getWidth() - mBorderWidget / 2, getHeight() - mBorderWidget / 2);
//画圆弧
//1、画布区域,2、起始角度,3、结束角度 4、是否填充 5、画笔
canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mOuterPaint);
6.2. 画内圆弧(已走进度条)
//未设置最大值时,返回,不进行绘画。
if (mMaxStep == 0) return;
//计算百分比,用于绘画已走进度条;
float sweepAngle = (float) mStep / mMaxStep;
canvas.drawArc(rectF, mStartAngle, sweepAngle * mEndAngle, false, mInnerPaint);
6.3 画文本
mStepText = mStep + "";
//计算的宽度 与 字体的长度有关 与字体的大小 用画笔来测量
//获取文本的Rect
//mTextPaint.getTextBounds(mStepText, 0, mStepText.length(), mTextBounds);
//获取文本宽度
//mTextBounds.width();
//获取文本宽度
float textWidget = mTextPaint.measureText(mStepText);
//计算X起始位置
int dx = (int) (getWidth() / 2 - textWidget / 2);
//计算基线位置
//dy代表的是:高度的一半到baseLine的距离
//获取文字排版信息
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
//基线
int baseLine = getHeight() / 2 + dy;
canvas.drawText(mStepText, dx, baseLine, mTextPaint);
7、其他 设置方法
7.1 设置最大值
public synchronized void setMaxStep(int maxStep) {
this.mMaxStep = maxStep;
}
7.2 设置已走进度条
public synchronized void setStep(int step){
this.mStep = step;
//不断绘制
invalidate();
}
7.1 设置已走进度条,实现动画效果
public synchronized void setAnimatorStep(int step){
//属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, step);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float step = (float) valueAnimator.getAnimatedValue();
setStep((int) step);
}
});
valueAnimator.start();
}
完整代码
/**
* 仿QQ运动步数
*/
public class QQStepView extends View {
private int mOuterColor = Color.RED;//总进度条颜色
private int mInnerColor = Color.BLUE;//已走颜色
private float mBorderWidget = 20;//圆弧宽度
private int mTextSize = 20;//字体大小
private String mStepText;
private int mTextColor = Color.GREEN;//字体颜色
private Paint mOuterPaint;//总画笔
private Paint mInnerPaint;//已走画笔
private Paint mTextPaint;//已走画笔
private int mStartAngle = 135;
private int mEndAngle = 270;
private int mMaxStep = 2000;
private int mStep = 1000;
private Rect mTextBounds ;
public QQStepView(Context context) {
this(context, null);
}
public QQStepView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 1 分析效果
// 2 确定自定义属性,编写attrs.xml
// 3 在布局中使用
// 4 在自定义View中获取自定义属性
// 7 其他
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mStepText = ta.getString(R.styleable.QQStepView_stepText);
mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
mBorderWidget = ta.getDimension(R.styleable.QQStepView_borderWidget, mBorderWidget);
mTextSize = ta.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, sp2px(mTextSize));
mTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mTextColor);
//回收
ta.recycle();
//创建画笔
mOuterPaint = new Paint();
//取消锯齿
mOuterPaint.setAntiAlias(true);
//设置颜色
mOuterPaint.setColor(mOuterColor);
//设置圆弧宽度
mOuterPaint.setStrokeWidth(mBorderWidget);
//设置圆弧Style
mOuterPaint.setStyle(Paint.Style.STROKE);
//设置起始,结束圆角
mOuterPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint = new Paint();
//取消锯齿
mOuterPaint.setAntiAlias(true);
//设置颜色
mInnerPaint.setColor(mInnerColor);
//设置圆弧宽度
mInnerPaint.setStrokeWidth(mBorderWidget);
//设置圆弧Style
mInnerPaint.setStyle(Paint.Style.STROKE);
//设置起始,结束圆角
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint = new Paint();
//抗锯齿
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
mTextBounds = new Rect();
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @return
*/
public static int sp2px(int spValue) {
final float fontScale = BaseApplication.getInstance().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
// 5 onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//在布局文件中,可能是wrap_content ,也可能宽高不一致
//高度不一致,取最大值,确保是个正方形
//获取宽高的模式
int widgetMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//获取宽高的大小
int widgetSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch (MeasureSpec.getMode(widthMeasureSpec)) {
}
setMeasuredDimension(widgetSize > heightSize ? heightSize : widgetSize, widgetSize > heightSize ? heightSize : widgetSize);
}
// 6 画外圆弧,内圆弧,文字
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//6.1 画外圆弧
//问题:边缘显示不全 描边有宽度 mBorderWidget
//获取圆点
int center = getWidth() / 2;
//获取半径
int radius = (int) (center - mBorderWidget / 2);
RectF rectF = new RectF(mBorderWidget / 2, mBorderWidget / 2, getWidth() - mBorderWidget / 2, getHeight() - mBorderWidget / 2);
canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mOuterPaint);
//6.2 画内圆弧
if (mMaxStep == 0) return;
float sweepAngle = (float) mStep / mMaxStep;
canvas.drawArc(rectF, mStartAngle, sweepAngle * mEndAngle, false, mInnerPaint);
//6.3 画文本
mStepText = mStep + "";
//计算的宽度 与 字体的长度有关 与字体的大小 用画笔来测量
//获取文本的Rect
// mTextPaint.getTextBounds(mStepText, 0, mStepText.length(), mTextBounds);
//获取文本宽度
float textWidget = mTextPaint.measureText(mStepText);
int dx = (int) (getWidth() / 2 - textWidget / 2);
//dy代表的是:高度的一半到baseLine的距离
//获取文字排版信息
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
//基线
int baseLine = getHeight() / 2 + dy;
canvas.drawText(mStepText, dx, baseLine, mTextPaint);
}
// 7 其他
public synchronized void setMaxStep(int maxStep) {
this.mMaxStep = maxStep;
}
public synchronized void setStep(int step){
this.mStep = step;
//不断绘制
invalidate();
}
public synchronized void setAnimatorStep(int step){
//属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, step);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float step = (float) valueAnimator.getAnimatedValue();
setStep((int) step);
}
});
valueAnimator.start();
}
}
总结
提示:这里对文章进行总结:
以上就是今天要讲的内容,本文仅仅简单介绍了自定义仿QQ运动步数,扩展效果可自己再去实现。