自定义进度条PictureProgressBar——从开发到开源发布全过程

出处:
炎之铠邮箱:yanzhikai_yjk@qq.com
本文原创,转载请注明本出处!
本项目JCenter地址:https://www.yuheng119.com/ /yanzhikaijky/CustomViewRepository/PictureProgressbar/
本项目GitHub地址:https://github.com/totond/PictureProgressBar
欢迎 Star or Fork和在Issue里提出意见建议!
前言

  上一篇文章掌握了ProgressBar的自定义样式和它的扩展ProgressDialog,但是没有进行封装,这一次就继承View从零开始做了一个自定义进度条——PictureProgressBar,并发布到Github和JCenter上,下面就开始一步一步介绍这个过程。

PS:JCenter是一个Android的代码库,把代码放上去,就可以在AS项目里的Gradle文件里compile ‘xxx’这样来引入你的代码了。
本文涉及到:

一个继承自View的自定义ProgressBar实现全过程
一个项目开源的全过程:使用AndroidStudio上传代码到GitHub、JCenter的过程,添加开源协议的过程等。
实现

  PictureProgressBar是一个可以带图片和动画效果的进度条,可以先看看它的效果,如下图:

  实现的逻辑并不复杂,看看流程图:

  主要的逻辑是在onDraw()方法实现,里面大量利用到Canvas,Canvas的使用可以参考下我以前这篇笔记。

1.初始化属性

  由于前面的属性定义太多了,所以这里不列出来,后面要用到的属性会有介绍,想详细了解的可以看GitHub的介绍文档,那里有个表详细介绍。这里定义初始化方法,用来配置画笔和设置Gradient渐变器,由于Gradient需要进度条的宽高,所以要在Measure过程之后才配置:

//初始化
private void init() {
//初始化画笔
paintPicture = new Paint();

paintBackGround = new Paint();
paintBackGround.setColor(backGroundColor);

paintBar = new Paint();
paintBar.setColor(barColor);

if (isGradient) {
//在PreDraw时获取View属性,因为在初始化的时候View还没进行Measure
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver(www.xuancayule.com).removeOnPreDrawListener(this);
linearGradient = new LinearGradient(0, progressHeight / 2, progressWidth, progressHeight / 2, gradientStartColor, gradientEndColor, Shader.TileMode.CLAMP);
paintBar.setShader(linearGradient);
return false;
}
});
}

2.画进度条

  首先就是要画好进度条,Android源码自带的ProgressBar是基于事件机制来刷新View的,也就是每当有进度改变才会调用刷新View的方法,但是因为我这里要实现动画而且对怎么实现事件机制不是很熟,所以采用了定时刷新的方法,先把进度条画出来:

//画进度条
private void drawBar(Canvas canvas){
if (isRound) {
//画圆角矩形
rectFBG.set(0, y - progressHeight / 2 + progressHeightOffset,
progressWidth, y + progressHeight / 2 + progressHeightOffset);
canvas.drawRoundRect(rectFBG, roundX, roundY, paintBackGround);
rectFPB.set(0, y - progressHeight / 2 + progressHeightOffset,
x, y + progressHeight / 2 + progressHeightOffset);
canvas.drawRoundRect(rectFPB, roundX, roundY, paintBar);
} else {
//画矩形
rectFBG.set(0, 0, getWidth(), getHeight());
canvas.drawRect(rectFBG, paintBackGround);
canvas.drawRect(0, 0, x, getHeight(www.yigouyule2.cn/), paintBar);
}
}
  简单说一下上面的一些属性:
- isRound是决定进度条是否圆角的boolean变量,由于觉得不是圆角的进度条有点难看,所以就最终发布时默认初始设置是true。
- progressWidth和progressHeight是进度条的宽高,而不是整个View的宽高,因为View是包括进度条和图片,要为图片的显示预留空间,所以进度条宽高会在onMeasure()根据属性设置来定义大小(具体怎么定义后面说)。
- x和y是当前进度的中心点坐标位置。
- progressHeightOffset是进度条的所处高度偏移量,负数为向上偏移,正数为向下偏移。前面就说过progressHeight是进度条的宽高不一定是整个View的宽高,所以进度条可以处于一个自定义的位置(目前仅仅是高度,因为一般都不用设置宽度)。具体的效果可以看前面demo效果的第一个,进度条就是向下偏移而实现了被可爱的丘比龙踩在脚下的效果。

3.画图片Drawable

  接下来是画图片Drawable的方法,这个Drawable可以是图片或者是Shape,根据当前进度的中心点坐标x、y和图片的半宽高属性halfDrawableWidth、halfDrawableHeight来实现,其中drawableHeightOffset是图片的高度偏移量:

//画图
private void drawPicture(Canvas canvas) {
if (drawable == null && animMode != ANIM_NULL){
Log.e(TAG,"drawable is null");
return;
}
drawable.setBounds(x - halfDrawableWidth,
getHeight() / 2 - http://027yeshenghuowang.com/ + drawableHeightOffset,
x + halfDrawableWidth,
getHeight() / 2 + halfDrawableHeight + drawableHeightOffset);
drawable.draw(canvas);
}

4.画动画:

  先对是否开启动画,当前动画模式做出判断,实现5个动画模式:

animMode模式 意义
ANIM_NULL 无动画模式
ANIM_ROTATE 旋转动画模式
ANIM_SCALE 缩放动画模式
ANIM_ROTATE_SCALE 旋转加缩放动画模式
ANIM_FRAME 帧动画模式
//画动画
private void drawAnimPicture(Canvas canvas) {
if (isAnimRun) {
switch (animMode) {
case ANIM_NULL:
drawPicture(canvas);
break;
case ANIM_ROTATE:
rotateCanvas(canvas);
drawPicture(canvas);
break;
case ANIM_SCALE:
scaleCanvas(canvas);
drawPicture(canvas);
break;
case ANIM_ROTATE_SCALE:
rotateCanvas(canvas);
scaleCanvas(canvas);
drawPicture(canvas);
break;
case ANIM_FRAME:
drawable = getResources().getDrawable(drawableIds[frameIndex]);
drawPicture(canvas);
if (frameIndex >= drawableIds.length - 1){
frameIndex = 0;
}else {
frameIndex++;
}
break;
}
} else {
drawPicture(canvas);
}
}

  实现帧动画是通过轮播图片的方法实现。
  实现旋转,缩放的效果,是采用操纵画布Canvas的方法来实现:

//旋转画布
private void rotateCanvas(Canvas canvas) {
canvas.rotate(rotateDegree % 360, x, y + drawableHeightOffset);
rotateDegree += rotateRate;
}

//伸缩画布
private void scaleCanvas(Canvas canvas) {
if (scaleLevel >= scaleMax) {
isScaleIncrease = false;
} else if (scaleLevel <= scaleMin) {
isScaleIncrease = true;
}
if (isScaleIncrease) {
scaleLevel += scaleRate;
} else {
scaleLevel -= scaleRate;
}
canvas.scale(scaleLevel, scaleLevel, x, y + drawableHeightOffset);
}

20
  由于drawAnimPicture()方法之后并没有其他使用Canvas的方法了,所以这里不用Canvas.save()和Canvas.restore()来使Canvas恢复到初始状态了,这里说明一下,免得后面有功能拓展的需要加代码时候忘了。

5.重写onMeasure()

  重写onMeasure(www.22yigouyule.cn/)的意义:让View支持wrap_content,还有设置了进度条的宽高(前面说过,进度条的宽高不一定等于整个View的宽高):

//重写onMeasure,以自定义获取进度条的宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);

if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
//在这里实现计算需要wrap_content时需要的宽
width = halfDrawableWidth * 2;
}
if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
//在这里实现计算需要wrap_content时需要的高
height = halfDrawableHeight * 2;
}

progressWidth = width;
//如果不是自定义设置进度条高度,就直接把高度当作进度条高度
if (!isSetBar) {
progressHeight = height;
}

//如果有图片,就为图片预留空间
if (drawable != null) {
progressWidth = width - halfDrawableWidth;
}

//传入处理后的宽高
setMeasuredDimension(width, height);
}

6.封装

自定义属性

  为了让自定义View的属性能直接通过XML设置,需要用到自定义属性,在res/value文件夹里新建一个attrs.xml(名字随便,建立位置对就行),定义自己所需的属性和相应类型:

<?xml version="1.0" encoding="utf-8"?www.feifanyule.cn>
<resources>
<declare-styleable name="PictureProgressBar">
<attr name="backGroundColor" format="color"/>
<attr name="barColor" format="color"/>
<attr name="drawable" format="reference"/>
<attr name="halfDrawableWidth" format="dimension"/>
<attr name="halfDrawableHeight" format="dimension"/>
<attr name="drawableHeightOffset" format="dimension"/>
<attr name="isRound" format="boolean"/>
<attr name="roundX" format="dimension"/>
<attr name="roundY" format="dimension"/>
<attr name="progress" format="integer"/>
<attr name="max" format="integer"/>
<attr name="isSetBar" format="boolean"/>
<attr name="progressHeight" format="dimension"/>
<attr name="progressHeightOffset" format="dimension"/>
<attr name="refreshTime" format="integer"/>
<attr name="animMode" format="enum">
<enum name="ANIM_NULL" value="0"/>
<enum name="ANIM_ROTATE" value="1"/>
<enum name="ANIM_SCALE" value="2"/>
<enum name="ANIM_ROTATE_SCALE" value="3"/>
<enum name="ANIM_FRAME" value="4"/>
</attr>
<attr name="rotateRate" format="integer"/>
<attr name="rotateDegree" format="integer"/>
<attr name="scaleMax" format="float"/>
<attr name="scaleMin" format="float"/>
<attr name="scaleRate" format="float"/>
<attr name="isGradient" format="boolean"/>
<attr name="gradientStartColor" format="color"/>
<attr name="gradientEndColor" format="color"/>
</declare-styleable>
</resources>

一些set、get方法和其他

  有了自定义属性,只是能在XML上使用,想要在Java代码上设置属性,还需要弄一些set、get方法,还有一些特殊的属性,在xml设置不了,如帧动画的图片id数组、线性渐变器、进度监听器等,也需要set方法,因为有太多,下面只列举一些特殊的出来:

自定义进度条PictureProgressBar——从开发到开源发布全过程的更多相关文章

  1. 自己定义进度条PictureProgressBar——从开发到开源公布全过程

    自己定义进度条PictureProgressBar--从开发到开源公布全过程 出处: 炎之铠邮箱:yanzhikai_yjk@qq.com 本文原创.转载请注明本出处! 本项目JCenter地址:ht ...

  2. C# 根据BackgroundWoker异步模型和ProgressBar控件,自定义进度条控件

    前言 程序开发过程中,难免会有的业务逻辑,或者算法之类产生让人能够感知的耗时操作,例如循环中对复杂逻辑处理;获取数据库百万乃至千万级数据;http请求的时候等...... 用户在使用UI操作并不知道程 ...

  3. android 自定义进度条颜色

    android 自定义进度条颜色 先看图 基于产品经理各种自定义需求,经过查阅了解,下面是自己对Android自定义进度条的学习过程!   这个没法了只能看源码了,还好下载了源码, sources\b ...

  4. Qt之模型/视图(自定义进度条)

    简述 在之前的章节中分享过关于QHeaderView表头排序.添加复选框等内容,相信大家模型/视图.自定义风格有了一定的了解,下面我们来分享一个更常用的内容-自定义进度条. 实现方式: 从QAbstr ...

  5. android113 自定义进度条

    MainActivity: package com.itheima.monitor; import android.os.Bundle; import android.app.Activity; im ...

  6. BitBlt()函数实现带数字百分比进度条控件、静态文本(STATIC)控件实现的位图进度条、自定义进度条控件实现七彩虹颜色带数字百分比

    Windows API BitBlt()函数实现带数字百分比进度条控件. 有两个例子:一用定时器实现,二用多线程实现. 带有详细注解. 此例是本人原创,绝对是网上稀缺资源(本源码用Windows AP ...

  7. 最简单的android自定义进度条样式

    一.自定义圆形进度条样式 1.在安卓项目drawable目录下新建一个xml文件如下:<?xml version="1.0" encoding="utf-8&quo ...

  8. iOS 自定义进度条

    自定义条形进度条(iOS) ViewController.m文件 #import "ViewController.h" @interface ViewController () @ ...

  9. Android_自定义进度条

    转载:http://blog.csdn.net/lmj623565791/article/details/43371299 ,本文出自:[张鸿洋的博客] 1.概述 最近需要用进度条,秉着不重复造轮子的 ...

随机推荐

  1. Vue2.0 探索之路——vuex入门教程和思考

    Vuex是什么 首先对于vuex是什么,我先引用下官方的解释. Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可 ...

  2. python-socket2

    UDP,服务端 #! /usr/bin/env python #coding=utf-8 import socket #创建socket,指定ipv4,udp类型 s = socket.socket( ...

  3. Java之 将程序打包成jar包

    准备材料: 1.java文件: Helloworld.java package com.ray; public class HelloWorld{ public static void main(St ...

  4. BEC listen and translation exercise 49

    Astronaut Sounds Alarm on Asteroids If a big asteroid with Earth's name on it were to reach us unimp ...

  5. BEC listen and translation exercise 43

    Reach for the stars so if you fall you land on a cloud.飞向星空吧,就算坠落,接住你的也是云彩. Anyway, exam failure can ...

  6. linux 故障:df -h统计磁盘空间占用太多,但又du -h找不到大的文件

    用lsof / | grep -i delete 从根目录定位打开的被删除的文件 如果定位到某文件占用空间很大 主要是因为我们在删除这个日志文件的时候是用rm -rf *.log这样的命令删除的,删除 ...

  7. Statement

    题目大意 给定一棵基环外向树,和若干组询问,对于每次独立的询问都指定一些起点和一些终点,你删去一些边,使得从任意起点出发都无法到达终点,并让删去的边的编号的最小值最大,求这个最大的最小值. 题解 不难 ...

  8. ACM学习历程—CSU 1216 异或最大值(xor && 贪心 && 字典树)

    题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1216 题目大意是给了n个数,然后取出两个数,使得xor值最大. 首先暴力枚举是C(n,  ...

  9. 【C&C++】查看代码运行时间

    查看代码运行时间有助于更好地优化项目代码 1. Windows平台 windows平台下有两种方式,精度有所不同,都需要包含<windows.h>头文件 1) DWORD GetTickC ...

  10. CentOS6.5中的vsftpd安装配置

    安装ftp 1.使用chkconfig 来查看是否装有vsftpd服务: 2.使用yum命令直接安装:yum -y install vsftpd 3.然后为它创建日志文件:touch /var/log ...