Go Android Custom Control series Five: custom fancy water ripple effect

Source: Internet
Author: User
Tags gety
<span id="Label3"></p><p><p>Source: http://www.2cto.com/kf/201411/353169.html</p></p><p><p>Today we are using Android custom controls to achieve a more interesting effect: sliding water ripples. Let's take a look at the Final:</p></p><p><p></p></p><p><p>Figure A</p></p><p><p></p></p><p><p>The effect is still very dazzling, the meal to a mouthful, the road to step by step, here we will the whole process into a few steps to achieve</p></p><p><p></p></p>one, the realization of click on the appearance of water ripple single ring effect:<p><p></p></p><p><p>Figure II</p></p><p><p></p></p><p><p>As a matter of routine, it's a custom control, and here we're just going to get this control to fill up the entire screen (a previous article that's not familiar to custom controls:<strong>Android custom control series ii: Custom switch button (i)</strong>). Observing this effect, we find that it is necessary to rewrite the Ontouchevent and OnDraw methods by getting the coordinates of the touch in the ontouchevent, and then drawing the graph we need with this coordinate value as the center of the map, which is the OnDraw method of the Call.</p></p><p><p></p></p>1. Create a new project, define a waterwave class, inherit from view, as a custom control; write out the custom control in the manifest file and fill the parent form directly.<p><p></p></p>2. In the Waterwave class, implement its Two-parameter constructor:<pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">Package com.example.waterwavedemo.ui; Import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.os.handler;import Android.os.message;import android.util.AttributeSet; Import Android.view.motionevent;import android.view.View; public class Waterwave extends View { ... / * * 1, two parameter constructors */public waterwave (context context, attributeset attrs) { Super (context, Attrs) ; Alpha = 0; Radius = 0; Initpaint (); } ...}</pre></pre><p><p></p></p><p><p></p></p>3, to use the custom control, then generally need to specify its size, here we only need to fill the form, so the default Onmeasure method can be used:<pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">the/** * Onmeasure method, which determines the size of the control, uses the default * /@Override protected void onmeasure (int widthmeasurespec, int Heightmeasurespec) { //TODO auto-generated Method stub super.onmeasure (widthmeasurespec, Heightmeasurespec) ; }</pre></pre><p><p></p></p>4, Draw this custom graphic, rewrite the OnDraw method, here because we need to draw a circle, so write:<pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">@Override/** * The method of drawing the required graphics, This method is more critical */protected void OnDraw (canvas canvas) { canvas.drawcircle (xdown, ydown, radius, paint); }</pre></pre><p><p></p></p><p><p>The parameters Xdown and Ydown are member variables, which represent the x and Y coordinates when pressed, and the corresponding point of the coordinate is the center of the circle to be drawn; The radius parameter is also a member variable that represents the radius of the ring to be drawn;</p></p><p><p></p></p><p><p>See here also need a paint, is the paint type of the brush object, which is defined as a member variable, because the OnDraw method is called the first time the custom control is displayed, so this paint needs us in the constructor of the two parameters to initialize, otherwise, a null pointer exception is reported, so we'll write another initpaint () method here to initialize our Paint:</p></p><p><p></p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">/** * Initialize paint */private void initpaint () {/ * * Create a new brush * /paint = new paint (); Paint.setantialias (true); Paint.setstrokewidth (width); The setting is a circular way to draw Paint.setstyle (Paint.Style.STROKE); System.out.println (alpha= + alpha); Paint.setalpha (alpha); System.out.println (obtained transparency: + paint.getalpha ()); Paint.setcolor (color.red);}</pre></pre><p><p></p></p>5. Touch Timed Refresh<p><p>After the OnDraw method, we can already draw this circle, but the real problem is that we want to achieve a click to draw a circle in the click position, then we definitely need to get the coordinates of the click Xdown and ydown, So it's definitely necessary to rewrite the ontouchevent method, and we need to let the transparency be the least transparent (alpha=255) when we press it, and in the process of drawing, let the radius of the ring expand, while minimizing the opacity until fully transparent (alpha=0) , this changing process needs to re-refresh the state and redraw the graphics every once in a while, so we use handler to handle it:</p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">@Override /** * Touch Event Method */public boolean ontouchevent (motionevent event) { super.ontouchevent ( event); Switch (event.getaction ()) {case motionevent.action_down: radius = 0; Alpha = max_alpha; width = radius/4; Xdown = (int) Event.getx (); Ydown = (int) event.gety (); Handler.sendemptymessage (0); break; Case Motionevent.action_move: Break ; Case Motionevent.action_up: Break ; Default: Break ; } return true; }</pre></pre><p><p></p></p><p><p>As you can see, we have only implemented the logic inside the action_down, set the radius radius to 0 at each press, the transparency alpha to be completely opaque, and the width to 0, and get the x and y coordinates pressed, After the use of handler sent an empty message, let handler to achieve the timing of the refresh State and drawing the work, we want to let the transparency of the ring when the Alpha pick up to 0 time will not continue to automatically refresh the timing, otherwise, each time the handlemessage is refreshed the state value , and then draw the Graphic:</p></p><p><p></p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">Private Handler Handler = new Handler () { @Override public void Handlemessage (Message msg) { Super.handlemessage (msg); Switch (msg.what) {case 0: flushstate (); Invalidate (); If (alpha! = 0) { //if The opacity is not to 0, continue to refresh, or stop refreshing handler.sendemptymessagedelayed (0); } break; Default: break ; } } /** * Refresh Status * /private void flushstate () { radius + = 5; alpha-= ten; If (alpha < 0) { Alpha = 0; } System.out.println (alpha= + alpha); width = radius/4; Paint.setalpha (alpha); Paint.setstrokewidth (width); } };</pre></pre><p><p></p></p><p><p>We can see that in handler, we rewrite the handlemessage method, in msg.what=0, we call the Flushstate () method to refresh the state, and the invalidate () method to draw the graph, Then use handler.sendemptymessagedelayed (0, 50) to repeat the above work every 50 milliseconds, where invalidate () is provided by android, and flushstate () needs to be implemented by ourselves;</p></p><p><p>According to our needs, each time the status of the refresh work flushstate (), We need to do the following several things:</p></p><p><p>(1) to increase the radius</p></p><p><p>(2) to reduce the transparency and set it to paint;</p></p><p><p>(3) the width of the ring increases and is set to paint</p></p><p><p>(4) for transparency, The maximum value is 255, but if the transparency is reduced to less than 0, for example, 1, then the value of alpha will not be 1, but 255+ (-1) = 254, so we need to add a judgment condition to prevent alpha<0</p></p><p><p></p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">/** * Refresh Status * /private void flushstate () { radius + = 5; alpha-= ten; If (alpha < 0) { Alpha = 0; } System.out.println (alpha= + alpha); width = radius/4; Paint.setalpha (alpha); Paint.setstrokewidth (width); }</pre></pre><p><p></p></p>6. Add some initialization work in the constructor of the two parameters:<p><p></p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">Public Waterwave (context context, attributeset attrs) { Super (context, attrs); Alpha = 0; Radius = 0; Initpaint (); }</pre></pre><p><p></p></p><p><p>At this point, our first step is basically done</p></p><p><p></p></p>second, The realization of multiple click Ring at the same time, the effect of refreshing:<p><p></p></p><p><p>From the surface of the second, we can not find that, no matter how to click, the screen will only have a circle at the same time, this is because each time we click, the center is reset, and all the parameters of the circle are member variables, are shared; not only that, if the previous circle did not disappear, click again, will make the new circle larger speed greatly increased, this is due to the use of the handler.sendemptymessagedelayed (0,50) method, the second click will repeatedly trigger this method, so that the front and back two clicks of the handler.sendemptymessagedelayed () overlap takes effect, so the actual interval is much less than 50 milliseconds, so the refresh rate is much faster</p></p><p><p>So now we have to solve the two small problems, to achieve the effect of:</p></p><p><p></p></p><p><p>The idea of solving these two small problems:</p></p>1. For all water ripple circle sharing parameters:<p><p>The method is to create a new inner wave, which holds the parameters for each circle, each of which corresponds to a wave object, and then, in the OnDraw method, redraws all the circle views at the same time, then a list collection wavelist is needed to hold all the wave Objects. Easy Traversal.</p></p><p><p></p></p>2, for the Handler.sendemptymessagedelayed method in the subsequent click of the time is constantly called, resulting in the problem of more and more rapid Refresh.<p><p>Here you can set a member variable Boolean isstart; the flag is not pressed for the first time, because when we press the first time, we must want to start a timed refresh, call handler.sendemptymessagedelayed, so that the state of the ring constantly changing. But for subsequent clicks, we actually just want it to be refreshed immediately and added to the Wavelist collection without having to send a handler message to invoke Handler.sendemptymessagedelayed. So when we set it to true at the very beginning and set it to false at the first click, when it's set to false, There's a third problem:</p></p><p><p></p></p>3, for the wavelist collection, if you always click to add Wave objects to the collection, then will undoubtedly make this collection more and more large, this is what we do not want to see.<p><p>We want the transparency value of the ring to be alpha to 0, which is completely transparent, to remove it from the wavelist so that it can be reclaimed by garbage collection, so that if you stop after a few clicks, the point will automatically disappear (the alpha value is reduced to 0). Then the corresponding Wave object will also be removed from the wavelist, the size of the wavelist will also become 0, which At some point we can stop the Handler.sendemptymessagedelayed method from continuing to be called, while the Isstart can be reset to True. So when is Isstart set to false? We can set this to False when the flushstate is refreshed because it indicates that the first click has been pressed when the status is Refreshed. then, under the Action_dwon condition of the ontouchevent method, if Isstart is true, the handler message is sent, which means that the first click is not sent but the wave object is added to the Wavelist. Because the first time the call Flushstate has set the Isstart to False.</p></p><p><p>Because of the large changes, the code is as Follows:</p></p><p><p></p></p><pre class="brush:java;gutter:true;">Package com.example.waterwavedemo.ui; Import Java.util.arraylist;import Java.util.collections;import java.util.List; Import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.os.handler;import Android.os.message;import android.util.AttributeSet; Import Android.view.motionevent;import android.view.View; public class Waterwave extends View {/** * Waveform List */private list<wave> wavelist; /** * Maximum opacity, completely opaque */private static final int max_alpha = 255; protected static final int Flush_all =-1; Private Boolean Isstart = true; /**//* Press the x coordinate//*//private int xdown; /**//* when pressed y coordinates//*//private int ydown; /**//* used to indicate the radius of the ring////private float radius; private int alpha; /* * 1, two parameter constructors */public waterwave (context context, attributeset attrs) {super (context, attrs); Wavelist =Collections.synchronizedlist (new arraylist<wave> ()); the/** * Onmeasure method, which determines the size of the control, uses the default */@Override protected void onmeasure (int widthmeasurespec, int Heigh TMEASURESPEC) {super.onmeasure (widthmeasurespec, heightmeasurespec); } @Override/** * To draw the required graphics method, This method is more critical */protected void OnDraw (canvas Canvas) {//redraw All rings for (int i = 0; i < wavelist.size (); i++) {wave wave = Wavelist.get (i); Canvas.drawcircle (wave.xdown, wave.ydown, wave.radius, wave.paint); }}/** * Initialize Paint */private paint initpaint (int alpha, float Width) {/* * Create a new brush */paint paint = new Paint (); Paint.setantialias (true); Paint.setstrokewidth (width); The setting is a circular way to draw Paint.setstyle (Paint.Style.STROKE); System.out.println (alpha= + alpha); Paint.setalpha (alpha); System.out.println (obtained transparency: + paint.getalpha ()); PAint.setcolor (color.red); Return paint; } private Handler Handler = new Handler () {@Override public void Handlemessage (Message Msg) { Super.handlemessage (msg); Switch (msg.what) {case 0:flushstate (); Invalidate (); If (wavelist! = null && wavelist.size () > 0) {handler.sendemptymessagedelayed (0, 50); } break; default:break; } } }; /** * Refresh status */private void Flushstate () {for (int i = 0; i < wavelist.size (); I++) {Wave Wave = Wavelist.get (i); If (isstart = = False && Wave.alpha = = 0) {wavelist.remove (i); Wave.paint = null; Wave = null; Continue } else if (isstart = = True) {isstart = false; } Wave.radius += 5; wave.alpha-= 10; If (wave.alpha < 0) {wave.alpha = 0; } wave.width = wave.radius/4; Wave.paint.setAlpha (wave.alpha); Wave.paint.setStrokeWidth (wave.width); }}//private Paint paint; Private float width; @Override/** * Touch Event Method */public Boolean ontouchevent (motionevent event) {super.ontouchevent (event) ; Switch (event.getaction ()) {case MotionEvent.ACTION_DOWN:Wave wave = New Wave (); Wave.radius = 0; Wave.alpha = max_alpha; Wave.width = wave.radius/4; Wave.xdown = (int) Event.getx (); Wave.ydown = (int) event.gety (); Wave.paint = Initpaint (wave.alpha, wave.width); If (wavelist.size () = = 0) {isstart = true; } System.out.println (isstart= + isstart); Wavelist.add (wave); Scrub a graphic once after clicking Invalidate (); If (isstart) {handler.sendemptymessage (0); } break; Case MotionEvent.ACTION_MOVE:break; Case MotionEvent.ACTION_UP:break; default:break; } return true; } private class Wave {int wavex; int wavey; /** * Used to indicate the radius of the ring */float radius; Paint paint; /** * Press the X coordinate */int xdown; /** * When pressed y coordinates */int ydown; Float width; int alpha; }}</wave></wave></pre><p><p></p></p>three, achieve Full effect (click and move, color random, circle Size Change Speed)<p><p>Is the same as Tuyi, mainly to do a few small places:</p></p>1, let ontouchevent inside of Action_down and action_move response to the same event, in fact, is to remove the Action_down break; then write the processing code to the subsequent Action_move.<p><p></p></p>2. Create a new array of member variables colors, put the color you want, and then in the Initpaint method set color, use Paint.setcolor (colors[(int) (math.random () * ( colors.length-1)]);<p><p></p></p>3, control the change trend of the waveform, this look at personal hobbies, I do this: in the Flushstate:<p><p></p></p><pre class="brush:java;gutter:true;"><pre class="brush:java;gutter:true;">Wave.radius + = wavelist.size ()-i;wave.width = (wave.radius/3); wave.paint.setStrokeWidth (wave.width); wave.alpha-= 10;if (wave.alpha < 0) { Wave.alpha = 0;} Wave.width = Wave.radius/4;wave.paint.setalpha (wave.alpha);</pre></pre><p><p></p></p><p><p>This completes the custom water ripple Effect. The problem is that if the simulator, fast sliding, there will be a lag, on my mobile phone Nexus5, still smooth, should be memory-independent, and may also do some optimizations.</p></p><p><p>Accessories demo:</p></p><p><p>Http://i.cnblogs.com/Files.aspx</p></p><p><p> [go]android Custom Control series Five: custom fancy water ripple effect </p> </p></span>

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.