First, put out the implementation of the effect of the diagram:
GIF may be a bit too fast to run on a real machine better. Our main idea is to use attribute animation to dynamically draw the selected state and the drawing process of the hook. See the above effect picture, I believe we can not wait to be tempted, then let us start.
Customize the first step of view: Custom properties.
<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
<declare-styleable name= "Smoothcheckbox ">
<!--animation duration-->
<attr name=" duration "format=" integer "></attr>
<!--border Width-- >
<attr name= "strikewidth" format= "dimension|reference" ></attr>
<!--border color-->
<attr name= "bordercolor" format= "color|reference" ></attr>
<!--the color of the selected state-->
<attr name = "Trimcolor" format= "color|reference" ></attr>
<!--to tick color-->
<attr name= "Tickcolor" format= "Color|reference" ></attr>
<!--hook width--> <attr name= "Tickwidth"
Dimension|reference "></attr>
</declare-styleable>
</resources>
We named the checkbox Smoothcheckbox, defined a few, and so on, to use the attributes. This step is very simple, I believe we are proficient.
Next look at onmeasure (int widthmeasurespec, int heightmeasurespec):
@Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (Widthmeasurespec, Hei
GHTMEASURESPEC);
int widthsize = measurespec.getsize (Widthmeasurespec);
int widthmode = Measurespec.getmode (Widthmeasurespec);
if (Widthmode = = measurespec.exactly) {mwidth = widthsize;
else {mwidth = 40;
int heightsize = measurespec.getsize (Heightmeasurespec);
int heightmode = Measurespec.getmode (Heightmeasurespec);
if (Heightmode = = measurespec.exactly) {mheight = heightsize;
else {mheight = 40;
} setmeasureddimension (Mwidth, mheight);
int size = Math.min (Mwidth, mheight);
Center = SIZE/2;
Mradius = (int) ((size-mstrokewidth)/2/1.2f);
Startpoint.set (center * 14/30, center * 28/30);
Breakpoint.set (center * 26/30, center * 40/30);
Endpoint.set (center * 44/30, center * 20/30);
Downlength = (float) math.sqrt (Math.pow (startpoint.x-breakpoint.x, 2f) + MATH.POW (STARTPOINT.Y-BREAKPOINT.Y, 2f)); Uplength = (FloaT) math.sqrt (MATH.POW (endpoint.x-breakpoint.x, 2f) + MATH.POW (ENDPOINT.Y-BREAKPOINT.Y, 2f));
Totallength = Downlength + uplength; }
At first it measured the width and height of the Smoothcheckbox, and the default width of the height was defined in a casual way, and of course you can modify and refine it yourself. Then it is to set the radius and so on, the final startpoint, breakpoint, endpoint respectively corresponding to check the tick three points (as for why these few numbers, that is the empirical value); Downlength is the distance between StartPoint and breakpoint, and the corresponding uplength is the distance between breakpoint and endpoint. This is the following illustration:
Before looking at OnDraw (Canvas Canvas), let's take a look at the two sets of animations, which are the animations in the selected state and the unselected animations:
The animated private void Checkedanimation () {Animatedvalue = 0f is not checked to the selected animation;
Tickvalue = 0f;
Animated mvalueanimator = Valueanimator.offloat (0f, 1.2f, 1f) when selected. Setduration (2 * DURATION/5);
Mvalueanimator.setinterpolator (New Acceleratedecelerateinterpolator ());
The Animated Mtickvalueanimator = Valueanimator.offloat (0f, 1f). Setduration (3 * DURATION/5);
Mtickvalueanimator.setinterpolator (New Linearinterpolator ()); Mtickvalueanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void
Onanimationupdate (Valueanimator valueanimator) {//Get animation execution Progress Tickvalue = (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
}); Mvalueanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void
Onanimationupdate (Valueanimator valueanimator) {//Get animation execution Progress Animatedvalue = (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
}); Mvalueanimator.addlistener (New Animatorlisteneradapter () {@Override public void Onanimationend (animator animation) {//When the background of the animation is completed and then start to tick the animation Mtickvalueanimator.start ();
LOG.I (TAG, "Mtickvalueanimator.start ();");
Mvalueanimator.start ();
///from selected to unchecked animation private void Uncheckedanimation () {animatedvalue = 0f;
Mvalueanimator = Valueanimator.offloat (1f, 0f). Setduration (2 * DURATION/5);
Mvalueanimator.setinterpolator (New Accelerateinterpolator ()); Mvalueanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void
Onanimationupdate (Valueanimator valueanimator) {animatedvalue = (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
});
Mvalueanimator.start (); }
These two sets of animations are called when you click Smoothcheckbox. Similarly, it is the progress of animation execution in animation execution, and then call Postinvalidate (), let Smoothcheckbox redraw. After reading this is the ultimate big recruit OnDraw (Canvas Canvas):
@Override protected void OnDraw (Canvas Canvas) {Super.ondraw (Canvas);
Canvas.save ();
Drawborder (canvas);
Drawtrim (canvas);
if (ischecked) {Drawtick (canvas);
} canvas.restore ();
///Draw to hook private void Drawtick (Canvas Canvas) {//Get draw on hook progress Float temp = Tickvalue * TOTALLENGTH;
LOG.I (TAG, "temp:" + temp + "Downlength:" + downlength); To determine if it is just the beginning of the tick, that is equal to StartPoint if (Float.compare (Tickvalue, 0f) = 0) {log.i (TAG, "startpoint:" + Startpoint.x + ",
"+ Startpoint.y);
Path.reset ();
Path.moveto (Startpoint.x, STARTPOINT.Y); ///If the progress of the draw is over breakpoint, that is (Breakpoint,endpoint] if (temp > Downlength) {path.moveto (startpoint.x, StartPoint
. y);
Path.lineto (Breakpoint.x, BREAKPOINT.Y);
LOG.I (TAG, "endPoint:" + endpoint.x + "," + endpoint.y); Path.lineto ((endpoint.x-breakpoint.x) * (temp-downlength)/Uplength + Breakpoint.x, (endpoint.y-breakpoint.y) * (Te
Mp-downlength)/uplength + Breakpoint.y); The progress of the else {//Draw check is between startpoinit and breakpoint, i.e. (Startpoint,breakpoint] LOG.I (TAG, "Down x:" + (breakpoint.x-startpoint.x) * temp/downlength + ", down Y:" + (break
POINT.Y-STARTPOINT.Y) * temp/downlength); Path.lineto ((breakpoint.x-startpoint.x) * temp/downlength + startpoint.x, (breakpoint.y-startpoint.y) * TEMP/DOWNL
Ength + startpoint.y);
} canvas.drawpath (path, tickpaint);
//Draw Border private void Drawborder (Canvas Canvas) {float temp;
The animatedvalue lets the border produce a "overshooting" animation if (Animatedvalue > 1f) {temp = Animatedvalue * Mradius;
else {temp = Mradius;
Canvas.drawcircle (center, center, temp, borderpaint); }//Draw checkbox internal private void Drawtrim (Canvas Canvas) {canvas.drawcircle (center, center, (mradius-mstrokewidth) * ani
Matedvalue, Trimpaint); }
OnDraw (Canvas Canvas) code in the logic is basically added annotation, the main principle is to understand the relatively simple. In the drawing of the check to distinguish between the current state of the draw on the hook, and then the corresponding processing to draw lines, the rest is simple. About Smoothcheckbox's explanation to here is almost.
The complete code for Smoothcheckbox is posted below:
public class Smoothcheckbox extends View implements View.onclicklistener {//Animation duration private long duration;
Border width private float mstrokewidth;
To hook width private float mtickwidth;
Interior brushes private Paint trimpaint;
Border brush private Paint borderpaint;
To sketch pen private Paint tickpaint;
Default border width private float defaultstrikewidth;
Default to hook width private float defaulttickwidth;
width private int mwidth;
highly private int mheight;
Border color private int bordercolor;
Interior color private int trimcolor;
to tick color private int tickcolor;
Radius private int Mradius;
Center point Private Int Center;
Whether the private Boolean ischecked is selected;
On the hook downward length private float downlength;
The length of the hook upward private float uplength;
The total length of the hook is private float totallength;
Listener Private Oncheckedchangelistener listener;
Private Valueanimator Mvalueanimator;
Private Valueanimator Mtickvalueanimator;
private float Animatedvalue;
private float Tickvalue;
To tick start dot private point startpoint = new points (); Right hookTurn private Point breakpoint = new points ();
To tick end dot private point endPoint = new points ();
private static final String TAG = "Smoothcheckbox";
private static final String key_instance_state = "Instancestate";
Private path PATH = new Path ();
public void Setoncheckedchangelistener (Oncheckedchangelistener listener) {This.listener = listener;
The public Smoothcheckbox {This (context, NULL);
Public Smoothcheckbox (context, AttributeSet attrs) {This (context, attrs, 0);
Public Smoothcheckbox (context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr);
TypedArray a = GetContext (). Obtainstyledattributes (Attrs, R.styleable.smoothcheckbox);
Duration = A.getint (r.styleable.smoothcheckbox_duration, 600); Defaultstrikewidth = Typedvalue.applydimension (Typedvalue.complex_unit_dip, 1, Getresources (). GetDisplayMetrics ())
; Mstrokewidth = A.getdimension (R.styleable.smoothcheckbox_strikewidth, DefaultstrikewiDTH);
Defaulttickwidth = Typedvalue.applydimension (Typedvalue.complex_unit_dip, 2, Getresources (). GetDisplayMetrics ());
Mtickwidth = A.getdimension (R.styleable.smoothcheckbox_tickwidth, defaulttickwidth); BorderColor = A.getcolor (R.styleable.smoothcheckbox_bordercolor, Getresources (). GetColor (Android.
R.color.darker_gray)); Trimcolor = A.getcolor (R.styleable.smoothcheckbox_trimcolor, Getresources (). GetColor (Android.
R.color.holo_green_light)); Tickcolor = A.getcolor (R.styleable.smoothcheckbox_tickcolor, Getresources (). GetColor (Android.
R.color.white));
A.recycle ();
Trimpaint = new Paint (Paint.anti_alias_flag);
Trimpaint.setstyle (Paint.Style.FILL);
Trimpaint.setcolor (Trimcolor);
Borderpaint = new Paint (Paint.anti_alias_flag);
Borderpaint.setstrokewidth (Mstrokewidth);
Borderpaint.setcolor (bordercolor);
Borderpaint.setstyle (Paint.Style.STROKE);
Tickpaint = new Paint (Paint.anti_alias_flag);
Tickpaint.setcolor (Tickcolor); Tickpaint.setstyle (Paint.Style.STROKE);
Tickpaint.setstrokecap (Paint.Cap.ROUND);
Tickpaint.setstrokewidth (Mtickwidth);
Setonclicklistener (this); @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (Widthmeasurespec,
HEIGHTMEASURESPEC);
int widthsize = measurespec.getsize (Widthmeasurespec);
int widthmode = Measurespec.getmode (Widthmeasurespec);
if (Widthmode = = measurespec.exactly) {mwidth = widthsize;
else {mwidth = 40;
int heightsize = measurespec.getsize (Heightmeasurespec);
int heightmode = Measurespec.getmode (Heightmeasurespec);
if (Heightmode = = measurespec.exactly) {mheight = heightsize;
else {mheight = 40;
} setmeasureddimension (Mwidth, mheight);
int size = Math.min (Mwidth, mheight);
Center = SIZE/2;
Mradius = (int) ((size-mstrokewidth)/2/1.2f);
Startpoint.set (center * 14/30, center * 28/30);
Breakpoint.set (center * 26/30, center * 40/30); Endpoint.set (center * 44/30, center * 20/30);
Downlength = (float) math.sqrt (Math.pow (startpoint.x-breakpoint.x, 2f) + MATH.POW (STARTPOINT.Y-BREAKPOINT.Y, 2f));
Uplength = (float) math.sqrt (Math.pow (endpoint.x-breakpoint.x, 2f) + MATH.POW (ENDPOINT.Y-BREAKPOINT.Y, 2f));
Totallength = Downlength + uplength;
} @Override protected void OnDraw (Canvas Canvas) {Super.ondraw (Canvas);
Canvas.save ();
Drawborder (canvas);
Drawtrim (canvas);
if (ischecked) {Drawtick (canvas);
} canvas.restore ();
@Override protected parcelable onsaveinstancestate () {Bundle Bundle = new Bundle ();
Bundle.putparcelable (Key_instance_state, Super.onsaveinstancestate ());
Bundle.putboolean (Key_instance_state, ischecked);
return bundle; } @Override protected void Onrestoreinstancestate (parcelable state) {if (state instanceof Bundle) {Bundle Bundle
= (Bundle) state;
Boolean ischecked = Bundle.getboolean (key_instance_state);
Setchecked (ischecked); Super.onrestoreinstancestate (Bundle.getparcelable (Key_instance_state));
Return
} super.onrestoreinstancestate (state);
}//Toggle state private void Toggle () {ischecked =!ischecked;
if (listener!= null) {listener.oncheckedchanged (this, ischecked);
} if (ischecked) {checkedanimation ();
else {uncheckedanimation ();
}///is not selected to the selected animation private void Checkedanimation () {animatedvalue = 0f;
Tickvalue = 0f;
Mvalueanimator = Valueanimator.offloat (0f, 1.2f, 1f). Setduration (2 * DURATION/5);
Mvalueanimator.setinterpolator (New Acceleratedecelerateinterpolator ());
Mtickvalueanimator = Valueanimator.offloat (0f, 1f). Setduration (3 * DURATION/5);
Mtickvalueanimator.setinterpolator (New Linearinterpolator ()); Mtickvalueanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void Onanimationu
Pdate (Valueanimator valueanimator) {tickvalue = (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
}); Mvalueanimator.addupdatelistener (New ValueaniMator. Animatorupdatelistener () {@Override public void onanimationupdate (Valueanimator valueanimator) {Animatedvalue
= (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
}); Mvalueanimator.addlistener (New Animatorlisteneradapter () {@Override public void Onanimationend (animator animation)
{Mtickvalueanimator.start ();
LOG.I (TAG, "Mtickvalueanimator.start ();");
Mvalueanimator.start ();
///from selected to unchecked animation private void Uncheckedanimation () {animatedvalue = 0f;
Mvalueanimator = Valueanimator.offloat (1f, 0f). Setduration (2 * DURATION/5);
Mvalueanimator.setinterpolator (New Accelerateinterpolator ()); Mvalueanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void Onanimationupdat
E (Valueanimator valueanimator) {animatedvalue = (float) valueanimator.getanimatedvalue ();
Postinvalidate ();
}
});
Mvalueanimator.start ();
///Draw to hook private void Drawtick (Canvas Canvas) { float temp = Tickvalue * TOTALLENGTH;
LOG.I (TAG, "temp:" + temp + "Downlength:" + downlength);
if (Float.compare (Tickvalue, 0f) = = 0) {log.i (TAG, "startpoint:" + startpoint.x + "," + startpoint.y);
Path.reset ();
Path.moveto (Startpoint.x, STARTPOINT.Y);
} if (Temp > Downlength) {path.moveto (startpoint.x, STARTPOINT.Y);
Path.lineto (Breakpoint.x, BREAKPOINT.Y);
LOG.I (TAG, "endPoint:" + endpoint.x + "," + endpoint.y); Path.lineto ((endpoint.x-breakpoint.x) * (temp-downlength)/Uplength + Breakpoint.x, (endpoint.y-breakpoint.y) * (Te
Mp-downlength)/uplength + Breakpoint.y); else {log.i (TAG, "Down x:" + (breakpoint.x-startpoint.x) * temp/downlength + ", down Y:" + (Breakpoint.y-sta
RTPOINT.Y) * temp/downlength); Path.lineto ((breakpoint.x-startpoint.x) * temp/downlength + startpoint.x, (breakpoint.y-startpoint.y) * TEMP/DOWNL
Ength + startpoint.y);
} canvas.drawpath (path, tickpaint); //Draw Border private void DrAwborder (Canvas Canvas) {float temp;
if (Animatedvalue > 1f) {temp = Animatedvalue * Mradius;
else {temp = Mradius;
Canvas.drawcircle (center, center, temp, borderpaint); ///Draw checkbox internal private void Drawtrim (Canvas Canvas) {canvas.drawcircle (center, center, (mradius-mstrokewidth) *
Animatedvalue, Trimpaint);
@Override public void OnClick (view view) {toggle ();
/** * To determine whether the checkbox is selected * * @return * * * public boolean ischecked () {return ischecked; /** * Set checkbox status * * @param ischecked Check/public void setchecked (Boolean ischecked) {This.setchecke
D (ischecked, false); /** * Set checkbox status * * @param ischecked whether the * @param isanimation Toggle is animated/public void setchecked (Boole
An ischecked, Boolean isanimation) {this.ischecked = ischecked;
if (isanimation) {if (ischecked) {checkedanimation ();
else {uncheckedanimation (); } else {animatedvalue = ischecked? 1f:0f;
Tickvalue = 1f;
Invalidate ();
} if (listener!= null) {listener.oncheckedchanged (this, ischecked); } public interface Oncheckedchangelistener {void oncheckedchanged (Smoothcheckbox smoothcheckbox, Boolean ischecked
); }
}
Summarize
The above is the entire content of this article, I hope the content of this article for everyone's study or work can bring certain help, if you have questions you can message exchange.