Android custom view to achieve water rising effect _android

Source: Internet
Author: User
Tags drawtext getcolor

The implementation effect is as follows:

Realize the idea:

1, how to achieve the effect of the round water surface: The use of paint Setxfermode attribute for PorterDuff.Mode.SRC_IN draw the progress of the rectangle and the intersection of the circle to achieve

2, how to water ripple effect: the use of Bezier curve, dynamic change wave crest value, achieve "with the increase in progress, water ripple gradually smaller effect"

Don't talk much, look at the code.

First is the custom attribute value, what are the customizable attribute values?

Background color of the circle: Circle_color, Progress color: Progress_color, Progress display text color: text_color, progress Text size: Text_size, and the last one: Ripple Maximum height: ripple_topheight

<declare-styleable name= "Waterprogressview" > 
 <attr name= "circle_color" format= "Color"/><!-- The color of the circle--> 
 <attr name= "progress_color" format= "Color"/><!--the progress of the colors--> <attr name= 
 "Text_color "format=" color/><!--text--> <attr name= "text_size" format= "Dimension"/>< 
 !--Text Size--> 
 <attr name= "Ripple_topheight" format= "Dimension"/><!--the maximum height of ripples in the water page--> </declare-styleable
>

The following is a partial code for the custom View:waterprogressview:

Member variable

 public class Waterprogressview extends ProgressBar {//default circle background color public static final int DEFAULT
 _circle_color = 0XFF00CCCC;
 The color of the default progress public static final int default_progress_color = 0XFF00CC66;
 The color of the default text is public static final int default_text_color = 0xFFFFFFFF;
 The size of the default text is public static final int default_text_size = 18;

 The default crest highest point public static final int default_ripple_topheight = 10;
 Private context Mcontext;
 Private Canvas Mpaintcanvas;

 Private Bitmap Mbitmap;
 Draw a round brush private Paint mcirclepaint;

 Draw round the color of the brush the private int mcirclecolor;
 Draw the progress of the brush private Paint mprogresspaint;
 Draw the progress of the brush color private int mprogresscolor;
 Draw the progress of path private path Mprogresspath;

 Bezier curve Crest maximum private int mrippletop = 10;
 The brush of progress text private Paint mtextpaint;
 The color of the progress text is private int mtextcolor;
 private int mtextsize = 18;

 Target progress, which is the progress of processing tasks when double-clicking, will affect the amplitude of the curve private int mtargetprogress = 50;
Listen for double click and click Events Private Gesturedetector mgesturedetector; }

Get custom Attribute values:

private void Getattrvalue (AttributeSet attrs) { 

 TypedArray ta = mcontext.obtainstyledattributes (Attrs, R.styleable.waterprogressview); 
 Mcirclecolor = Ta.getcolor (r.styleable.waterprogressview_circle_color,default_circle_color);    
 Mprogresscolor = Ta.getcolor (r.styleable.waterprogressview_progress_color,default_progress_color); 
 Mtextcolor = Ta.getcolor (r.styleable.waterprogressview_text_color,default_text_color);  
 mtextsize = (int) ta.getdimension (r.styleable.waterprogressview_text_size, desityutils.sp2px (MContext,DEFAULT_TEXT _size)); 
 Mrippletop = (int) ta.getdimension (r.styleable.waterprogressview_ripple_topheight,desityutils.dp2px (MContext, Default_ripple_topheight)); 
 Ta.recycle ();

}

Define constructors, notemProgressPaint.setXfermode

Call this constructor public Waterprogressview (Context,null) When this class is new;

Call this constructor public Waterprogressview when the custom view is defined in the XML file (context, AttributeSet attrs) {This (context, attrs,0);} 
 Public Waterprogressview (context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr); 
 This.mcontext = context; 
 Getattrvalue (ATTRS); 
 Initializes the related property of the brush Initpaint (); 
Mprogresspath = new Path (); 
 } private void Initpaint () {//init Paint mcirclepaint = new Paint (); 
 Mcirclepaint.setcolor (Mcirclecolor); 
 Mcirclepaint.setstyle (Paint.Style.FILL); 
 Mcirclepaint.setantialias (TRUE); 

 Mcirclepaint.setdither (TRUE); 
 Paint mprogresspaint = new Paint () to initialize the drawing progress; 
 Mprogresspaint.setcolor (Mprogresscolor); 
 Mprogresspaint.setantialias (TRUE); 
 Mprogresspaint.setdither (TRUE); 
 Mprogresspaint.setstyle (Paint.Style.FILL); In fact, Mprogresspaint painting is also a rectangle, when set Xfermode to PorterDuff.Mode.SRC_IN after the intersection of the circle and progress rectangle, then semicircle mprogresspaint.setxfermode (new PorterduffxferMode (PorterDuff.Mode.SRC_IN)); 
 Initializes the brush mtextpaint = new Paint () of the drawing progress text; 
 Mtextpaint.setcolor (Mtextcolor); 
 Mtextpaint.setstyle (Paint.Style.FILL); 
 Mtextpaint.setantialias (TRUE); 
 Mtextpaint.setdither (TRUE);

Mtextpaint.settextsize (mtextsize); }

onMeasure()Method Code:

@Override
protected synchronized void onmeasure (int widthmeasurespec, int heightmeasurespec) { 
 //when used, The dimension of the view needs to be clearly defined, that is, the measurespec.exactly
 int width = measurespec.getsize (widthmeasurespec) with the measurement mode; 
 int height = measurespec.getsize (heightmeasurespec); 
 Setmeasureddimension (width,height); 

 Initialize the bitmap so that all the drawcircle,drawpath,drawtext are draw on the canvas where the bitmap is located, and then the bitmap is painted on the OnDraw of the canvas method.
 So the width,height of this Bitmap need to subtract left,top,right,bottom padding
 mbitmap = Bitmap.createbitmap (Width-getpaddingleft () -getpaddingright (), Height-getpaddingtop ()-getpaddingbottom (), Bitmap.Config.ARGB_8888); 
 Mpaintcanvas = new Canvas (MBITMAP);
}

Next is the core part, the code in the OnDraw. We first draw the circle, progress bar, progress text to the bitmap of the custom canvas, and bitmap This draw to OnDraw in the canvas method. Drawcircle and DrawText should be no difficulty, the key point is to draw progress bar, how to draw it? Since there is water ripple effect, have curve, use DrawPath.

The DrawPath process is as follows:


Where the ratio code is as follows, that is, ratio is the percentage of the total progress for the current progress

float ratio = getprogress () *1.0f/getmax ();

Since the coordinates are extended from point B to right forward, the coordinates of point A are (width, (1-ratio) *height), where width is bitmap and height is bitmap. Let's start with the Mprogresspath.moveto to point a, and then determine the path's key points clockwise from point A, as shown in the following code:

int righttop = (int) ((1-ratio) *height);
Mprogresspath.moveto (width,righttop);
Mprogresspath.lineto (width,height);
Mprogresspath.lineto (0,height);
Mprogresspath.lineto (0,righttop);

So Mprogresspath has been lineto to C and needs to form a ripple effect between point A and C, you need to draw a Bezier curve between point A and C.


We set the peak of 10, then a section of wavelength is 40, need to draw width*1.0f/40 a paragraph such a curve, then draw the curve of the code as follows:

int count = (int) Math.ceil (width*1.0f/(*4));
for (int i=0; i<count; i++) { 
 mprogresspath.rquadto (10,10,2* 10,0);
 Mprogresspath.rquadto (10,-10,2* 10,0);  
}

Mprogresspath.close ();
Mpaintcanvas.drawpath (Mprogresspath,mprogresspaint);


This will draw the surface of the water rising and ripple effect of the progress bar. But we also have to achieve as the water rises, the closer the target progress, the water surface ripples should be smaller, should be 10 out for the variable defined as the initial peak value of Mrippletop, and then define top as the progress of the target progress to the real-time peak value of the curve, Where the mtargetprogress is the target progress, because there is a goal progress to achieve the current progress in the process of approaching the target progress, the surface gradually to the plane effect:

float top = (mtargetprogress-getprogress ()) *1.0f/mtargetprogress* mrippletop;

So the code for DrawPath is updated as follows:

float top = (mtargetprogress-getprogress ()) *1.0f/mtargetprogress* mrippletop;

for (int i=0; i<count; i++) { 
 mprogresspath.rquadto (mrippletop,top,2* mrippletop,0);
 Mprogresspath.rquadto (mrippletop,-top,2* mrippletop,0); 
}

So you can really achieve the progress of the water surface rising bar.

But how to achieve a double click when the water surface from 0% up to the target progress, click when the water in the target progress continuously surging effect?
First of all, the implementation of the double-click Effect: This simple, define a handler, when double click, every once in a while, handler.postDelayed(runnable,time) progress+1 in runnable invalidate() constantly update progress, until the current progress to mtargetprogress.

The code is as follows

/**
 * Realize double click Animation * *
private void Startdoubletapanimation () { 
 setprogress (0); 
 Doubletaphandler.postdelayed (doubletaprunnable,60);
}

Private Handler Doubletaphandler = new Handler () { 
 @Override public 
 void Handlemessage (msg) {  
 Super.handlemessage (msg); 
 }
;

Double-click the processing thread to send data once in 60ms
private Runnable doubletaprunnable = new Runnable () { 
 @Override public 
 void Run () {  
 if (getprogress () < mtargetprogress) {   
  invalidate ();   
  Setprogress (Getprogress () +1);   
  Doubletaphandler.postdelayed (doubletaprunnable,60);  
 } else {   
  doubletaphandler.removecallbacks (doubletaprunnable);  
 } 
 }
;

Double-click the effect to achieve, that how to achieve the clicked effect? Click when the surface of the water constantly surging for a period of time, the surface ripple gradually smaller, and then the surface leveling. We can define a Msingletapanimationcount variable as the number of times the water surges, and then, like the process of double-clicking, define a handler to send a message to update the interface at intervals mSingleTapAnimationCount-- , Then we alternately let the initial wave once for the first time is negative, it can achieve the effect of water surging.

The core code is as follows:

private void Startsingletapanimation () { 
 issingletapanimation = true; 
 Singletaphandler.postdelayed (singletaprunnable,200);
}

Private Handler Singletaphandler = new Handler () { 
 @Override public 
 void Handlemessage (msg) {  
 Super.handlemessage (msg); 
 }
;

Click the processing thread to send data once in 200ms
private Runnable singletaprunnable = new Runnable () { 
 @Override public 
 void Run () {  
 if (Msingletapanimationcount > 0) {   
  invalidate ();   
  msingletapanimationcount--;   
  Singletaphandler.postdelayed (singletaprunnable,200);  
 } else {   
  singletaphandler.removecallbacks (singletaprunnable);  
 Whether the click Animation  
  issingletapanimation = false;   
 The Reset click Animation runs 50 times Msingletapanimationcount = n}}
;

The code in OnDraw makes the appropriate changes, because when you click the drawing logic of the curve part of the DrawPath when you double-click it, we define a variable issingletapanimation whether you are clicking or double-clicking the animation.

The changed code is as follows:

Draw Progress Mprogresspath.reset ();
Starting at the top right draw path int righttop = (int) (1-ratio) *height);
Mprogresspath.moveto (Width,righttop);
Mprogresspath.lineto (Width,height);
Mprogresspath.lineto (0,height);

Mprogresspath.lineto (0,righttop);
Draws a Bezier curve, forming a wavy line int count = (int) Math.ceil (width*1.0f/(Mrippletop *4)); Not click Animation State if (!issingletapanimation&&getprogress () >0) {Float top = (mtargetprogress-getprogress) 
 ) *1.0f/mtargetprogress* Mrippletop;   
 for (int i=0; i<count; i++) {mprogresspath.rquadto (mrippletop,-top,2* mrippletop,0); 
 Mprogresspath.rquadto (mrippletop,top,2* mrippletop,0); } else {/Click Animation state, in order to enlarge the effect, enlarge the mrippletop twice times//At the same time even when the curve goes as shown, the odd curve is just the opposite of float top = (msingletapanimationcount*1 
 .0f/50) *10;  Odd-even time curve switch if (msingletapanimationcount%2==0) {for (int i=0; i<count; i++) {mprogresspath.rquadto (mrippletop    
  *2,top*2,2* mrippletop,0);   
 Mprogresspath.rquadto (Mrippletop *2,-top*2,2* mrippletop,0); } else {for (int i=0; i< count;    
  i++) {mprogresspath.rquadto (mrippletop *2,-top*2,2* mrippletop,0); 
 Mprogresspath.rquadto (Mrippletop *2,top*2,2* mrippletop,0);
}} mprogresspath.close (); Mpaintcanvas.drawpath (Mprogresspath,mprogresspaint);

Basically the important code with the core logic and code is on it.

Note the point:

1, when drawcircle to take into account the padding, then circle width and height of getwidth and getheight minus padding value, the code is as follows:

Custom bitmap width and height
int width = getwidth ()-getpaddingleft ()-getpaddingright ();
int height = getheight ()-getpaddingtop ()-getpaddingbottom ();

Draw round
mpaintcanvas.drawcircle (width/2,height/2,height/2,mcirclepaint);

2, when DrawText, not from the text of the middle of the height of the draw, but from the beginning of the baseline draw

So how do I get baseline's height coordinates?

Paint.fontmetrics metrics = Mtextpaint.getfontmetrics ();
Because ascent is above baseline, so ascent is negative. Descent+ascent is negative, so it is minus rather than adding
float BaseLine = HEIGHT*1.0F/2-(metrics.descent+metrics.ascent)/2;

The full code for DrawText is as follows:

Draw Progress Text
String text = ((int) (ratio*100)) + "%";

Gets the width of the text
float textWidth = mtextpaint.measuretext (text);

Paint.fontmetrics metrics = Mtextpaint.getfontmetrics ();
Descent+ascent is negative, so it is minus rather than adding
float BaseLine = HEIGHT*1.0F/2-(metrics.descent+metrics.ascent)/2;
Mpaintcanvas.drawtext (Text,width/2-textwidth/2,baseline,mtextpaint);

3, because to take into account the padding, remember to OnDraw in the canvas translate to the (getPaddingLeft(),getPaddingTop()) place.

Canvas.translate (Getpaddingleft (), Getpaddingtop ());
Canvas.drawbitmap (Mbitmap,0,0,null);

Finally, remember to draw the custom bitmap to the canvas in OnDraw. Here are the progress strips for customizing the rise of the surface of the water to be finished.

Summarize

The above is the entire content of this article, I hope the content of this article for everyone's study or work to bring certain help, if you have questions you can message exchange.

Related Article

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.