欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现水波纹控件的方法

程序员文章站 2023-10-27 18:07:22
有很多app使用过水波纹的这样的效果,看着很酷酷的样子,所以自己就撸码写了一个。 实现思路: 利用贝塞尔曲线绘制圆弧(也就是水波的波纹) 通过动画改变绘制的起始点...

有很多app使用过水波纹的这样的效果,看着很酷酷的样子,所以自己就撸码写了一个。

Android实现水波纹控件的方法

实现思路:

利用贝塞尔曲线绘制圆弧(也就是水波的波纹)
通过动画改变绘制的起始点使水波纹平移

首先,定义我们需要的自定义属性。

<?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

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