博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义进度条PictureProgressBar——从开发到开源发布全过程
阅读量:4991 次
发布时间:2019-06-12

本文共 7098 字,大约阅读时间需要 23 分钟。

自定义进度条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方法,因为有太多,下面只列举一些特殊的出来:

转载于:https://www.cnblogs.com/chenergougou/p/6877529.html

你可能感兴趣的文章
Java学习笔记()ArrayList
查看>>
redis缓存清除
查看>>
django Highcharts制作图表--显示CPU使用率
查看>>
文本处理 tr ,col,join,paste
查看>>
oracle权限
查看>>
java方法的虚分派和方法表
查看>>
【转】字符串和浮点数格式化输出小结
查看>>
Android开发 - Retrofit 2 使用自签名的HTTPS证书进行API请求
查看>>
对测试人员或开发人员来说相互沟通有多重要?
查看>>
解释器、编译器以及他们之间的差别。
查看>>
MongoDB的快速手动安装
查看>>
JS制作简单的日历控件【JS Date对象操作实例演示】
查看>>
模板—树上倍增LCA
查看>>
高二小假期集训—D5
查看>>
EasyUI easyui-combobox 重复发送请求
查看>>
memcached-repcached
查看>>
[转]CentOS 5.3通过yum升级php到最新版本的方法
查看>>
UVA 11235 - Frequent values RMQ的应用
查看>>
大数据日志采集系统
查看>>
java 堆调优
查看>>