当前位置: 移动技术网 > 移动技术>移动开发>Android > Android仿水波纹流量球进度条控制器

Android仿水波纹流量球进度条控制器

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

仿水波纹流球进度条控制器,android实现高端大气的主流特效,供大家参考,具体内容如下

效果图:

circleview

这里主要是实现中心圆以及水波特效

package com.lgl.circleview;

import android.content.context;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.graphics.path;
import android.graphics.rectf;
import android.os.handler;
import android.os.parcel;
import android.os.parcelable;
import android.util.attributeset;
import android.view.view;
import android.widget.progressbar;

/**
 * 水波圆
 * 
 * @author lgl
 * 
 */
public class circleview extends view {

 private context mcontext;

 private int mscreenwidth;
 private int mscreenheight;

 private paint mringpaint;
 private paint mcirclepaint;
 private paint mwavepaint;
 private paint linepaint;
 private paint flowpaint;
 private paint leftpaint;

 private int mringstrokewidth = 15;
 private int mcirclestrokewidth = 2;
 private int mlinestrokewidth = 1;

 private int mcirclecolor = color.white;
 private int mringcolor = color.white;
 private int mwavecolor = color.white;

 private handler mhandler;
 private long c = 0l;
 private boolean mstarted = false;
 private final float f = 0.033f;
 private int malpha = 50;// 透明度
 private float mamplitude = 10.0f; // 振幅
 private float mwaterlevel = 0.5f;// 水高(0~1)
 private path mpath;

 // 绘制文字显示在圆形中间,只是我没有设置,我觉得写在布局上也挺好的
 private string flownum = "";
 private string flowleft = "还剩余";

 /**
  * @param context
  */
 public circleview(context context) {
  super(context);
  // todo auto-generated constructor stub
  mcontext = context;
  init(mcontext);
 }

 /**
  * @param context
  * @param attrs
  */
 public circleview(context context, attributeset attrs) {
  super(context, attrs);
  // todo auto-generated constructor stub
  mcontext = context;
  init(mcontext);
 }

 /**
  * @param context
  * @param attrs
  * @param defstyleattr
  */
 public circleview(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  // todo auto-generated constructor stub
  mcontext = context;
  init(mcontext);
 }

 public void setmwaterlevel(float mwaterlevel) {
  this.mwaterlevel = mwaterlevel;
 }

 private void init(context context) {
  mringpaint = new paint();
  mringpaint.setcolor(mringcolor);
  mringpaint.setalpha(50);
  mringpaint.setstyle(paint.style.stroke);
  mringpaint.setantialias(true);
  mringpaint.setstrokewidth(mringstrokewidth);

  mcirclepaint = new paint();
  mcirclepaint.setcolor(mcirclecolor);
  mcirclepaint.setstyle(paint.style.stroke);
  mcirclepaint.setantialias(true);
  mcirclepaint.setstrokewidth(mcirclestrokewidth);

  linepaint = new paint();
  linepaint.setcolor(mcirclecolor);
  linepaint.setstyle(paint.style.stroke);
  linepaint.setantialias(true);
  linepaint.setstrokewidth(mlinestrokewidth);

  flowpaint = new paint();
  flowpaint.setcolor(mcirclecolor);
  flowpaint.setstyle(paint.style.fill);
  flowpaint.setantialias(true);
  flowpaint.settextsize(36);

  leftpaint = new paint();
  leftpaint.setcolor(mcirclecolor);
  leftpaint.setstyle(paint.style.fill);
  leftpaint.setantialias(true);
  leftpaint.settextsize(36);

  mwavepaint = new paint();
  mwavepaint.setstrokewidth(1.0f);
  mwavepaint.setcolor(mwavecolor);
  mwavepaint.setalpha(malpha);
  mpath = new path();

  mhandler = new handler() {
   @override
   public void handlemessage(android.os.message msg) {
    if (msg.what == 0) {
     invalidate();
     if (mstarted) {
      // 不断发消息给自己,使自己不断被重绘
      mhandler.sendemptymessagedelayed(0, 60l);
     }
    }
   }
  };
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  int width = measure(widthmeasurespec, true);
  int height = measure(heightmeasurespec, false);
  if (width < height) {
   setmeasureddimension(width, width);
  } else {
   setmeasureddimension(height, height);
  }

 }

 /**
  * @category 测量
  * @param measurespec
  * @param iswidth
  * @return
  */
 private int measure(int measurespec, boolean iswidth) {
  int result;
  int mode = measurespec.getmode(measurespec);
  int size = measurespec.getsize(measurespec);
  int padding = iswidth ? getpaddingleft() + getpaddingright()
    : getpaddingtop() + getpaddingbottom();
  if (mode == measurespec.exactly) {
   result = size;
  } else {
   result = iswidth ? getsuggestedminimumwidth()
     : getsuggestedminimumheight();
   result += padding;
   if (mode == measurespec.at_most) {
    if (iswidth) {
     result = math.max(result, size);
    } else {
     result = math.min(result, size);
    }
   }
  }
  return result;
 }

 @override
 protected void onsizechanged(int w, int h, int oldw, int oldh) {
  // todo auto-generated method stub
  super.onsizechanged(w, h, oldw, oldh);
  mscreenwidth = w;
  mscreenheight = h;
 }

 @override
 protected void ondraw(canvas canvas) {
  // todo auto-generated method stub
  super.ondraw(canvas);
  // 得到控件的宽高
  int width = getwidth();
  int height = getheight();
  setbackgroundcolor(mcontext.getresources().getcolor(r.color.main_bg));
  // 计算当前油量线和水平中线的距离
  float centeroffset = math.abs(mscreenwidth / 2 * mwaterlevel
    - mscreenwidth / 4);
  // 计算油量线和与水平中线的角度
  float horiangle = (float) (math.asin(centeroffset / (mscreenwidth / 4)) * 180 / math.pi);
  // 扇形的起始角度和扫过角度
  float startangle, sweepangle;
  if (mwaterlevel > 0.5f) {
   startangle = 360f - horiangle;
   sweepangle = 180f + 2 * horiangle;
  } else {
   startangle = horiangle;
   sweepangle = 180f - 2 * horiangle;
  }

  canvas.drawline(mscreenwidth * 3 / 8, mscreenheight * 5 / 8,
    mscreenwidth * 5 / 8, mscreenheight * 5 / 8, linepaint);

  float num = flowpaint.measuretext(flownum);
  canvas.drawtext(flownum, mscreenwidth * 4 / 8 - num / 2,
    mscreenheight * 4 / 8, flowpaint);
  float left = leftpaint.measuretext(flowleft);
  canvas.drawtext(flowleft, mscreenwidth * 4 / 8 - left / 2,
    mscreenheight * 3 / 8, leftpaint);

  // 如果未开始(未调用startwave方法),绘制一个扇形
  if ((!mstarted) || (mscreenwidth == 0) || (mscreenheight == 0)) {
   // 绘制,即水面静止时的高度
   rectf oval = new rectf(mscreenwidth / 4, mscreenheight / 4,
     mscreenwidth * 3 / 4, mscreenheight * 3 / 4);
   canvas.drawarc(oval, startangle, sweepangle, false, mwavepaint);
   return;
  }
  // 绘制,即水面静止时的高度
  // 绘制,即水面静止时的高度
  rectf oval = new rectf(mscreenwidth / 4, mscreenheight / 4,
    mscreenwidth * 3 / 4, mscreenheight * 3 / 4);
  canvas.drawarc(oval, startangle, sweepangle, false, mwavepaint);

  if (this.c >= 8388607l) {
   this.c = 0l;
  }
  // 每次ondraw时c都会自增
  c = (1l + c);
  float f1 = mscreenheight * (1.0f - (0.25f + mwaterlevel / 2))
    - mamplitude;
  // 当前油量线的长度
  float wavewidth = (float) math.sqrt(mscreenwidth * mscreenwidth / 16
    - centeroffset * centeroffset);
  // 与圆半径的偏移量
  float offsetwidth = mscreenwidth / 4 - wavewidth;

  int top = (int) (f1 + mamplitude);
  mpath.reset();
  // 起始振动x坐标,结束振动x坐标
  int startx, endx;
  if (mwaterlevel > 0.50f) {
   startx = (int) (mscreenwidth / 4 + offsetwidth);
   endx = (int) (mscreenwidth / 2 + mscreenwidth / 4 - offsetwidth);
  } else {
   startx = (int) (mscreenwidth / 4 + offsetwidth - mamplitude);
   endx = (int) (mscreenwidth / 2 + mscreenwidth / 4 - offsetwidth + mamplitude);
  }
  // 波浪效果
  while (startx < endx) {
   int starty = (int) (f1 - mamplitude
     * math.sin(math.pi
       * (2.0f * (startx + this.c * width * this.f))
       / width));
   canvas.drawline(startx, starty, startx, top, mwavepaint);
   startx++;
  }
  canvas.drawcircle(mscreenwidth / 2, mscreenheight / 2, mscreenwidth / 4
    + mringstrokewidth / 2, mringpaint);

  canvas.drawcircle(mscreenwidth / 2, mscreenheight / 2,
    mscreenwidth / 4, mcirclepaint);
  canvas.restore();
 }

 @override
 public parcelable onsaveinstancestate() {
  parcelable superstate = super.onsaveinstancestate();
  savedstate ss = new savedstate(superstate);
  ss.progress = (int) c;
  return ss;
 }

 @override
 public void onrestoreinstancestate(parcelable state) {
  savedstate ss = (savedstate) state;
  super.onrestoreinstancestate(ss.getsuperstate());
  c = ss.progress;
 }

 @override
 protected void onattachedtowindow() {
  super.onattachedtowindow();
  // 关闭硬件加速,防止异常unsupported operation exception
  this.setlayertype(view.layer_type_software, null);
 }

 @override
 protected void ondetachedfromwindow() {
  super.ondetachedfromwindow();
 }

 /**
  * @category 开始波动
  */
 public void startwave() {
  if (!mstarted) {
   this.c = 0l;
   mstarted = true;
   this.mhandler.sendemptymessage(0);
  }
 }

 /**
  * @category 停止波动
  */
 public void stopwave() {
  if (mstarted) {
   this.c = 0l;
   mstarted = false;
   this.mhandler.removemessages(0);
  }
 }

 /**
  * @category 保存状态
  */
 static class savedstate extends basesavedstate {
  int progress;

  /**
   * constructor called from {@link progressbar#onsaveinstancestate()}
   */
  savedstate(parcelable superstate) {
   super(superstate);
  }

  /**
   * constructor called from {@link #creator}
   */
  private savedstate(parcel in) {
   super(in);
   progress = in.readint();
  }

  @override
  public void writetoparcel(parcel out, int flags) {
   super.writetoparcel(out, flags);
   out.writeint(progress);
  }

  public static final parcelable.creator<savedstate> creator = new parcelable.creator<savedstate>() {
   public savedstate createfromparcel(parcel in) {
    return new savedstate(in);
   }

   public savedstate[] newarray(int size) {
    return new savedstate[size];
   }
  };
 }

}

我们运行一下

其实他是十分的空旷的,所以也值得我们去定制,我们在中间加个流量显示,再加个进度条
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@color/main_bg" >

 <textview
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_alignparenttop="true"
  android:layout_centerhorizontal="true"
  android:layout_margintop="10dp"
  android:text="流量"
  android:textcolor="@android:color/white"
  android:textsize="18sp" />

 <com.lgl.circleview.circleview
  android:id="@+id/wave_view"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_centerinparent="true" />

 <textview
  android:id="@+id/power"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_centerinparent="true"
  android:textcolor="@android:color/white" />

 <seekbar
  android:id="@+id/seekbar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_alignparentbottom="true"
  android:layout_marginbottom="150dp" />

</relativelayout>

我们要实现这个,就要调用它的初始化以及start方法

 mcircleview = (circleview) findviewbyid(r.id.wave_view);
  // 设置多高,float,0.1-1f
  mcircleview.setmwaterlevel(0.1f);
  // 开始执行
  mcircleview.startwave();

别忘了activity销毁的时候把它回收哦
@override
 protected void ondestroy() {
  // todo auto-generated method stub
  mcircleview.stopwave();
  mcircleview = null;
  super.ondestroy();
 }

我们再运行一遍

但是我们要怎么让水波纹随着进度条一起上升下降尼?,这里我们就要用到我们刚才写的seekbar了,我们实现它的

setonseekbarchangelistener来监听,这样我们就要复写他的三个方法,这里我们只要用到一个
public void onprogresschanged(seekbar seekbar, int progress,
     boolean fromuser) {
    //跟随进度条滚动
    mcircleview.setmwaterlevel((float) progress / 100);
    }

这里,我们要这样算的,我们设置高度的单位是float,也就是从0-1f,而我们的进度是int progress,从0-100,我们就要用(float) progress / 100)并且强转来得到单位,好了,我们现在水波纹的高度就是随着我们的进度条一起变化了,我们再来运行一下

好的,这样的话,我们就只剩下一个了,就是让大小随着我们的进度条变化了,这里我们因为更新ui不能再主线程中操作,所以我们需要用到我们的老伙计handler了,但是用到handler还不够,我们的进度条数值也是在内部类里面,所以这里我们需要用到handler来传值了,这里我们用的是bundle,我们还是在onprogresschanged方法中操作了

 //创建一个消息
    message message = new message();
    bundle bundle = new bundle();
    //put一个int值
    bundle.putint("progress", progress);
    //装载
    message.setdata(bundle);
    //发送消息
    handler.sendmessage(message);
    //创建表示
    message.what = 1;

消息发送过去了,我们就在前面写个handler去接收就是了

 private handler handler = new handler() {
  public void handlemessage(android.os.message msg) {
   if (msg.what == 1) {
    int num = msg.getdata().getint("progress");
    log.i("num", num + "");
    power.settext((float) num / 100 * max + "m/" + max + "m");
   }
  }
 };

这里的计算公式尼,是当前的数值/100得到百分比再去*最大值。我们现在可以完整的运行一下了,其实和最上面运行的图片是一样的

mainactivity

package com.lgl.circleview;

import android.app.activity;
import android.os.bundle;
import android.os.handler;
import android.os.message;
import android.util.log;
import android.widget.seekbar;
import android.widget.textview;

public class mainactivity extends activity {

 private circleview mcircleview;
 private seekbar mseekbar;
 private textview power;
 private int max = 1024;
 private int min = 102;

 private handler handler = new handler() {
 public void handlemessage(android.os.message msg) {
  if (msg.what == 1) {
  int num = msg.getdata().getint("progress");
  log.i("num", num + "");
  power.settext((float) num / 100 * max + "m/" + max + "m");
  }
 }
 };

 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 getactionbar().hide();
 setcontentview(r.layout.activity_main);

 power = (textview) findviewbyid(r.id.power);
 power.settext(min + "m/" + max + "m");

 mcircleview = (circleview) findviewbyid(r.id.wave_view);
 // 设置多高,float,0.1-1f
 mcircleview.setmwaterlevel(0.1f);
 // 开始执行
 mcircleview.startwave();

 mseekbar = (seekbar) findviewbyid(r.id.seekbar);
 mseekbar.setonseekbarchangelistener(new seekbar.onseekbarchangelistener() {
  @override
  public void onprogresschanged(seekbar seekbar, int progress,
   boolean fromuser) {
  mcircleview.setmwaterlevel((float) progress / 100);
  // 创建一个消息
  message message = new message();
  bundle bundle = new bundle();
  // put一个int值
  bundle.putint("progress", progress);
  // 装载
  message.setdata(bundle);
  // 发送消息
  handler.sendmessage(message);
  // 创建表示
  message.what = 1;
  }

  @override
  public void onstarttrackingtouch(seekbar seekbar) {

  }

  @override
  public void onstoptrackingtouch(seekbar seekbar) {

  }
 });
 }

 @override
 protected void ondestroy() {
 // todo auto-generated method stub
 mcircleview.stopwave();
 mcircleview = null;
 super.ondestroy();
 }
}

代码下载:

以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。

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

相关文章:

验证码:
移动技术网