前言

因为本人是个魅族控,魅族的UI以及页面风格真的是深入我心,特别是一些小动效更是让我爱不释手,所以也想仿着来做一点,好吧,啰嗦毕了,看下实现的UI效果。 这个是魅族应用商店下载进度条的进度读取,我挺喜欢这个效果的,具体效果我们分成3部分:

  1. 没开始之前显示 圆角按钮
  2. 点击下载后有一个缩回去的动画,缩成一个圆,然后进行进度条加载
  3. 进度条走到底之后再次横向拉,拉成一个圆角按钮,上面显示安装或者是打开。

下图看下具体的实现效果

gayHub地址:https://github.com/LinHuanTanLy/CircularProgressBar.git

实现过程

需求分析

先动脑再动手。 很明显这里面涉及到了画弧形画圆角矩形画字属性动画,这些单独拆出来问题不大,合并在一起就麻烦点了。 思路:

  1. 先画矩形
  2. 点击 矩形回收,缩到一定倍数后不再画矩形,改而画进度条(虽然是叫进度条,但是我们不考虑用progressbar也不考虑画圆,我们画弧形来实现) 3.根据进度来选择画多少弧度的弧形,当到达360弧度的时候,即进度100%,这个时候不再画弧形,改而画圆角矩形,横向拉伸到圆角矩形的宽度。

具体动手

定义需要的参数

    //    定义 宽 高 半径 开始角度 滑动角度private int mW, mH, mRadius, mStartAngle = 0, mSweepAngle = 0;//    默认弧形的颜色,进度条颜色,暂停杠杠的颜色,字体颜色private int mDefColor, mProgressColor, mTabColor, mFontColor;//   画进度弧度,画中间暂停斜杠,画默认弧度,画文字private Paint mPaintForCircle, mPaintForTag, mPaintForDefCircle, mPaintForText;//   是否画进度条private boolean isDrawProgressBar = false;//   由圆形进度条 进化到  圆角矩形 需要增长的半径的倍数以及最大倍数private final float sMultiple = 1f, sMaxMultiple = 2.5f;//   可变的倍数private float mMultiple = sMaxMultiple;//   进度条的宽度,圆角矩形的宽度private float mStrokeWidthCircle = 12, mRectWidth = 6;//   圆角矩形中的提示文字private String mTipsStr = "下载", mTipsInstallStr = "安装";//   圆角矩形中的提示文字字号大小private float mTipsStrFontSize = 40;//   下载监听private OnProgressListener mOnProgressListener;
复制代码

重写onDraw()

  @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(mW / 2, mH / 2);if (isDrawProgressBar) {drawPauseTag(canvas);drawDefCircle(canvas);drawBigCircle(canvas);} else {drawRoundRect(canvas);}}
复制代码

这里我们根据isDrawProgressBar来判断是画什么图形,相对应的方法如下:

/*** 画中间的暂停按钮** @param canvas*/private void drawPauseTag(Canvas canvas) {mPaintForTag.setColor(mTabColor);RectF rectFLeft = new RectF(-mRadius / 2.6f, -mRadius / 2.4f, -mRadius / 8, mRadius / 2.4f);RectF rectFRight = new RectF(mRadius / 8, -mRadius / 2.4f, mRadius / 2.6f, mRadius / 2.4f);canvas.drawRect(rectFLeft, mPaintForTag);canvas.drawRect(rectFRight, mPaintForTag);}
复制代码
 /*** 画基础的背景圆** @param canvas*/private void drawDefCircle(Canvas canvas) {mPaintForDefCircle.setStrokeWidth(mStrokeWidthCircle);mPaintForDefCircle.setColor(mDefColor);RectF rectF = new RectF(-mRadius, -mRadius, mRadius, mRadius);canvas.drawArc(rectF, mStartAngle, 360, false, mPaintForDefCircle);}
复制代码
  /*** 画进度弧形** @param canvas*/private void drawBigCircle(Canvas canvas) {mPaintForCircle.setColor(mProgressColor);mPaintForCircle.setStrokeWidth(mStrokeWidthCircle);RectF rectF = new RectF(-mRadius, -mRadius, mRadius, mRadius);canvas.drawArc(rectF, mStartAngle, mSweepAngle, false, mPaintForCircle);}复制代码
 /*** 画圆角矩形** @param canvas*/private void drawRoundRect(Canvas canvas) {mPaintForCircle.setStrokeWidth(mRectWidth);mPaintForCircle.setColor(mProgressColor);RectF rectF = new RectF(-mRadius * mMultiple, -mRadius, mRadius * mMultiple, mRadius);canvas.drawRoundRect(rectF, 60, 60, mPaintForCircle);mPaintForCircle.setStrokeWidth(mStrokeWidthCircle);if (mMultiple == sMaxMultiple) {drawTipsText(rectF, canvas);}}复制代码

这时候基本的模板已经画好了,怎么让他动起来呢?我们考虑使用属性动画

属性动画

 /*** 用属性动画绘制组件  开始画圆角矩形*/private void doDrawRect() {ValueAnimator valueAnimator = ValueAnimator.ofFloat(sMultiple, sMaxMultiple);valueAnimator.setDuration(500);valueAnimator.addUpdateListener(animation -> {mMultiple = (float) animation.getAnimatedValue();invalidate();});valueAnimator.start();}/*** 用属性动画绘制组件  开始画进度条*/private void doDrawProgress() {ValueAnimator valueAnimator = ValueAnimator.ofFloat(sMaxMultiple, sMultiple);valueAnimator.setDuration(500);valueAnimator.addUpdateListener(animation -> {float rate = (float) animation.getAnimatedValue();mMultiple = rate;invalidate();if (rate == sMultiple) {isDrawProgressBar = true;invalidate();}});valueAnimator.start();}
复制代码

设置文字

自定义View写文字一向是让人讨厌的,因为baseline实在是不好找(也可能是我太笨),这里是在一个限定好的圆角矩形里面写文字,所以相对比较好处理一些:

  /*** 画矩形框中的文本** @param rectF* @param canvas*/private void drawTipsText(RectF rectF, Canvas canvas) {mPaintForText.setTextSize(mTipsStrFontSize);mPaintForText.setColor(mFontColor);Paint.FontMetricsInt fontMetrics = mPaintForText.getFontMetricsInt();float baseline2 = rectF.bottom - ((rectF.bottom - rectF.top - fontMetrics.bottom + fontMetrics.top) / 2 + fontMetrics.bottom);canvas.drawText(mTipsStr, rectF.centerX(), baseline2, mPaintForText);}
复制代码

自定义属性

  1. value/attrs.xml 定义属性文件:
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="circular"><!--默认弧度的颜色--><attr name="defColor" format="color"/><!--进度条颜色--><attr name="progressColor" format="color"/><!--暂停杠杠的颜色--><attr name="tabColor" format="color"/><!--字体颜色--><attr name="fontColor" format="color"/><!--提示文字--><attr name="tipsStr" format="string"/><!--进度完成后的提示文字--><attr name="finishStr" format="string"/><!--字体大小--><attr name="fontSize" format="dimension"/><!--圆角矩形的宽度--><attr name="rectWidth" format="dimension"/><!--进度条宽度--><attr name="progressWidth" format="dimension"/></declare-styleable>
</resources>
复制代码
  1. 构造方法中获取属性文件
 private void init(Context context, AttributeSet attrs) {TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.circular);mDefColor = typedArray.getColor(R.styleable.circular_defColor, Color.parseColor("#9E9E9E"));mProgressColor = typedArray.getColor(R.styleable.circular_progressColor, Color.parseColor("#FF34A350"));mTabColor = typedArray.getColor(R.styleable.circular_tabColor, Color.parseColor("#FF34A350"));mFontColor = typedArray.getColor(R.styleable.circular_fontColor, Color.parseColor("#FF34A350"));mTipsStr = typedArray.getString(R.styleable.circular_tipsStr);mStrokeWidthCircle = typedArray.getDimension(R.styleable.circular_progressWidth, 12);mRectWidth = typedArray.getDimension(R.styleable.circular_rectWidth, 6);if (TextUtils.isEmpty(mTipsStr)) {mTipsStr = "下载";}mTipsInstallStr = typedArray.getString(R.styleable.circular_finishStr);if (TextUtils.isEmpty(mTipsInstallStr)) {mTipsInstallStr = "安装";}mTipsStrFontSize = typedArray.getDimension(R.styleable.circular_fontSize, 40);typedArray.recycle();mPaintForCircle = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintForCircle.setStyle(Paint.Style.STROKE);mPaintForTag = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintForDefCircle = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintForDefCircle.setStyle(Paint.Style.STROKE);mPaintForText = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintForText.setTextAlign(Paint.Align.CENTER);}复制代码

暴露更新进度的方法供外界调用:

  /*** 是不是正在显示进度中** @return*/public boolean isInProgress() {return isDrawProgressBar;}/*** 开始显示下载进度*/public void doStartProgress() {doDrawProgress();}/*** 暂停下载进度*/public void doPauseProgress() {isDrawProgressBar = false;doDrawRect();}/*** 完成下载进度*/public void doFinishProgress() {mSweepAngle = 360;isDrawProgressBar = false;mTipsStr = mTipsInstallStr;doDrawRect();if (mOnProgressListener != null) {mOnProgressListener.onProgressFinish();mSweepAngle = 0;}}/*** 更新进度** @param curProgress 当前进度* @param maxProgress 全部进度*/public void setProgress(int curProgress, int maxProgress) {if (curProgress < maxProgress) {BigDecimal result = new BigDecimal(curProgress).divide(new BigDecimal(maxProgress), 2, BigDecimal.ROUND_HALF_DOWN).multiply(new BigDecimal(360));mSweepAngle = result.intValue();} else {mSweepAngle = 360;}invalidate();}/*** 默认弧形的颜色** @param colorRgb*/public void setDefColor(int colorRgb) {mDefColor = colorRgb;}/*** 进度条颜色** @param colorRgb*/public void setProgressColor(int colorRgb) {mProgressColor = colorRgb;}/*** 暂停杠杠的颜色** @param colorRgb*/public void setTabColor(int colorRgb) {mTabColor = colorRgb;}/*** 设置进度条宽度** @param progressWidth*/public void setProgressWidth(int progressWidth) {mStrokeWidthCircle = (float) progressWidth;}/*** 设置圆角矩形的宽度** @param rectWidth*/public void setRectWidth(int rectWidth) {mRectWidth = rectWidth;}/*** 设置矩形方框里面的信息文字** @param msg*/public void setTipsMessage(String msg) {mTipsStr = msg;}/*** 设置进度走完后显示的信息** @param msg*/public void setTipsFinish(String msg) {mTipsInstallStr = msg;}/*** 设置字体大小** @param fontSize*/public void setTipsMessageSize(int fontSize) {mTipsStrFontSize = fontSize;}/*** 设置字体颜色** @param colorRgb*/public void setFontColor(int colorRgb) {mFontColor = colorRgb;}/*** 監聽回調** @param onProgressListener*/public void setOnProgressListener(OnProgressListener onProgressListener) {mOnProgressListener = onProgressListener;}public interface OnProgressListener {void onProgressFinish();}
复制代码

使用方法:

  1. xml定义:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="@dimen/padding_10"><ImageViewandroid:layout_width="80dp"android:layout_height="80dp"android:src="@mipmap/icon"/><LinearLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_marginLeft="@dimen/margin_20"android:layout_weight="1"android:gravity="center_vertical"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="QQ华夏手游"android:textColor="@android:color/black"android:textSize="@dimen/fonts_18"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="@dimen/margin_4"android:text="11.8MB"android:textColor="@android:color/black"android:textSize="@dimen/fonts_12"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="@dimen/margin_4"android:text="3000px 超高清体验"android:textColor="@android:color/black"android:textSize="@dimen/fonts_12"/></LinearLayout><com.playingjoy.fanrabbit.widget.CircularProgressBarandroid:id="@+id/cpb_test_progress"android:layout_width="80dp"android:layout_height="40dp"android:layout_gravity="center"android:layout_marginBottom="8dp"android:layout_marginEnd="8dp"android:layout_marginStart="8dp"android:layout_marginTop="8dp"app:defColor="@color/x_red"app:finishStr="@string/app_name"app:rectWidth="@dimen/margin_2"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/>
</LinearLayout>
复制代码
  1. java 定义
public class TestActivity extends RxActivity {private CircularProgressBar mCircularProgressBar;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);mCircularProgressBar = findViewById(R.id.cpb_test_progress);
//        mCircularProgressBar.setTipsMessage("下载");
//        mCircularProgressBar.setDefColor(getResources().getColor(R.color.x_red));
//        mCircularProgressBar.setFontColor(getResources().getColor(R.color.x_yellow));
//        mCircularProgressBar.setTabColor(getResources().getColor(R.color.x_blue));
//        mCircularProgressBar.setProgressColor(getResources().getColor(R.color.colorPrimaryDark));
//        mCircularProgressBar.setProgressWidth(4);
//        mCircularProgressBar.setRectWidth(1);
//        mCircularProgressBar.setTipsFinish("Ly");mCircularProgressBar.setOnClickListener(v -> {if (mCircularProgressBar.isInProgress()) {mCircularProgressBar.doPauseProgress();} else {doStartDownLoad();}});}private void doStartDownLoad() {mCircularProgressBar.doStartProgress();io.reactivex.disposables.Disposable disposable = io.reactivex.Observable.interval(1, TimeUnit.SECONDS).compose(bindToLifecycle()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(aLong -> {XLog.e("the progress is " + aLong + 1);if (aLong >= 9) {mCircularProgressBar.doFinishProgress();} else {mCircularProgressBar.setProgress((int) ((aLong + 1) * 10), 100);}});mCircularProgressBar.setOnProgressListener(disposable::dispose);}
}复制代码

OK,基本就完成了

问题

因为本人也是自定义View初学者,所以很多问题我也不是很清楚是不是会造成问题或者是性能损耗:

  1. 这样画多个图形来完成页面会不会有性能损耗?
  2. 我这边定义了mPaintForCircle, mPaintForTag, mPaintForDefCircle, mPaintForText四只画笔来完成绘图,会不会太多了呢?是一个图形对应一个画笔还是可以复用呢?

仿魅族应用商店下载进度控件相关推荐

  1. Android之旅:仿魅族应用商店下载进度控件

    前言 实现过程 需求分析 具体动手 定义需要的参数 重写onDraw() 属性动画 设置文字 自定义属性 暴露更新进度的方法供外界调用: 使用方法: 问题 前言 因为本人是个魅族控,魅族的UI以及页面 ...

  2. CGContextRef绘图-iOS球形波浪加载进度控件-HcdProcessView详解

    简书也有发布:http://www.jianshu.com/p/20d7... <iOS球形波浪加载进度控件-HcdProcessView>这篇文章已经展示了我在项目中编写的一个球形进度加 ...

  3. [.ashx檔?泛型处理程序?]基础入门#5....ADO.NET 与 将DB里面的二进制图片还原 (范例下载 大型控件的ImageField)...

    [.ashx檔?泛型处理程序?]基础入门#5....ADO.NET 与 将DB里面的二进制图片还原 (范例下载 & 大型控件的ImageField) http://www.dotblogs.c ...

  4. Android之自定义一个环形进度控件

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/53445072 本文出自:[顾林海的博客] ##前言 最近看到一个评分和 ...

  5. C#仿QQ皮肤-常用用户控件EnterFrom1和窗体EntryForm的实现

    导读部分 ----------------------------------------------------------------------------------------------- ...

  6. 示例:WPF开发的步骤进度控件

    一.目的:分享一个WPF中级控件,Step步骤控件,主要用来显示复杂步骤进度 二.实现: 1.步骤个数可以动态配置 2.正在运行步骤动画效果 3.已完成步骤.运行中步骤.错误步骤等状态 4.正在运行可 ...

  7. 仿苹果AppStore 环形下载进度条

    以前项目自己写的 ,一个模仿苹果AppStore 下载进度条的winfrom用户控件,GDI绘制.效果如图 1 using System.Drawing; 2 using System.Windows ...

  8. Android仿饿了么加减控件,Flutter + Native混合栈仿饿了么APP

    前言 一个基于Flutter + Native混合开发的APP,请求数据均人为制造. 目前仅上传Android版本,iOS暂未上传 APK下载 Github地址 效果图: 实现功能: 首页 使用百度定 ...

  9. 云炬Android开发笔记 15评价晒单功能实现(自定义评分控件和仿微信自动多图选择控件)

    阅读目录 1. 晒单评价 1.1 点击页面跳转的实现 1.2 自定义评价订单的布局实现 1.3 星星布局的实现 2. 仿微信自动多图及删除控件 2.1 属性值及控件的定义 2.2 图片初始化方法onM ...

最新文章

  1. 深海中的STL—nth_element
  2. linux编程综合案例
  3. LeetCode-链表-面试题 02.07. 链表相交
  4. Hadoop LZO的安装与配置
  5. 模拟电梯1.0(类与对象实验)
  6. 操作系统【一】进程同步和信号量
  7. 动手写了一个12306插件 chrome浏览器
  8. django-配置静态文件路径
  9. NLTK使用英文词性还原
  10. java程序设计专业介绍_简介Java编程中的Object类
  11. django分页-Paginator类
  12. CatBoost快速入门
  13. 张小七的C#语言笔记
  14. linux中文件权限为drwxr,linux drwxr-xr-x 什么意思 ?
  15. 歌词big big no_编程的第二个十年:Big Iron
  16. 【智能安全车载中控系统】(一)概览
  17. 百度地图API入门1-申请百度API key
  18. 什么是虚拟主播?虚拟数字人直播,不用出镜,不用露脸的直播方式
  19. 每天一个---- 吉尔德定律和迈特卡尔定律
  20. 垃圾收集器G1和ZGC详解

热门文章

  1. bonsai软件使用经验(1)
  2. 苏珊米勒 10月占星运势
  3. 数字化转型,究竟在“转”什么?
  4. 向量范数:1-范数、2-范数、无穷范数;矩阵范数;欧几里得度量
  5. 在深圳地铁卡余额不足没带现金出不去怎么办?
  6. 拜耳加大在华投资,启动处方药北京工厂产能提升项目
  7. 计算机机房一般在几楼,电梯机房一般在几楼 设备层属于公摊吗
  8. poj1034 The dog task 最大匹配
  9. 发点随感——踏实做好一件事
  10. antd pro mysql_antd pro 路由