Android自定义WaveView实现波浪进度效果
程序员文章站
2024-02-24 18:42:40
实现原理
首先就是自定义个waveview 继承view,然后再waveview 内部实现代码逻辑:
①...
实现原理
首先就是自定义个waveview 继承view,然后再waveview 内部实现代码逻辑:
① 水波就波嘛? sin函数? 贝塞尔曲线? 都行,这里就用二阶贝塞 尔曲线去画吧
② 波要动嘛,怎么动呢?线程? 好吧 这里用了个handler。
③绘制波首先要找点,那么在onmeasure()
里找出需要的点咯,这里就暂时展示一个波段吧,一个波长移动左边不就没了?ok 那就两个波吧,吼吼,两个波(猥琐男潜质表露无遗啊)。接下来就是handler 结合 ondraw()
绘制。ok,那就先看我word绘制的粗瘪的波动图,请看vcr,oh,no... gif
意思就是波平移一个波长之后回到初始位置继续平移循环。
好吧,有人说了,这么简单的逻辑你要啰嗦那么多???
好吧,我承认,我有唐僧的潜质。。。
闲话就不说了,先上
效果图
示例代码如下
调用的activity
* created by liudong on 2016/12/22. * email:15002102128@126.com */ public class waveactivity extends activity { ld_waveview waveview;//方形 ld_waveview wavecircleview;//圆形 private int progrees=0;//进度 private handler mhandler=new handler(){ @override public void handlemessage(message msg) { if (progrees==100) progrees=0; log.i("progress",progrees+""); waveview.setmprogress(progrees++); wavecircleview.setmprogress(progrees++); mhandler.sendemptymessagedelayed(0,100); } }; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_wave); waveview= (ld_waveview) findviewbyid(r.id.waveview); wavecircleview= (ld_waveview) findviewbyid(r.id.waveviewcircle); mhandler.sendemptymessagedelayed(0,10); } }
xml布局
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:background="@color/ld_white" android:layout_height="match_parent"> <com.dadong.ld_tools.widget.ld_waveview android:id="@+id/waveviewcircle" android:layout_margintop="20dp" android:layout_width="100dp" android:layout_centerhorizontal="true" android:layout_height="100dp" app:wave_color="@color/ld_black" app:wave_circle="true" /> <com.dadong.ld_tools.widget.ld_waveview android:id="@+id/waveview" android:layout_width="100dp" android:layout_height="100dp" app:wave_color="@color/ld_black" app:wave_circle="false" android:layout_centerinparent="true" /> </relativelayout>
自定义waveview
/** * created by liudong on 2016/12/23. * email:15002102128@126.com */ public class ld_waveview extends view { private int mprogress;//进度 private int mtimestep = 10;//时间间隔 private int mspeed = 5;//波单次移动的距离 private int mviewheight;//视图宽高 private int mviewwidth;//视图宽度 private int mlevelline;// 基准线 private int mwavelength;//波长 暂定view宽度为一个波长 private int mstrokewidth;//园的线宽 private rectf rectf;//圆环区域 private int mwaveheight;//波峰高度 private int mleftwavemovelength;//波平移的距离,用来控制波的起点位置 private int mwavecolor;//波的颜色 private paint mpaint;//画笔 private paint mcirclepaint;//圆环画笔 private paint mborderpaint;//边界画笔 private int mborderwidth=4;//边界宽度 private paint mtextpaint;//文字画笔 private path mpath;//绘画线 private list<point> mpoints;//点的集合 private boolean ismeasure = false;//是否已测量过 private boolean iscircle=false;//是否圆形默认false,可属性代码设置 //处理消息 private handler handler = new handler() { @override public void handlemessage(message msg) { initwavemove(); } }; /** * 初始化波的移动 */ private void initwavemove(){ mleftwavemovelength+=mspeed;//波向右移动距离增加mspeed; if (mleftwavemovelength>=mwavelength){//当增加到一个波长时回复到0 mleftwavemovelength=0; } invalidate(); } public ld_waveview(context context) { this(context, null); } public ld_waveview(context context, attributeset attrs) { this(context, attrs, 0); } public ld_waveview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); getattr(context, attrs, defstyleattr); init(); } /** * 初始化画笔 */ private void init() { mpoints = new arraylist<point>(); //波浪轨迹画笔 mpaint = new paint(); mpaint.setantialias(true); mpaint.setcolor(mwavecolor); mpaint.setstyle(paint.style.fill_and_stroke); mpath = new path(); //文字画笔 mtextpaint=new paint(); mtextpaint.setcolor(color.red); mtextpaint.settextalign(paint.align.center); mtextpaint.settextsize(48); //圆环画笔 mcirclepaint=new paint(); mcirclepaint.setantialias(true); mcirclepaint.setcolor(color.white); mcirclepaint.setstyle(paint.style.stroke); //边界线画笔 mborderpaint=new paint(); mborderpaint.setantialias(true); mborderpaint.setcolor(mwavecolor); mborderpaint.setstrokewidth(mborderwidth); mborderpaint.setstyle(paint.style.stroke); } /** * 获取自定义的属性值 * * @param attrs */ private void getattr(context context, attributeset attrs, int defstyle) { typedarray a = context.obtainstyledattributes(attrs, r.styleable.ld_waveview, defstyle, 0); mwavecolor = a.getcolor(r.styleable.ld_waveview_wave_color, color.red); iscircle=a.getboolean(r.styleable.ld_waveview_wave_circle,false); a.recycle(); } /** * * @param widthmeasurespec * @param heightmeasurespec */ @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); if (!ismeasure&&math.abs(getmeasuredheight()-getmeasuredwidth())<50) {//只计算一次就够了 ,relativelayout的时候要绘制两次 加个宽高判断 mviewheight = getmeasuredheight(); mviewwidth = getmeasuredwidth(); mlevelline = mviewheight; //初始化波的准位线 起始位视图最底部 { mlevelline = mviewheight * (100-mprogress) / 100; if (mlevelline < 0) mlevelline = 0; } //计算波峰值 mwaveheight = mviewheight / 20;//波峰暂定为view高度的1/20,如果需要设置 可设置set方法赋值; mwavelength = getmeasuredwidth(); //计算所有的点 这里取宽度为整个波长 往左再延伸一个波长 两个波长则需要9个点 for (int i = 0; i < 9; i++) { int y = 0; switch (i % 4) { case 0: y = mviewheight; break; case 1: y =mviewheight+ mwaveheight; break; case 2: y = mviewheight; break; case 3: y = mviewheight-mwaveheight; break; } point point = new point(-mwavelength + i * mwavelength / 4, y); mpoints.add(point); } /** * 计算圆环宽度 */ int mincircleradius=mviewheight<mviewwidth?mviewheight/2:mviewwidth/2;//内切圆半径 int mcircumcircleradius= (int) (math.sqrt((float)(math.pow(mviewheight/2,2)+math.pow(mviewwidth/2,2)))+0.5);//外接圆半径 int radius=mcircumcircleradius/2+mincircleradius/2; rectf=new rectf(mviewwidth/2-radius,mviewheight/2-radius,mviewwidth/2+radius,mviewheight/2+radius); mstrokewidth=mcircumcircleradius-mincircleradius; mcirclepaint.setstrokewidth(mstrokewidth);//线是有宽度的 采用了这种方式画圆环 ismeasure = true; } } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); /** * 绘制线条 */ mpath.reset(); int i = 0; mpath.moveto(mpoints.get(0).getx()+mleftwavemovelength, mpoints.get(0).gety()-mviewheight*mprogress/100); for (; i < mpoints.size() - 2; i += 2) { mpath.quadto(mpoints.get(i + 1).getx()+mleftwavemovelength, mpoints.get(i + 1).gety()-mviewheight*mprogress/100, mpoints.get(i + 2).getx()+mleftwavemovelength, mpoints.get(i + 2).gety()-mviewheight*mprogress/100); } mpath.lineto(mpoints.get(i).getx()+mleftwavemovelength, mviewheight); mpath.lineto(mpoints.get(0).getx()+mleftwavemovelength, mviewheight); mpath.close(); /** * 绘制轨迹 */ canvas.drawpath(mpath,mpaint); rect rect = new rect(); string progress=string.format("%d%%",mprogress); mtextpaint.gettextbounds(progress,0,progress.length(), rect); int textheight = rect.height(); if (mprogress>=50)//如果进度达到50 颜色变为白色,没办法啊,进度在中间 不变颜色看不到 mtextpaint.setcolor(color.white); else mtextpaint.setcolor(mwavecolor); canvas.drawtext(progress,0,progress.length(),mviewwidth/2,mviewheight/2+textheight/2,mtextpaint); if (iscircle) { /** * 绘制圆环 */ canvas.drawarc(rectf, 0, 360, true, mcirclepaint); paint circlepaint = new paint(); circlepaint.setstrokewidth(5); circlepaint.setcolor(color.white); circlepaint.setantialias(true); circlepaint.setstyle(paint.style.stroke); canvas.drawcircle(mviewwidth / 2, mviewheight / 2, mviewheight / 2, circlepaint); /** * 绘制边界 */ mborderpaint.setstrokewidth(mborderwidth/2); canvas.drawcircle(mviewwidth/2,mviewheight/2,mviewheight/2-mborderwidth/2,mborderpaint); }else { /** * 绘制矩形边框 */ canvas.drawrect(0,0,mviewwidth,mviewheight,mborderpaint); } // handler.sendemptymessagedelayed(0,mtimestep); } /** * 设置进度 基准线 * @param mprogress */ public void setmprogress(int mprogress) { this.mprogress = mprogress; mlevelline=(100-mprogress)*mviewheight/100; } /** * 设置是否为圆形 * @param circle */ public void setcircle(boolean circle) { iscircle = circle; } }
自定义属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ld_waveview"> <attr name="wave_color" format="color"></attr> <attr name="wave_circle" format="boolean"></attr> </declare-styleable> </resources>
总结
好了,以上就是这篇文章的全部内容了,代码里备注应该还算比较清楚了,希望能对一些人有一些帮助,瑕疵不足之处欢迎指正,或者有好的建议。也可以留言交流。