Objective
Water ripple effects, presumably we have seen more or less, in my impression, there are roughly the following:
Alipay "Xiu Xiu Xiu" type
Flow Ball "rippling" type
Real water ripple effect, based on bitmap processing
Say not much, first to see the effect:
Filled-type water ripple, equal spacing
Non-filling water ripple, equal spacing
Non-filling water ripple, the spacing is constantly getting larger
Filled-type water ripple, the spacing keeps getting smaller
Presumably we already know the basic principle, is to use canvas to draw, but it is not a simple painting oh, please look down.
Analysis
This type of wave pattern, in fact, is only to draw a circle, in a given rectangle, a circle from the smallest radius to the maximum radius, accompanied by the transparency from 1.0 to 0.0. We assume that this diffusion is uniform, then a circle from the creation (transparency of 1.0) to the disappearance (transparency of 0.0) of the time is fixed value, then a certain moment the radius of a circle and transparency can be determined by the diffusion time (current time-creation time).
Realize
According to the above analysis, we write the following Circle
class to represent a circle:
Private class Circle {
private long mcreatetime;
Public Circle () {
this.mcreatetime = System.currenttimemillis ();
}
public int Getalpha () {
float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
return (int) (1.0f-percent) * 255);
public float Getcurrentradius () {
float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
return Minitialradius + percent * (Mmaxradius-minitialradius);
}
Naturally, in WaveView
, there should be a list of 来
the circles that are currently being displayed:
Private list<circle> mcirclelist = new arraylist<circle> ();
We define a start
method to start the diffusion:
public void Start () {
if (!misrunning) {
misrunning = true;
Mcreatecircle.run ();
}
Private Runnable mcreatecircle = new Runnable () {
@Override public
void Run () {
if (misrunning) {
Newcir CLE ();
Postdelayed (Mcreatecircle, mspeed); Create a round}} every mspeed
milliseconds
;
private void Newcircle () {
Long currenttime = System.currenttimemillis ();
if (Currenttime-mlastcreatetime < mspeed) {return
;
}
Circle Circle = new Circle ();
Mcirclelist.add (circle);
Invalidate ();
Mlastcreatetime = currenttime;
}
start
The method simply creates a circle and adds it to the mCircleList
, opening the loop to create the Circle Runnable
, and then notifies the interface to refresh, and then we look at the onDraw
method:
protected void OnDraw (Canvas Canvas) {
iterator<circle> iterator = Mcirclelist.iterator ();
while (Iterator.hasnext ()) {
Circle Circle = Iterator.next ();
if (System.currenttimemillis ()-circle.mcreatetime < mduration) {
Mpaint.setalpha (Circle.getalpha ());
Canvas.drawcircle (GetWidth ()/2, GetHeight ()/2, Circle.getcurrentradius (), mpaint);
else {
iterator.remove ();
}
}
if (mcirclelist.size () > 0) {
postinvalidatedelayed;
}
}
onDraw
The method traverses each one Circle
to determine Circle
whether the diffusion time exceeds the set diffusion time, if it is removed, and if not, calculates the Circle
current transparency and radius and draws it. We have added a delay refresh to continually redraw the interface to achieve continuous ripple spread effect.
Now run the program, should be able to see the effect of Figure 2, but a bit awkward, according to common sense, the spacing of water is more and more big, how to do it?
Skills
To make the water ripple radius is not constant, we can only modify Circle.getCurrentRadius()
the method. Let's look at the method again:
public float Getcurrentradius () {
float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
Return Minitialradius + percent * (Mmaxradius-minitialradius);
}
percent
Represents Circle
a percentage of the current diffusion time and total diffusion time, considering that the current diffusion time will be removed when it exceeds the total diffusion time Circle
, percent
the actual interval is [0, 1], see [0, 1], I don't know what you think, the first thing I think of is the difference device ( Interpolator
, we can control the radius change by defining the difference device Circle
!
We modify the code:
Private Interpolator Minterpolator = new Linearinterpolator ();
public void Setinterpolator (Interpolator interpolator) {
minterpolator = interpolator;
if (Minterpolator = = null) {
minterpolator = new Linearinterpolator ();
}
}
Private class Circle {
private long mcreatetime;
Public Circle () {
this.mcreatetime = System.currenttimemillis ();
}
public int Getalpha () {
float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
return (int) (1.0f-minterpolator.getinterpolation (percent)) * 255);
public float Getcurrentradius () {
float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
return Minitialradius + minterpolator.getinterpolation (percent) * (Mmaxradius-minitialradius);
}
In this way, when used externally, a different WaveView
effect can be achieved by simply calling setInterpolator()
to define a different interpolation.
Figure 3 The effect of the code:
Mwaveview = (Waveview) Findviewbyid (R.id.wave_view);
Mwaveview.setduration (5000);
Mwaveview.setstyle (Paint.Style.STROKE);
Mwaveview.setspeed ();
Mwaveview.setcolor (Color.parsecolor ("#ff0000"));
Mwaveview.setinterpolator (New Accelerateinterpolator (1.2f));
Mwaveview.start ();
Figure 4 The effect of the code:
Mwaveview = (Waveview) Findviewbyid (R.id.wave_view);
Mwaveview.setduration (5000);
Mwaveview.setstyle (Paint.Style.FILL);
Mwaveview.setcolor (Color.parsecolor ("#ff0000"));
Mwaveview.setinterpolator (New Linearoutslowininterpolator ());
Mwaveview.start ();
Attach all code for Waveview:
/** * Water Ripple Effect * Created by Hackware on 2016/6/17. * * public class Waveview extends View {private float Minitialradius;/////Initial ripple radius private float mmaxradiusrate = 0.85f;//
If Mmaxradius is not set, Mmaxradius = Minimum length * mmaxradiusrate; private float Mmaxradius; Maximum ripple radius private long mduration = 2000; A ripple from creation to vanishing duration private int mspeed = 500;
Ripple creation speed, each 500ms creates a private interpolator Minterpolator = new Linearinterpolator ();
Private list<circle> mcirclelist = new arraylist<circle> ();
Private Boolean misrunning;
Private Boolean Mmaxradiusset;
Private Paint Mpaint;
Private long mlastcreatetime;
Private Runnable mcreatecircle = new Runnable () {@Override public void run () {if (misrunning) {newcircle ();
Postdelayed (Mcreatecircle, mspeed);
}
}
};
Public Waveview {This (context, NULL);
Public Waveview (context, AttributeSet attrs) {Super (context, attrs);
Mpaint = new Paint (Paint.anti_alias_flag); SetStyle (Paint.Style.FILL);
public void SetStyle (Paint.style style) {Mpaint.setstyle (style); @Override protected void onsizechanged (int w, int h, int oldw, int oldh) {if (!mmaxradiusset) {Mmaxradius = Math.
Min (w, h) * mmaxradiusrate/2.0f;
} public void Setmaxradiusrate (float maxradiusrate) {this.mmaxradiusrate = maxradiusrate;
public void SetColor (int color) {mpaint.setcolor (color);
/** * Start/public void start () {if (!misrunning) {misrunning = true;
Mcreatecircle.run ();
}/** * Stop/public void Stop () {misrunning = false;
} protected void OnDraw (Canvas Canvas) {iterator<circle> iterator = Mcirclelist.iterator ();
while (Iterator.hasnext ()) {Circle Circle = Iterator.next ();
if (System.currenttimemillis ()-circle.mcreatetime < Mduration) {Mpaint.setalpha (Circle.getalpha ());
Canvas.drawcircle (GetWidth ()/2, GetHeight ()/2, Circle.getcurrentradius (), mpaint);
else {iterator.remove (); } if (Mcirclelist.size () > 0) {PostInvalidatedelayed (10);
} public void Setinitialradius (float radius) {Minitialradius = radius;
} public void Setduration (long duration) {this.mduration = duration;
public void Setmaxradius (float maxradius) {This.mmaxradius = Maxradius;
Mmaxradiusset = true;
The public void setspeed (int speed) {mspeed = speed;
private void Newcircle () {Long currenttime = System.currenttimemillis ();
if (Currenttime-mlastcreatetime < mspeed) {return;
} Circle Circle = new Circle ();
Mcirclelist.add (circle);
Invalidate ();
Mlastcreatetime = currenttime;
Private class Circle {private Long mcreatetime;
Public Circle () {this.mcreatetime = System.currenttimemillis ();
public int Getalpha () {Float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduration;
return (int) (1.0f-minterpolator.getinterpolation (percent)) * 255); public float Getcurrentradius () {Float percent = (system.currenttimemillis ()-mcreatetime) * 1.0f/mduratioN
return Minitialradius + minterpolator.getinterpolation (percent) * (Mmaxradius-minitialradius);
} public void Setinterpolator (Interpolator interpolator) {minterpolator = Interpolator;
if (Minterpolator = = null) {Minterpolator = new linearinterpolator (); }
}
}
Summarize
I guess you'll see this article will think that the original interpolation can be used. In fact, sometimes we use the API provided by the system, often too limited to it, and occasionally a change of ideas, might get wonderful results. This is the full content of the water ripple effect on Android, and I hope it will help you to develop Android.
。