当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现水波纹控件的方法

Android实现水波纹控件的方法

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

有很多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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

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

相关文章:

验证码:
移动技术网