效果如下

gif图展示效果不好,实际体验无卡顿

1.自定义属性

早Values目录下New-values resource file,命名为attrs.xml(命名随意,但规范命名为attrs.xml)

自定义属性如下,注意format不要与Android自带的命名重复。

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="QQStepView">
<attr name="outerColor" format="color" />
<attr name="innerColor" format="color" />
<attr name="borderWidth" format="dimension" />
<attr name="stepTextSize" format="dimension" />
<attr name="stepTextColor" format="color" />
</declare-styleable> <declare-styleable name="MyProgressBar">
<attr name="leftColor" format="color" />
<attr name="rightColor" format="color" />
<attr name="progressTextColor" format="color" />
<attr name="progressTextSize" format="dimension" />
<attr name="progressBounds" format="dimension" />
</declare-styleable>
</resources>
2.编写自定义View
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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View; import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils; @SuppressWarnings("all")
public class QQStepView extends View {
private int mOuterColor = Color.parseColor("#2196F3");
private int mInnerColor = Color.parseColor("#F44336");
private int mStepTextColor = Color.parseColor("#EC407A");
private int mBorderWidth = 20;//px
private int mStepTextSize = 18;//px private int mSeptMax = 10000;
private int mSeptCurrent = 0; private Paint mOutPaint;
private Paint mInnerPaint;
private Paint mTextPaint; 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);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, MeasureUtils.dp2px(mBorderWidth, this));
mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, MeasureUtils.sp2px(mStepTextSize, this));
array.recycle(); mOutPaint = new Paint();
mOutPaint.setAntiAlias(true);
mOutPaint.setStrokeWidth(mBorderWidth);
mOutPaint.setColor(mOuterColor);
mOutPaint.setStyle(Paint.Style.STROKE);
mOutPaint.setStrokeCap(Paint.Cap.ROUND);//圆角 mInnerPaint = new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStyle(Paint.Style.STROKE);//实心
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//圆角 mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setStyle(Paint.Style.STROKE);
mTextPaint.setColor(mStepTextColor);
mTextPaint.setTextSize(mStepTextSize);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
//用户设置的是wrap_content,此时设置一个默认宽高100
width = height = MeasureUtils.dp2px(200, this);
}
setMeasuredDimension(width > height ? height : width, width > height ? height : width);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int center = getWidth() / 2;
int radius = getWidth() / 2 - mBorderWidth;
RectF rectF = new RectF(mBorderWidth, mBorderWidth, center + radius, center + radius);
canvas.drawArc(rectF, 135, 270, false, mOutPaint); if (mSeptMax == 0) return;
float sweepAngle = (float) mSeptCurrent / mSeptMax;
canvas.drawArc(rectF, 135, 270 * sweepAngle, false, mInnerPaint); String stepText = mSeptCurrent + "";
Rect textBounds = new Rect();
mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
int dx = getWidth() / 2 - textBounds.width() / 2;
int baseLine = MeasureUtils.measureBaseLine(mTextPaint, stepText, this);
canvas.drawText(stepText, dx, baseLine, mTextPaint);
} public void setmSeptMax(int mSeptMax) {
this.mSeptMax = mSeptMax;
} public synchronized void setmSeptCurrent(int mSeptCurrent) {
this.mSeptCurrent = mSeptCurrent;
//重绘
invalidate();
}
}
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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View; import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils; @SuppressWarnings("all")
public class MyProgressBar extends View {
private int mLiftColor = Color.parseColor("#F44336");
private int mRightColor = Color.parseColor("#E0E0E0");
private int mProgressTextColor = Color.parseColor("#616161");
private int mProgressTextSize = 12;//px 后续再考虑需不需要转换成sp
private int mProgressBounds = 1;//px private int mCurrentProgress, mMaxProgress = 100;//默认最大刻度为100 private Paint mLeftPaint, mRightPaint, mTextPaint; public MyProgressBar(Context context) {
this(context, null);
} public MyProgressBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public MyProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar);
mLiftColor = array.getColor(R.styleable.MyProgressBar_leftColor, mLiftColor);
mRightColor = array.getColor(R.styleable.MyProgressBar_rightColor, mRightColor);
mProgressTextColor = array.getColor(R.styleable.MyProgressBar_progressTextColor, mProgressTextColor);
mProgressTextSize = array.getDimensionPixelSize(R.styleable.MyProgressBar_progressTextSize, mProgressTextSize);
array.recycle(); mLeftPaint = new Paint();
mLeftPaint.setAntiAlias(true);
mLeftPaint.setColor(mLiftColor); mRightPaint = new Paint();
mRightPaint.setAntiAlias(true);
mRightPaint.setColor(mRightColor); mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setStyle(Paint.Style.STROKE);
mTextPaint.setColor(mProgressTextColor);
mTextPaint.setTextSize(mProgressTextSize);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widht = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widht, height);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mRightPaint.setStrokeWidth(getHeight());
RectF rightRect = new RectF(0, 0, getWidth(), getHeight());
canvas.drawRoundRect(rightRect, getHeight() / 2, getHeight() / 2, mRightPaint); mLeftPaint.setStrokeWidth(getHeight());
float progress = (float) mCurrentProgress / (mMaxProgress * 10);
int radius = getHeight() / 2;
RectF rectF = new RectF(0, 0, progress * getWidth(), getHeight());
canvas.drawRoundRect(rectF, radius, radius, mLeftPaint); //画文字随着进度条右移
String text = (float) mCurrentProgress / 10 + "%";
int dx = getHeight() / 2;
Rect textBounds = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
int baseLine = MeasureUtils.measureBaseLine(mTextPaint, text, this);
canvas.drawText(text, progress * getWidth() + 10, baseLine, mTextPaint);
} public void setProgress(int mCurrentProgress) {
this.mCurrentProgress = mCurrentProgress;
//重绘
invalidate();
} public void setMaxProgress(int mMaxProgress) {
this.mMaxProgress = mMaxProgress;
} public int getProgress() {
return mCurrentProgress;
}
}
3.为自定义View添加动画

首先在xml中使用我们的自定义布局和自定义属性

<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".page3.QQSportActivity"> <com.cyq.customview2.page3.QQStepView
android:id="@+id/custom_QQ_step"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="50dp"
app:borderWidth="6dp"
app:innerColor="@color/innerColor"
app:outerColor="@color/outerColor"
app:stepTextSize="30sp"
android:layout_marginBottom="100dp"/> <com.cyq.customview2.page3.MyProgressBar
android:id="@+id/custom_progressbar"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_margin="50dp"
app:leftColor="@color/innerColor"
app:progressTextColor="@color/stepTextColor"
app:progressTextSize="16sp"
app:rightColor="@color/greyColor" />
</LinearLayout>

通过属性动画动态增加进度


import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.animation.DecelerateInterpolator; import com.cyq.customview2.R; import butterknife.BindView;
import butterknife.ButterKnife; public class QQSportActivity extends AppCompatActivity {
@BindView(R.id.custom_QQ_step)
QQStepView customQQStep;
@BindView(R.id.custom_progressbar)
MyProgressBar customProgressbar; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qqsport);
ButterKnife.bind(this); customQQStep.setmSeptMax(10000); //属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 8765);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new DecelerateInterpolator());//插值器
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (Float) animation.getAnimatedValue();
customQQStep.setmSeptCurrent((int) currentStep);
}
});
valueAnimator.start(); //属性动画
ValueAnimator valueAnimator2 = ObjectAnimator.ofFloat(0, 780);
valueAnimator2.setDuration(2000);
valueAnimator2.setInterpolator(new DecelerateInterpolator());//插值器
valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (Float) animation.getAnimatedValue();
customProgressbar.setProgress((int) currentStep);
}
});
valueAnimator2.start();
}
}

获取文字基线和sp,dp转xp的工具类如下;

import android.graphics.Paint;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View; public class MeasureUtils {
/**
* drawText获取基线
*
* @param textPaint
* @param text
* @param view
* @return
*/
public static int measureBaseLine(Paint textPaint, String text, View view) {
Rect textBounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textBounds);
Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = view.getHeight() / 2 + dy;
return baseLine;
} public static int sp2px(int sp, View view) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, view.getResources().getDisplayMetrics());
} public static int dp2px(int dp, View view) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, view.getResources().getDisplayMetrics());
}
}

后续待改进

1.sp,dp,xp的转换

2.进度文字接近100%时不向右边移动,并且文字和进度重叠部分动态变色

QQ运动步数&自定义ProgressBar的更多相关文章

  1. 手把手教你修改iOS版QQ的运动步数

    手把手教你修改iOS版QQ的运动步数 现在很多软件都加上了运动模块,比如QQ和微信,而且还有排行榜,可以和好友比较谁的运动步数多,任何东西只要添加了比较功能,就变得不一样了.今天教大家用代码去修改QQ ...

  2. Android 自定义View实现QQ运动积分抽奖转盘

    因为偶尔关注QQ运动, 看到QQ运动的积分抽奖界面比较有意思,所以就尝试用自定义View实现了下,原本想通过开发者选项查看下界面的一些信息,后来发现积分抽奖界面是在WebView中展示的,应该是在H5 ...

  3. Android_AsyncTaskDemo之QQ记步数(画圆形图片知识)

    今天学习了AsyncTask Android 的异步机制.我简单的实现我的一个小小案例--qq记步数.然后穿插一个画圆形图片的知识点. 由于所学知识有限,目前我计数,还有排名等等我就简单的利用随机数实 ...

  4. 微信小程序 初步认识一(微信运动步数)

    1.注册微信小程序 2.安装小程序开发工具 3.实例(显示微信运动步数) 4.后端处理(c#) 一 注册微信小程序 注册地址:https://mp.weixin.qq.com/cgi-bin/regi ...

  5. QQ运动,新楛的马桶还在香,营销人不应摒弃。

    QQ运动,都说新楛的马桶还香三天,为毛你这般明日黄花,为营销人所弃. QQ运动,一个差不多被遗忘的冷却地带,却圈粉无数,以性感.狂野.妖艳.线条.汗水等秀元素贯穿始终,狼友显露于此,爱美的女性也未曾缺 ...

  6. Android ProgressBar分析及自定义ProgressBar

    ProgressBar是在执行耗时操作时的一种人性化设计.分为两种形式:转圈的,能显示进度的. 而能取决于是什么样式的PregressBar,当然就是PregressBar的样式啦~ Widget.P ...

  7. 用 Python 修改微信(支付宝)运动步数,轻松 TOP1

    用 Python 修改微信(支付宝)运动步数,轻松 TOP1 项目意义 如果你想在支付宝蚂蚁森林收集很多能量种树,为环境绿化出一份力量,又或者是想每天称霸微信运动排行榜装逼,却不想出门走路,那么该py ...

  8. Android自定义View实现仿QQ实现运动步数效果

    效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...

  9. 2.自定义view-QQ运动步数

    1.效果 2.实现 2.1自定义属性 在res/values 文件夹中新建xx.xml,内容如下 <?xml version="1.0" encoding="utf ...

随机推荐

  1. 深入浅出zeptojs中tap事件

    1.tap事件实现 zepto 源码里面看关于tap的实现方法: $(document).ready(function(){ var now, delta, deltaX = 0, deltaY = ...

  2. 04 - JavaSE之异常处理

    异常的概念(运行期出现的错误) java 异常是 java 提供的用于处理程序中错误的一种机制. 所谓的错误是指在程序运行的过程中发生的一些异常事件.(如:除0溢出,数组下标越界,所要读取的文件不存在 ...

  3. mysql的sql执行计划详解(非常有用)

    以前没有怎么了解mysql执行计划,以及sql 优化方面,今天算学习了. https://blog.csdn.net/heng_yan/article/details/78324176 https:/ ...

  4. scala-传名函数和传值函数

    Scala的解释器在解析函数参数(function arguments)时有两种方式:先计算参数表达式的值(reduce the arguments),再应用到函数内部:或者是将未计算的参数表达式直接 ...

  5. MVC源码分析 - Error过滤器

    接 上一篇 内容, 这里先看一下错误处理过滤器. 在看此部分之前, 先看看MVC已经提供的功能吧. 一. MVC 自带功能 1. 配置方法 <system.web> <!--mode ...

  6. Android so 文件进阶<三> so文件的简单加密

    0x00  前言 之前的两篇文章从链接视图和执行视图分析了elf文件的大致结构,这篇文章主要内容是对于so文件进行简单的加密工作,针对Ida等静态分析工具的分析,一旦开始动态调试就应该很容易就可以du ...

  7. win 7 64 mysql 5.6.4 安装

    windows 7 64位下配置mysql64位免安装版1.官方网站下载mysql-5.6.10-winx64.zip2.解压到E:\MYSQL(路径自己指定)3.在E:\MYSQL下新建my.ini ...

  8. Spring 环境与profile(二)——Properties with Spring

    1. 简述 Spring profile用例,分3个场景(Test, Dev, Prod)相对Spring 环境与profile(一)——超简用例多了根据具体的profile获取对应的Properti ...

  9. multiset多重集合容器(常用的使用方法总结)

    关于C++STL中multiset集合容器的学习,看别人的代码一百遍,不如自己动手写一遍. multiset多重集合容器和set集合容器的使用方法大多相同,不同的是multiset多重集合容器允许重复 ...

  10. 分布式理论(二)——Base 理论

    前言 在前文 分布式理论(一) -- CAP 定理 中,我们说,CAP 不可能同时满足,而分区容错是对于分布式系统而言,是必须的.最后,我们说,如果系统能够同时实现 CAP 是再好不过的了,所以出现了 ...