Android进阶之自定义View(2)高仿钉钉运动步数实现可动的进度圆环(上)
本文比较详细的介绍了绘制圆环及圆弧的基础知识,为实现钉钉运动步数打下基础,实现了下面的效果,实现钉钉运动就灰常简单了,本文实现的初步效果如下:
如果想直接看钉钉运动的最终效果,请戳:Android进阶之自定义控件(2)高仿钉钉运动步数实现可动的进度圆环(下)
1、圆环的绘制
2、绘制背景圆环和进度圆环
3、绘制中间的文字
(1)使用drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)绘制圆环:
public class SportStepView extends View { private Paint mPaint; //圆环绘制的宽度 private int mRoundWidth = 40; public SportStepView(Context context) { this(context, null); } public SportStepView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SportStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取宽的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取宽的尺寸 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //对wrap_content这种模式进行处理 if (heightMode == MeasureSpec.AT_MOST) { heightSize = widthSize; } //绘制圆环以宽度为标准,保存丈量结果 setMeasuredDimension(widthSize, heightSize); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(mRoundWidth); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制圆,设置画笔的Style为Paint.Style.STROKE,则绘制出来的为圆环,否则绘制出来的为圆 //canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mRoundWidth / 2, mPaint); RectF oval = new RectF(0 , 0, getWidth(), getWidth()); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0, 360, false, mPaint); } }
会发现显示不全,绘制超出边界了,因为圆的宽度是在当前半径向两边展开的。如下图分析得知,圆所在的矩形区域不是rect(为屏幕的矩形区域),而是real Rect所在的区域:
因此只需要修改如下即可。
RectF oval = new RectF(0 + mRoundWidth / 2, 0 + mRoundWidth / 2, getWidth() - mRoundWidth / 2, getWidth() - mRoundWidth / 2); // RectF oval = new RectF(0 , 0, getWidth(), getWidth()); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0, 360, false, mPaint);
修改后达到我们的预期效果:
(2)如果只是绘制上面的圆环效果,还可以使用: canvas.drawCircle()的方式实现,这种方法更简单:
//绘制圆,设置画笔的Style为Paint.Style.STROKE,则绘制出来的为圆环,否则绘制出来的为圆 //由于圆环本身有宽度,所以半径要减去圆环宽度的一半,不然一部分圆会在view外面。 canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mRoundWidth / 2, mPaint);
(3)接下来我们来实现背景圆环+进度圆环的效果了,利用drawCircle绘制背景圆环,drawArc()绘制进度圆环,预期效果如下:
这个很简单,再画个圆弧即可:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制背景圆环,设置画笔的Style为Paint.Style.STROKE,则绘制出来的为圆环,否则绘制出来的为圆 //正常情况下 canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mRoundWidth / 2, mPaint); //正常情况下,绘制进度圆环 RectF oval = new RectF(0 + mProgressRoundWidth / 2, 0 + mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0, 300, false, mProgressPaint); }
但是光这样处理,会有个小问题,就是当背景圆环和进度圆环宽度不一致时,会出现下面的问题。
解决方法:以宽度较大为准即可。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 如果背景圆环和进度圆环宽度不一致,都以较大的宽度为准绘制。避免出现两者显示不居中的问题 */ //绘制背景圆环,设置画笔的Style为Paint.Style.STROKE,则绘制出来的为圆环,否则绘制出来的为圆 // canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint); if (mRoundWidth < mProgressRoundWidth) { canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mProgressRoundWidth / 2, mPaint); } else { //正常情况下 canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mRoundWidth / 2, mPaint); } if (mRoundWidth < mProgressRoundWidth) { // //正常情况下,绘制进度圆环 RectF oval = new RectF(0 + mProgressRoundWidth / 2, 0 + mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2); // RectF oval = new RectF(0 , 0, getWidth(), getWidth()); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0, 300, false, mProgressPaint); } else { //绘制进度圆环 RectF oval = new RectF(0 + mRoundWidth / 2, 0 + mRoundWidth / 2, getWidth() - mRoundWidth / 2, getWidth() - mRoundWidth / 2); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0, 300, false, mProgressPaint); } }
(4)绘制居中的进度文字
代码实现:
//绘制中间的文字 Rect textRect = new Rect(); //进度百分比 int progressPercent = (int) (mCurrentProgress * 1f / mMaxProgress * 100); String mShowText = progressPercent + "%"; mTextPaint.getTextBounds(mShowText, 0, mShowText.length(), textRect); canvas.drawText(mShowText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2, mTextPaint);
(5)处理圆环进度和文字进度的动态显示
方法一:开一个分线程,动态改变进度的值,不断绘制达到进度变化的效果。
方法二:下篇会介绍到_
在测试的Activity中使用:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // mytextview = findViewById(R.id.mytextview); final SportStepView sportStepView = findViewById(R.id.sportstepview); mCurrentProgress = 80; //速度,值越大,变化速度越快 rate = 1; //开一个分线程,动态改变进度的值,不断绘制达到进度变化的效果 new Thread(new Runnable() { @Override public void run() { sportStepView.setCurrentProgress(0); for (int i = 0; i < mCurrentProgress / rate; i++) { sportStepView.setCurrentProgress(sportStepView.getCurrentProgress() + rate); SystemClock.sleep(20); // pb_progress.invalidate();//invalidate()必须在主线程中执行,此处不能使用 sportStepView.postInvalidate();//强制重绘,postInvalidate()可以在主线程也可以在分线程中执行 } } }).start(); }
完整代码:
package com.example.jojo.learn.customview; 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.util.TypedValue; import android.view.View; import com.example.jojo.learn.R; /** * Created by JoJo on 2018/7/31. * wechat:18510829974 * description: 仿钉钉运动步数 */ public class SportStepView extends View { //绘制背景圆环的画笔 private Paint mPaint; //绘制外面进度的圆环的画笔 private Paint mProgressPaint; //绘制外面进度的圆环的画笔 private Paint mTextPaint; //背景圆弧的绘制的宽度 private int mRoundWidth = 40; //进度圆环的宽度 private float mProgressRoundWidth = 60; private int mTextSize = 40;//单位 sp //圆环最大进度 private int mMaxProgress = 100; //圆环当前进度 private int mCurrentProgress = 0; public SportStepView(Context context) { this(context, null); } public SportStepView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SportStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取宽的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取宽的尺寸 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //对wrap_content这种模式进行处理 if (heightMode == MeasureSpec.AT_MOST) { heightSize = widthSize; } //以宽度为标准保存丈量结果 setMeasuredDimension(widthSize, heightSize); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(mRoundWidth); mProgressPaint = new Paint(); mProgressPaint.setAntiAlias(true);// 抗锯齿效果 mProgressPaint.setStyle(Paint.Style.STROKE); mProgressPaint.setColor(Color.YELLOW); mProgressPaint.setStrokeCap(Paint.Cap.ROUND);// 圆形笔头 mProgressPaint.setStrokeWidth(mProgressRoundWidth); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true);// 抗锯齿效果 mTextPaint.setStyle(Paint.Style.STROKE); mTextPaint.setColor(Color.BLACK); mTextPaint.setTextSize(sp2px(mTextSize)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 如果背景圆环和进度圆环宽度不一致,都以较大的宽度为准绘制。避免出现两者显示不居中的问题 */ //绘制背景圆环,设置画笔的Style为Paint.Style.STROKE,则绘制出来的为圆环,否则绘制出来的为圆 // canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint); if (mRoundWidth < mProgressRoundWidth) { canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mProgressRoundWidth / 2, mPaint); } else { //正常情况下 canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mRoundWidth / 2, mPaint); } if (mRoundWidth < mProgressRoundWidth) { // //正常情况下,绘制进度圆环 RectF oval = new RectF(0 + mProgressRoundWidth / 2, 0 + mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2); // RectF oval = new RectF(0 , 0, getWidth(), getWidth()); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0 + 90, mCurrentProgress * 1f / mMaxProgress * 360, false, mProgressPaint); } else { //绘制进度圆环 RectF oval = new RectF(0 + mRoundWidth / 2, 0 + mRoundWidth / 2, getWidth() - mRoundWidth / 2, getWidth() - mRoundWidth / 2); //画圆弧 useCenter:是否显示圆内的横线 下面的绘制0,360的圆弧,也可以实现绘制圆环的效果 canvas.drawArc(oval, 0 + 90, mCurrentProgress * 1f / mMaxProgress * 360, false, mProgressPaint); } //绘制中间的文字 Rect textRect = new Rect(); //进度百分比 int progressPercent = (int) (mCurrentProgress * 1f / mMaxProgress * 100); String mShowText = progressPercent + "%"; mTextPaint.getTextBounds(mShowText, 0, mShowText.length(), textRect); canvas.drawText(mShowText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2, mTextPaint); } public void setCurrentProgress(int currentProgress) { this.mCurrentProgress = currentProgress; } public void setMaxProgress(int maxProgress) { this.mMaxProgress = maxProgress; } public int getMaxProgress() { return mMaxProgress; } public int getCurrentProgress() { return mCurrentProgress; } /** * 将sp转换成px * * @param sp * @return */ private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } }
涉及到的自定义属性
<!--SportStepView--> <declare-styleable name="SportStepView"> <!--圆环半径--> <attr name="radius" format="dimension"></attr> <attr name="outerRoundColor" format="color"></attr> <attr name="innerRoundColor" format="color"></attr> </declare-styleable>
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android进阶之自定义View(1)实现可换行的TextView
今天来一起学习一下最简单的自定义view,自己动手写一个MyTextView,当然不会像系统的TextView那么复杂,只是实现一下TextView的简单功能,包括分行显示及自定义属性的处理,主要目的是介绍自定义view的实现的基本思路和需要掌握的一些基础知识。 《一》先展示一下实现的最终效果 image.png 《二》实现步骤分析 1、创建MyTextView extends View,重写构造方法。一般是重写前三个构造方法,让前两个构造方法最终调用三个参数的构造方法,然后在第三个构造方法中进行一些初始化操作。 2、在构造方法中进行一些初始化操作,如初始化画笔及获取自定义属性等。 如何自定义属性? (1)在values下创建attrs.xml. //定义你的view可以在布局文件中配置的自定义属性 <declare-styleable name="MyTextViewApprence"> <attr name="textColor" format="color"></attr> <attr name="textSize" format="di...
- 下一篇
(转)Android拍摄的视频无法在IOS播放
https://blog.csdn.net/u012992171/article/details/50673305 公司最近的项目需要上线一个视频功能,在临近尾声的时候出现了一个比较坑爹的BUG,那就是开发Android的同事拍摄的视频上传到服务器之后,我这边用手机无法播放,而我自己拍摄的视频上传后是可以直接播放的。 我用浏览器访问了Android同事拍摄的视频的互联网 公司最近的项目需要上线一个视频功能,在临近尾声的时候出现了一个比较坑爹的BUG,那就是开发Android的同事拍摄的视频上传到服务器之后,我这边用手机无法播放,而我自己拍摄的视频上传后是可以直接播放的。 我用浏览器访问了Android同事拍摄的视频的互联网路径,在浏览器上是可以正常播放的,下载到本地,在Mac上用QuickTime也是正常播放,在此可以排除是文件损坏导致的问题。 我这边调试工程得到的错误信息如下: image 是一个_itemFailedToPlayToEnd的KVO记录log,Google了这个错误,包括stackoverflow上给出的原因基本上都是视频URL有问题,不存在这个文件导致的,由于我已经...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- MySQL8.0.19开启GTID主从同步CentOS8
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Hadoop3单机部署,实现最简伪集群