当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义View之圆形进度条式按钮

Android自定义View之圆形进度条式按钮

2019年07月24日  | 移动技术网移动技术  | 我要评论

介绍

今天上班的时候有个哥们问我怎么去实现一个按钮式的进度条,先来看看他需要实现的效果图。

这里写图片描述

和普通的圆形进度条类似,只是中间的地方有两个状态表示,未开始,暂停状态。而且他说圆形进度的功能已经实现了。那么我们只需要对中间的两个状态做处理就行了。

先来看看实现的效果图:

这里写图片描述

上面说了我们只需要处理中间状态的变化就可以了,对于进度的处理直接使用了弘洋文章中实现:
http://blog.csdn.net/lmj623565791/article/details/43371299

下面开始具体实现。

具体实现

自定义的实现还是按照官方提供的步骤来,对于自定义view的步骤之前我也写过一篇文章,感兴趣的朋友可以看一下:android自定义view的官方套路。

为了完整讲解,下面还是会提到圆形进度条的自定义,知道进度的实现可以直接跳过部分步骤。

1、创建view

观察要实现的外圈进度条,有两个进度:一个用来表示默认的圆形,另一个表示进度的颜色。所以这里涉及到两个进度条颜色宽高的定义。要绘制圆肯定需要半径了。

创建view有三小步

(1)、定义属性

<declare-styleable name="buttoncircleprogressbar">
<!--无进度时的颜色-->
<attr name="progress_unreached_color" format="color" />
<!--进度颜色-->
<attr name="progress_reached_color" format="color" />
<!--进度条的高-->
<attr name="progress_reached_bar_height" format="dimension" />
<!--无进度时的边框高-->
<attr name="progress_unreached_bar_height" format="dimension" />
<!--圆的半径-->
<attr name="radius" format="dimension" />
</declare-styleable>

(1)、定义属性变量以及构造方法中获取属性

private static final int default_text_color = 0xfffc00d1;
private static final int default_color_unreached_color = 0xffd3d6da;
private static final int default_height_reached_progress_bar = 2;
private static final int default_height_unreached_progress_bar = 2;
/**
* the status of this view currently;
*/
private status mstatus = status.end;
/**
* painter of all drawing things
*/
protected paint mpaint = new paint();
/**
* height of reached progress bar
*/
protected int mreachedprogressbarheight = dp2px(default_height_reached_progress_bar);
/**
* color of reached bar
*/
protected int mreachedbarcolor = default_text_color;
/**
* color of unreached bar
*/
protected int munreachedbarcolor = default_color_unreached_color;
/**
* height of unreached progress bar
*/
protected int munreachedprogressbarheight = dp2px(default_height_unreached_progress_bar);
/**
* the length of triangle
*/
private int trianglelength;
/**
* use path to draw triangle
*/
private path mpath;
/**
* mradius of view
*/
private int mradius = dp2px(30);
public buttoncircleprogressbar(context context) {
this(context,null);
}
public buttoncircleprogressbar(context context, attributeset attrs) {
this(context, attrs,0);
}
public buttoncircleprogressbar(context context, attributeset attrs, int defstyleattr) {
super(context, attrs, defstyleattr);
// init values from custom attributes
final typedarray attributes = getcontext().obtainstyledattributes(
attrs, r.styleable.buttoncircleprogressbar);
mreachedbarcolor = attributes
.getcolor(
r.styleable.buttoncircleprogressbar_progress_reached_color,
color.blue);
munreachedbarcolor = attributes
.getcolor(
r.styleable.buttoncircleprogressbar_progress_unreached_color,
default_color_unreached_color);
mreachedprogressbarheight = (int) attributes
.getdimension(
r.styleable.buttoncircleprogressbar_progress_reached_bar_height,
mreachedprogressbarheight);
munreachedprogressbarheight = (int) attributes
.getdimension(
r.styleable.buttoncircleprogressbar_progress_unreached_bar_height,
munreachedprogressbarheight);
mradius = (int) attributes.getdimension(
r.styleable.buttoncircleprogressbar_radius, mradius);
trianglelength = mradius;
attributes.recycle();
mpaint.setstyle(paint.style.stroke);
mpaint.setantialias(true);
mpaint.setdither(true);
mpaint.setstrokecap(paint.cap.round);
mpath = new path();//need path to draw triangle
}
public status getstatus() {
return mstatus;
}
public void setstatus(status status) {
mstatus = status;
invalidate();
}
public enum status{
end,
starting
}

获取基础的一些属性,这里mstatus用来表示当前view的状态:end代码结束,starting正在进行。我们用这两个状态来判定怎么去draw去合适的效果。提供了setstatus为staus设置状态。

mpath用来进行绘制未开始时候的三角形。

2、处理view的布局

这一步主要是onmeasure方法中测量出合适的宽高。

@override
protected synchronized void onmeasure(int widthmeasurespec, int heightmeasurespec) {
int heightmode = measurespec.getmode(heightmeasurespec);
int widthmode = measurespec.getmode(widthmeasurespec);
int paintwidth = math.max(mreachedprogressbarheight,
munreachedprogressbarheight);
if (heightmode != measurespec.exactly) {
int exceptheight = (int) (getpaddingtop() + getpaddingbottom()
+ mradius * 2 + paintwidth);
heightmeasurespec = measurespec.makemeasurespec(exceptheight,
measurespec.exactly);
}
if (widthmode != measurespec.exactly) {
int exceptwidth = (int) (getpaddingleft() + getpaddingright()
+ mradius * 2 + paintwidth);
widthmeasurespec = measurespec.makemeasurespec(exceptwidth,
measurespec.exactly);
}
super.onmeasure(widthmeasurespec, heightmeasurespec);
}

只需要处理宽高没有精确指定的情况,通过padding加上整个圆以及paint的宽度计算出具体的值。

接下来就是第三步,绘制效果。

3、绘制view

为了更加清晰一点,这里先说绘制圆的进度,再说圆中间的状态。

(1)、绘制圆

@override
protected synchronized void ondraw(canvas canvas) {
super.ondraw(canvas);
canvas.save();
canvas.translate(getpaddingleft(), getpaddingtop());
mpaint.setstyle(paint.style.stroke);
// draw unreaded bar
mpaint.setcolor(munreachedbarcolor);
mpaint.setstrokewidth(munreachedprogressbarheight);
canvas.drawcircle(mradius, mradius, mradius, mpaint);
// draw reached bar
mpaint.setcolor(mreachedbarcolor);
mpaint.setstrokewidth(mreachedprogressbarheight);
float sweepangle = getprogress() * 1.0f / getmax() * 360;
canvas.drawarc(new rectf(0, 0, mradius * 2, mradius * 2), 0,
sweepangle, false, mpaint);

通过 canvas.drawcircle(mradius, mradius, mradius, mpaint);绘制默认状态下的圆。之后改变画笔的颜色,根据进度绘制圆弧。

(2)、绘制中间的状态。

第一种是未开始的情况,中间是一个三角形。我们使用path来绘制三角形,主要通过 moveto(float x, float y)来设置第一个点,然后通过lineto(float x, float y)来连接一个三角形。再设置paint为填充。

第一个点这里设置为三角形的左上角的顶点。

那么第一个点怎么算?

我们这里绘制一个等边三角形,设置边长等于半径。

第一个点的x坐标就应该是圆的直径减去三角形的高之后除以2,即:

float leftx = (float) ((2*mradius-math.sqrt(3.0)/2*trianglelength)/2); 

y坐标就为:mradius-(trianglelength/2)

第二个点这里选三角形的左下角,x坐标不变,y值为半径加上边长的一半:mradius+(trianglelength/2)

第三个点选右边的点,x点的坐标显然就是第一个点的x坐标加上三角形的高
即:(float) (leftx+math.sqrt(3.0)/2*trianglelength),y点坐标就是半径mradius。

最后再回到第一个点就连接成三角形了。

mpath设置的完整代码如下

public class buttoncircleprogressbar extends progressbar {
.........
mpath = new path();//need path to draw triangle
trianglelength = mradius;
float leftx = (float) ((2*mradius-math.sqrt(3.0)/2*trianglelength)/2);
float realx = (float) (leftx+leftx*0.2);
mpath.moveto(realx,mradius-(trianglelength/2));
mpath.lineto(realx,mradius+(trianglelength/2));
mpath.lineto((float) (realx+math.sqrt(3.0)/2*trianglelength),mradius);
mpath.lineto(realx,mradius-(trianglelength/2));
}

这里用了realx设置成了leftx的两倍,是因为我感觉三角形设置在中间的效果不太好,所以让他在原有基础上增加0.2倍的距离。

有了mpath变量之后就可以在ondraw中绘制未开始状态的三角形了,看代码

@override
protected synchronized void ondraw(canvas canvas) {
super.ondraw(canvas);
canvas.save();
canvas.translate(getpaddingleft(), getpaddingtop());
....
if (mstatus==status.end){//未开始状态,画笔填充
mpaint.setstyle(paint.style.fill);
canvas.drawpath(mpath,mpaint);//直接drawpath
}else{
mpaint.setstyle(paint.style.stroke);
mpaint.setstrokewidth(dp2px(5));
canvas.drawline(mradius*2/3,mradius*2/3,mradius*2/3,2*mradius*2/3,mpaint);
canvas.drawline(2*mradius-(mradius*2/3),mradius*2/3,2*mradius-(mradius*2/3),2*mradius*2/3,mpaint);
}
canvas.restore();
}

进行中的状态就是画两条线,第一条线x直接设为半径的2/3倍,起始y点为2/3倍,结束为开始y点的2/3倍

对与另外一条线,x点直径减去mradius*2/3,y点坐标的变化和上一条线一样。

这样就完成了ondraw方法。

4、处理用户交互

由于对于下载更新进度的情况来说,该控件只做状态显示,所以这一步不需要了,要使用的话自己设置点击事件就可以了。

使用

<?xml version="1.0" encoding="utf-8"?>
<relativelayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingbottom="@dimen/activity_vertical_margin"
android:paddingleft="@dimen/activity_horizontal_margin"
android:paddingright="@dimen/activity_horizontal_margin"
android:paddingtop="@dimen/activity_vertical_margin"
tools:context="com.qiangyu.test.buttoncircleprogress.mainactivity">
<com.qiangyu.test.buttoncircleprogress.view.buttoncircleprogressbar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margintop="50dip"
android:padding="5dp"
android:progress="30" />
</relativelayout>

activity里设置点击事件修改状态,具体根据自己逻辑处理。

public class mainactivity extends appcompatactivity {
private buttoncircleprogressbar mprogressbar;
private static final int msg_progress_update = 0x110;
private int progress;
private handler mhandler = new handler() {
public void handlemessage(android.os.message msg) {
progress = mprogressbar.getprogress();
mprogressbar.setprogress(++progress);
if (progress >= 100) {
mhandler.removemessages(msg_progress_update);
progress = 0;
mprogressbar.setstatus(buttoncircleprogressbar.status.end);
mprogressbar.setprogress(0);
}else{
mhandler.sendemptymessagedelayed(msg_progress_update, 100);
}
};
};
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
mprogressbar = (buttoncircleprogressbar) findviewbyid(r.id.progressbar);
mprogressbar.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view view) {
if (mprogressbar.getstatus()== buttoncircleprogressbar.status.starting){
mprogressbar.setstatus(buttoncircleprogressbar.status.end);
mhandler.removemessages(msg_progress_update);
}else{
mhandler.sendemptymessage(msg_progress_update);
mprogressbar.setstatus(buttoncircleprogressbar.status.starting);
}
}
});
}
}

好了,到这里一个圆形进度条式按钮就实现了.希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网