Android实现水波纹控件的方法
程序员文章站
2022-05-28 10:15:05
有很多app使用过水波纹的这样的效果,看着很酷酷的样子,所以自己就撸码写了一个。
实现思路:
利用贝塞尔曲线绘制圆弧(也就是水波的波纹)
通过动画改变绘制的起始点...
有很多app使用过水波纹的这样的效果,看着很酷酷的样子,所以自己就撸码写了一个。
实现思路:
利用贝塞尔曲线绘制圆弧(也就是水波的波纹)
通过动画改变绘制的起始点使水波纹平移
首先,定义我们需要的自定义属性。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="wavestyleable"> <!-- 水波纹的长度--> <attr name="wavelength" format="float"></attr> <!-- 水波纹的高度--> <attr name="waveheight" format="float"></attr> <!-- 水波纹的速度--> <attr name="wavespeed" format="float"></attr> <!--水波纹上方的头像 --> <attr name="wavetopicon" format="reference"></attr> <!--水波的颜色 --> <attr name="wavecolor" format="color"></attr> <!--水波距离底部的距离 --> <attr name="distancey" format="float"></attr> </declare-styleable> </resources>
自定义view绘制水波纹控件
public class waveview extends view { private paint paint; private path path; private float wavelength ; private float waveheight ; private float wavespeed ; private bitmap bitmap; private int wavecolor ; private int strokewidth = 3; private region region; private int width,height; public int translatex ; private float distancey; public waveview(context context) { super(context); } public waveview(context context, attributeset attrs) { super(context, attrs); typedarray array = context.obtainstyledattributes(attrs, r.styleable.wavestyleable); wavelength = array.getfloat(r.styleable.wavestyleable_wavelength,300); wavecolor = array.getcolor(r.styleable.wavestyleable_wavecolor,0x00ff00); waveheight = array.getfloat(r.styleable.wavestyleable_waveheight,100); wavespeed = array.getfloat(r.styleable.wavestyleable_wavespeed,5); distancey = array.getfloat(r.styleable.wavestyleable_distancey,100); drawable wavetopicon = array.getdrawable(r.styleable.wavestyleable_wavetopicon); array.recycle(); bitmap = drawabletobitmap(wavetopicon); initpaint(); startanimal(); } private void initpaint() { paint = new paint(); paint.setstyle(paint.style.fill); paint.setcolor(wavecolor); paint.setstrokewidth(strokewidth); //绘制贝塞尔曲线的path path = new path(); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); //绘制贝塞尔曲线 drawpath(canvas,path); //绘制wave上部的头像 drawicon(canvas); } private void drawicon(canvas canvas) { float baseline = height-distancey; if(region.getbounds().top==baseline){ canvas.drawbitmap(bitmap,width/2-bitmap.getwidth()/2,region.getbounds().bottom-bitmap.getheight(),paint); }else { if(region.getbounds().top==0){ canvas.drawbitmap(bitmap,width/2-bitmap.getwidth()/2,height-bitmap.getheight()-distancey,paint); } canvas.drawbitmap(bitmap,width/2-bitmap.getwidth()/2,region.getbounds().top-bitmap.getheight(),paint); } } private void drawpath(canvas canvas, path path) { path.reset(); //path的起始点,向手机外多绘制一段 path.moveto(-2* wavelength +translatex,getheight()-distancey); for(int i = 0; i<getwidth()+ wavelength; i+= wavelength){ path.rquadto(wavelength /2,-waveheight, wavelength,0); path.rquadto(wavelength /2,waveheight, wavelength,0); } region = new region(); region clip = new region(); clip.set((int) (getwidth()/2-0.1),0,getwidth()/2,getheight()*2); region.setpath(path,clip); path.lineto(getwidth(),getheight()); path.lineto(-wavelength,getheight()); path.close(); canvas.drawpath(path,paint); } public void startanimal(){ valueanimator animator = valueanimator.offloat(0,1); animator.setduration(3000); animator.setrepeatcount(valueanimator.infinite); animator.setinterpolator(new linearinterpolator()); animator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { translatex += wavespeed; if(-2* wavelength +translatex >= 0){ translatex = 0; } postinvalidate(); } }); animator.start(); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); //获取宽高模式 int widthmode = measurespec.getmode(widthmeasurespec); int heightmode = measurespec.getmode(heightmeasurespec); width = measurespec.getsize(widthmeasurespec); height = measurespec.getsize(heightmeasurespec); if (widthmode == measurespec.at_most){ width = (int) wavelength; } if(heightmode == measurespec.at_most){ height = (int) (waveheight+ distancey+bitmap.getheight()); } setmeasureddimension(width,height); } /** * dp转化为px * @param dpvalue * @param context * @return */ public float dp2px(float dpvalue,context context){ return typedvalue.applydimension(typedvalue.complex_unit_dip,dpvalue,context.getresources().getdisplaymetrics()); } /** * 如果图片底部有很多空白会导致图片不能贴到波纹底部 * @param bitmap * @return */ public bitmap makeroundcorner(bitmap bitmap) { int width = bitmap.getwidth(); int height = bitmap.getheight(); int left = 0, top = 0, right = width, bottom = height; float roundpx = height/2; if (width > height) { left = (width - height)/2; top = 0; right = left + height; bottom = height; } else if (height > width) { left = 0; top = (height - width)/2; right = width; bottom = top + width; roundpx = width/2; } bitmap output = bitmap.createbitmap(width, height, bitmap.config.argb_8888); canvas canvas = new canvas(output); int color = 0xff424242; paint paint = new paint(); rect rect = new rect(left, top, right, bottom); rectf rectf = new rectf(rect); paint.setantialias(true); canvas.drawargb(0, 0, 0, 0); paint.setcolor(color); canvas.drawroundrect(rectf, roundpx, roundpx, paint); paint.setxfermode(new porterduffxfermode(porterduff.mode.src_in)); canvas.drawbitmap(bitmap, rect, rect, paint); return output; } public bitmap drawabletobitmap(drawable drawable) { bitmap bitmap = bitmap.createbitmap( drawable.getintrinsicwidth(), drawable.getintrinsicheight(), drawable.getopacity() != pixelformat.opaque ? bitmap.config.argb_8888 : bitmap.config.rgb_565); canvas canvas = new canvas(bitmap); drawable.setbounds(0, 0, drawable.getintrinsicwidth(), drawable.getintrinsicheight()); drawable.draw(canvas); return makeroundcorner(bitmap); } }
相关类:
path: 可以绘制二次曲线或者三次曲线到画布上,moveto()方法将path移动到手机屏幕的(-2* wavelength,distancey)这个点,然后以这个点为起始点绘制二次曲线曲线,rquadto(),以最后点为相对位置点进行取点绘制。在属性动画里面,不断改变起始点的位置,这样绘制的水波纹就会平移。
region:表示区域的类,通过set(path,rect)可以获取到矩形区域与path弧线相交的新的矩形。如果rect的宽度无限小,那么获取的矩形区域会近似为一个点,这个点就是图片移动的y坐标。
xml文件使用:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.iwintrue.waveapplication.mainactivity"> <com.iwintrue.waveapplication.waveview xmlns:app="http://schemas.android.com/apk/res-auto" app:wavelength = "200" app:waveheight = "50" app:wavespeed = "10" app:wavecolor = "#0ff" app:distancey = "100" app:wavetopicon = "@mipmap/icon" android:layout_centerinparent="true" android:id="@+id/waterview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#f00" /> </relativelayout>
核心代码就是这么多,代码中也有解释,关键的类也做了注解了。要是还有那里有疑问,多多交流哈
github地址:https://github.com/zhoukai1526/waveapplication
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。