Recently wrote a custom countdown control in the project, the effect is that after the countdown begins, Hearts are gradually filled. Effects such as:
It is divided into two parts: a timer and a drawing bitmap.
Timers use the timer and TimerTask to perform a timertask run function once per second to redraw the control. The code is as follows:
Mtimer = new Timer (), mtimertask = new TimerTask () {@Overridepublic void run () {postinvalidate (); synchronized (this) {if (i Ndex >) {index = 1;mtimer.cancel ();} index++;}}; Mtimer.schedule (Mtimertask, 1000, 1000);
The idea of drawing is probably:
1. The bitmap is obtained from the image resources, and its width and height are attained;
2. Overload the Onmeasure and Onsizechanged functions, set and get the width and height of the control;
3. Use the Porterduffxfermode graphics blending mode to get the desired bitmap;
4. Reload the OnDraw function, and in the function, zoom the bitmap from the previous step to the size of the control to display.
Let's take a look at a few important parts and the rest of the code will be appended.
1. Overloading the Onmeasure function to complete the measurement of the control size
@Overridepublic void onmeasure (int widthmeasurespec, int heightmeasurespec) {int RESULTW = 0;if (Measurespec.getmode ( WIDTHMEASURESPEC) = = measurespec.exactly) {RESULTW = Measurespec.getsize (widthmeasurespec);} else {if ( Measurespec.getmode (widthmeasurespec) = = measurespec.at_most) {RESULTW = Math.min (Bwidth,measurespec.getsize ( Widthmeasurespec));}} int resulth = 0;if (Measurespec.getmode (heightmeasurespec) = = measurespec.exactly) {resulth = Measurespec.getsize ( HEIGHTMEASURESPEC);} else {if (Measurespec.getmode (heightmeasurespec) = = measurespec.at_most) {resulth = Math.min (Bheight, Measurespec.getsize (Heightmeasurespec));}} Setmeasureddimension (RESULTW, resulth);}
If I set the following in the XML file:
<com.example.grownheart.grownheart android:id= "@+id/grownheart" android:layout_width= "100DP"
We set the width and height of the control is the specific value: 100DP, then the Onmeasure function in the measurement of the width of the control Widthmeasurespec/heightmeasurespec GetMode is
measurespec.exactly, the width height of the control is GetSize, which is the 100DP we set.
If we configure the following in XML:
<com.example.grownheart.grownheart android:id= "@+id/grownheart2" android:layout_width= "wrap_content "
Then Widthmeasurespec/heightmeasurespec GetMode is Measurespec.at_most, when the width of the control is the width of the picture resource (or high) and the remaining width (or height) of the parent container is smaller
The one.
2. Get the width and height of the control in the Onsizechanged function
@Overridepublic void onsizechanged (int w, int h, int oldw, int oldh) {super.onsizechanged (W, H, OLDW, OLDH); width = W;heig HT = H;BM = Bitmap.createbitmap (Bwidth, Bheight, Bitmap.Config.ARGB_8888); Mcanvas = new Canvas (BM);
the int w and int h in the parameter are the width and height of the control, respectively.
3. The desired bitmap is obtained through a mix of graphs in the OnDraw.
The border and solid graphs are as follows:
First draw the border graph to bitmap, and then create a new canvas layer to draw a red rectangle on that layer, each time the height of the rectangle is changed. Then set the Piant graphics blending mode, Mpaint.setxfermode (new
Porterduffxfermode (PorterDuff.Mode.DST_IN)), followed by drawing a solid graph into the layer so that overlapping areas can be obtained. The restore is then drawn on the layer. Finally through the Createscaledbitmap will
Bitmap zoom to the control size and display.
Source
public class Grownheart extends View {public Timer mtimer;public timertask mtimertask;public int bwidth;//bitmap width public int bheight;//Bitmap Height public int width;//control width public int height;//control height public Bitmap charm;//resource bitmap public Bitmap charm_on; Resource Bitmap public Bitmap bm;public Canvas mcanvas;public Paint mpaint;public float h;private static int index;public static fi NAL porterduffxfermode modein;public static final Porterduffxfermode modeout;static {modein = new Porterduffxfermode ( PorterDuff.Mode.DST_IN); modeout = new Porterduffxfermode (PorterDuff.Mode.SRC_IN);} Public Grownheart (Context context) {super (context); init ();} Public Grownheart (context context, AttributeSet Attrs) {Super (context, attrs); init ();} public void init () {Mtimer = new Timer (); mtimertask = new TimerTask () {@Overridepublic void run () {postinvalidate (); SYNCHR Onized (This) {if (Index >) {index = 1;mtimer.cancel ();} LOG.I ("Grownheart", "timertask1:index=" + index); index++; LOG.I ("Grownheart", "timertask2:index=" + index);}}};charm = Bitmapfactory.decoderesource (Getresources (), r.drawable.chatroom_charm). Copy (Bitmap.Config.ARGB_8888, true); charm_on = Bitmapfactory.decoderesource (Getresources (), r.drawable.chatroom_charm_on). Copy ( bitmap.config.argb_8888,true); bwidth = Charm_on.getwidth (); bheight = Charm_on.getheight (); H = Bheight/60f;index = 1;mpaint = new Paint (); Mpaint.setantialias (true); Mpaint.setfilterbitmap (false);} public void Starttimer () {mtimer.schedule (mtimertask, 1000, 1000);} @Overridepublic void onmeasure (int widthmeasurespec, int heightmeasurespec) {int RESULTW = 0;if (Measurespec.getmode ( WIDTHMEASURESPEC) = = measurespec.exactly) {RESULTW = Measurespec.getsize (widthmeasurespec);} else {if ( Measurespec.getmode (widthmeasurespec) = = measurespec.at_most) {RESULTW = Math.min (Bwidth,measurespec.getsize ( Widthmeasurespec));}} int resulth = 0;if (Measurespec.getmode (heightmeasurespec) = = measurespec.exactly) {resulth = Measurespec.getsize ( HEIGHTMEASURESPEC);} else {if (Measurespec.getmode (heightmeasureSPEC) = = measurespec.at_most) {resulth = Math.min (Bheight,measurespec.getsize (Heightmeasurespec));}} Setmeasureddimension (RESULTW, resulth);} @Overridepublic void onsizechanged (int w, int h, int oldw, int oldh) {super.onsizechanged (W, H, OLDW, OLDH); width = W;heig HT = H;BM = Bitmap.createbitmap (Bwidth, Bheight, Bitmap.Config.ARGB_8888); Mcanvas = new Canvas (BM); Draw Bitmappublic Bitmap Makebitmap () {//Draw the underlying image first Mcanvas.drawbitmap (charm, 0, 0, mpaint); int i = mcanvas.savelayer (0, 0, b M.getwidth (), Bm.getheight (), Null,canvas.all_save_flag); Mpaint.setcolor (color.red); LOG.I ("Grownheart", "ondraw:index=" + Index) mcanvas.drawrect (new RECTF (0f, (60-index) * H, Bm.getwidth (), Bm.getheight ()), mpaint); Mpaint.setxfermode (Modein); Mcanvas.drawbitmap (charm_on, 0, 0, mpaint); Mpaint.setxfermode (null); Mcanvas.restoretocount (i); return BM;} @Overridepublic void OnDraw (canvas canvas) {Canvas.drawbitmap (Bitmap.createscaledbitmap (Makebitmap (), width, height, true), 0, 0, mpaint);}}
<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:gravity= "center" tools:context= "com.example.grownheart.MainActivity" > < Com.example.grownheart.GrownHeart android:id= "@+id/grownheart" android:layout_width= "Wrap_content" android:layout_height= "wrap_content"/> </RelativeLayout>
public class Mainactivity extends Activity {@Overrideprotected void onCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); Grownheart grownheart= (Grownheart) Findviewbyid (R.id.grownheart); Grownheart.starttimer ();}
Android custom Control implementation