Android中PathMeasure仿支付宝支付动画
程序员文章站
2023-12-04 18:38:52
前言
在 android 自定义 view 中,path 可能用的比较多,pathmeasure 可能用的比较少,就我而言,以前也没有使用过 pathmeasure 这个...
前言
在 android 自定义 view 中,path 可能用的比较多,pathmeasure 可能用的比较少,就我而言,以前也没有使用过 pathmeasure 这个 api,看到别人用 pathmeasure 和 valueanimator 结合在一起完成了很好的动画效果,于是我也学习下 pathmeasure ,此处记录下。
pathmeasure
构造器:
forceclosed 含义:
// 创建一个 path 对象 path = new path(); path.moveto(20, 20); path.lineto(200, 20); path.lineto(200, 400);
在ondraw(canvas canvas) 中绘制 path
@override protected void ondraw(canvas canvas) { destpath.reset(); destpath.lineto(0, 0); pathmeasure.setpath(path, true); log.e("debug", "pathmeasure.getlength() = " + pathmeasure.getlength()); pathmeasure.getsegment(0, pathmeasure.getlength() * curvalue, destpath, true); canvas.drawpath(destpath, paint); // 绘制线段路径 }
当 pathmeasure.setpath(path,false) 时:
当 pathmeasure.setpath(path,true) 时:
可以看到:当 forceclosed = true 时, path 进行了闭合,相应的 path 长度也变长了,即 算上了斜边的长度。
仿支付宝支付动画 view - loadingview
效果:
思路:
绘制对号,叉号,主要是通过 valueanimator 结合 getsegment() 不断绘制新的弧形段,其中,叉号由两个 path 组成,在第一个 path 绘制完成时,需要调用 pathmeasure.nextcontour() 跳转到另一个 path。
getsegment() 将获取的片段填充到 destpath 中,在 android 4.4 及以下版本中,不能绘制,需要调用 destpath.reset(),destpath.line(0,0)
loadingview 完整代码:
public class loadingview extends view { private final int default_color = color.black; // 默认圆弧颜色 private final int default_stroke_width = dp2px(2); // 默认圆弧宽度 private final boolean default_is_show_result = false; // 默认不显示加载结果 private final int default_view_width = dp2px(50); // 控件默认宽度 private final int default_view_height = dp2px(50); // 控件默认高度 private int color; // 圆弧颜色 private int strokewidth; // 圆弧宽度 private boolean isshowresult; // 是否显示加载结果状态 private paint paint; // 画笔 private int mwidth; // 控件宽度 private int mheight; // 控件高度 private int radius; // 圆弧所在圆的半径 private int halfstrokewidth; // 画笔宽度的一半 private int rotatedelta = 4; private int curangle = 0; private int minangle = -90; private int startangle = -90; // 上方顶点 private int endangle = 0; private rectf rectf; private stateenum stateenum = stateenum.loading; private path successpath; private path rightfailpath; private path leftfailpath; private valueanimator successanimator; private valueanimator rightfailanimator; private valueanimator leftfailanimator; private pathmeasure pathmeasure; private float successvalue; private float rightfailvalue; private float leftfailvalue; private path destpath; private animatorset animatorset; public loadingview(context context) { this(context, null); } public loadingview(context context, attributeset attrs) { super(context, attrs); init(context, attrs); } private void init(context context, attributeset attrs) { typedarray typedarray = null; try { typedarray = context.obtainstyledattributes(attrs, r.styleable.loadingview); color = typedarray.getcolor(r.styleable.loadingview_color, default_color); strokewidth = (int) typedarray.getdimension(r.styleable.loadingview_storkewidth, default_stroke_width); isshowresult = typedarray.getboolean(r.styleable.loadingview_isshowresult, default_is_show_result); } catch (exception e) { e.printstacktrace(); } finally { if (typedarray != null) { typedarray.recycle(); } } paint = createpaint(color, strokewidth, paint.style.stroke); } @override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); mwidth = w; mheight = h; log.i("debug", "getmeasurewidth() = " + getmeasuredwidth()); log.i("debug", "getmeasureheight() = " + getmeasuredheight()); radius = math.min(mwidth, mheight) / 2; halfstrokewidth = strokewidth / 2; rectf = new rectf(halfstrokewidth - radius, halfstrokewidth - radius, radius - halfstrokewidth, radius - halfstrokewidth); // success path successpath = new path(); successpath.moveto(-radius * 2 / 3f, 0f); successpath.lineto(-radius / 8f, radius / 2f); successpath.lineto(radius / 2, -radius / 3); // fail path ,right top to left bottom rightfailpath = new path(); rightfailpath.moveto(radius / 3f, -radius / 3f); rightfailpath.lineto(-radius / 3f, radius / 3f); // fail path, left top to right bottom leftfailpath = new path(); leftfailpath.moveto(-radius / 3f, -radius / 3f); leftfailpath.lineto(radius / 3f, radius / 3f); pathmeasure = new pathmeasure(); destpath = new path(); initsuccessanimator(); initfailanimator(); } private void initsuccessanimator() { // pathmeasure.setpath(successpath, false); successanimator = valueanimator.offloat(0, 1f); successanimator.setduration(1000); successanimator.setinterpolator(new linearinterpolator()); successanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { successvalue = (float) animation.getanimatedvalue(); invalidate(); } }); } private void initfailanimator() { // pathmeasure.setpath(rightfailpath, false); rightfailanimator = valueanimator.offloat(0, 1f); rightfailanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { rightfailvalue = (float) animation.getanimatedvalue(); invalidate(); } }); // pathmeasure.setpath(leftfailpath, false); leftfailanimator = valueanimator.offloat(0, 1f); leftfailanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { leftfailvalue = (float) animation.getanimatedvalue(); invalidate(); } }); animatorset = new animatorset(); animatorset.play(leftfailanimator).after(rightfailanimator); animatorset.setduration(500); animatorset.setinterpolator(new linearinterpolator()); } /** * 测量控件的宽高,当测量模式不是精确模式时,设置默认宽高 * * @param widthmeasurespec * @param heightmeasurespec */ @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { int widthmode = measurespec.getmode(widthmeasurespec); int heightmode = measurespec.getmode(heightmeasurespec); if (widthmode == measurespec.at_most || widthmode == measurespec.unspecified) { widthmeasurespec = measurespec.makemeasurespec(default_view_width, measurespec.exactly); } if (heightmode == measurespec.at_most || heightmode == measurespec.unspecified) { heightmeasurespec = measurespec.makemeasurespec(default_view_height, measurespec.exactly); } super.onmeasure(widthmeasurespec, heightmeasurespec); } @override protected void ondraw(canvas canvas) { canvas.save(); canvas.translate(mwidth / 2, mheight / 2); destpath.reset(); destpath.lineto(0, 0); // destpath if (stateenum == stateenum.loading) { if (endangle >= 300 || startangle > minangle) { startangle += 6; if (endangle > 20) { endangle -= 6; } } if (startangle > minangle + 300) { minangle = startangle; endangle = 20; } canvas.rotate(curangle += rotatedelta, 0, 0);//旋转rotatedelta=4的弧长 canvas.drawarc(rectf, startangle, endangle, false, paint); // endangle += 6 放在 drawarc()后面,是防止刚进入时,突兀的显示了一段圆弧 if (startangle == minangle) { endangle += 6; } invalidate(); } if (isshowresult) { if (stateenum == stateenum.load_success) { pathmeasure.setpath(successpath, false); canvas.drawcircle(0, 0, radius - halfstrokewidth, paint); pathmeasure.getsegment(0, successvalue * pathmeasure.getlength(), destpath, true); canvas.drawpath(destpath, paint); } else if (stateenum == stateenum.load_failed) { canvas.drawcircle(0, 0, radius - halfstrokewidth, paint); pathmeasure.setpath(rightfailpath, false); pathmeasure.getsegment(0, rightfailvalue * pathmeasure.getlength(), destpath, true); if (rightfailvalue == 1) { pathmeasure.setpath(leftfailpath, false); pathmeasure.nextcontour(); pathmeasure.getsegment(0, leftfailvalue * pathmeasure.getlength(), destpath, true); } canvas.drawpath(destpath, paint); } } canvas.restore(); } public void updatestate(stateenum stateenum) { this.stateenum = stateenum; if (stateenum == stateenum.load_success) { successanimator.start(); } else if (stateenum == stateenum.load_failed) { animatorset.start(); } } public enum stateenum { loading, // 正在加载 load_success, // 加载成功,显示对号 load_failed // 加载失败,显示叉号 } /** * 创建画笔 * * @param color 画笔颜色 * @param strokewidth 画笔宽度 * @param style 画笔样式 * @return */ private paint createpaint(int color, int strokewidth, paint.style style) { paint paint = new paint(paint.anti_alias_flag); paint.setstrokecap(paint.cap.round); paint.setstrokejoin(paint.join.round); paint.setcolor(color); paint.setstrokewidth(strokewidth); paint.setstyle(style); return paint; } /** * dp 转换成 px * * @param dpvalue * @return */ private int dp2px(int dpvalue) { return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dpvalue, getresources().getdisplaymetrics()); } }
github : https://github.com/xing16/loadingview
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。