Overview:
Most of the time we want to write some kind of clock and compass control, but we can't find the right demo. I think you might just go straight to the movies. In the case of Android with canvas and paint such a good painter, or choose to use the picture, there is indeed some embarrassment. I'll take a step-by-step implementation of a custom clock to explain this problem. ( Note: I am not very able to make GIF pictures, to film can not be dynamically displayed, want to see the dynamic effect, please go to the end of the blog to download the source for viewing )
Error Example:
Here I have an example of "error". The mistake here should actually be to double quotation marks, because it is not really wrong, just at some point, it is inappropriate. Let's take a look at this example and see what's not suitable for this example.
Show:
Looking at the two runs above, we can see two running graphs that are normal, but this is not the whole picture. The error message is displayed and analyzed below. Here I'll explain why this example is not all wrong, just an inappropriate reason. Because if our demand is not a change of graphics, such as some polygon display, etc., do not need to refresh the interface in real time, OK, this example does not have any problems, and easy to use. For this, I think it is also necessary to enclose the code to demonstrate the implementation process.
static drawing code:
public class Customcanvasview extends View {private static final String TAG = CustomCanvasView.class.getName (); private paint paint; private int Mradius; Private Canvas Mcanvas; private int mhours; private int mminutes; private int mseconds; Private Thread Mthread; Public Customcanvasview (context context, int radius) {super (context); Paint = new paint (); Paint.setcolor (color.red); Paint.setstrokejoin (Paint.Join.ROUND); Paint.setstrokecap (Paint.Cap.ROUND); Paint.setstrokewidth (5); Mradius = radius; }//Here we will test the drawing method provided by the canvas @Override protected void OnDraw (canvas canvas) {Mcanvas = canvas; Drawcompass (Mcanvas); Refreshclock (); } private void Refreshclock () {mthread = new Thread () {@Override public void run () { try {while (true) {Handler.sendEmptymessage (0x123); Sleep (1000); }} catch (Interruptedexception e) {e.printstacktrace (); } } }; Mthread.start (); } Handler Handler = new Handler () {public void Handlemessage (Message msg) {Calendar c = Calenda R.getinstance (); Mhours = C.gettime (). GetHours (); Mminutes = C.gettime (). getminutes (); Mseconds = C.gettime (). getseconds (); Invalidate (); c = null; }; }; /** * Draw Compass * 2015-2-3 * */private void Drawcompass (canvas canvas) {Paint.setantialias (true); Paint.setstyle (Style.stroke); Canvas.translate (Canvas.getwidth ()/2, Mradius + 300); Pan Compass canvas.drawcircle (0, 0, Mradius, paint); Draw a circle//use path to draw the route text Canvas.save (); Drawlabel (canvas); Canvas.restore (); Drawdividing (canvas); Drawminutehand (canvas, 0); canvas = null; /** * Draw the label text inside the compass * 2015-2-4 */private void Drawlabel (canvas canvas) {canvas.translate (-1 55,-155); Path PATH = new Path (); Path.addarc (New RECTF (0, 0, Mradius + 180, Mradius + 100), 180,); Paint Citepaint = new paint (paint); Citepaint.settextsize (30); Citepaint.setstrokewidth (1); Canvas.drawtextonpath ("Http://blog.csdn.net/lemon_tree", path, 0, citepaint); Path = NULL; Citepaint = null; canvas = null; }/** * Draw ticks * 2015-2-4 * */private void drawdividing (canvas canvas) {Paint Divdpaint = new P Aint (paint); Small scale Brush Object Divdpaint.setstrokewidth (1); Divdpaint.settextsize (20); Float y = Mradius; int count = 60; Total number of ticks canvas.rotate (* 360/count, 0f, 0f); for (int i = 0; i < count; i++) { if (i% 5 = = 0) {canvas.drawline (0f, y, 0, y + 20f, paint); Canvas.drawtext (string.valueof (I/5 + 1), -4f, y + 55f, divdpaint); } else {canvas.drawline (0f, y, 0f, y + 15f, divdpaint); } canvas.rotate (360/count, 0f, 0f); Rotate the drawing paper} divdpaint = null; canvas = null; /** * Draw minute hand * 2015-2-4 */private void Drawminutehand (canvas canvas, int second) {Paint TMP Paint = new paint (paint); Tmppaint.setstrokewidth (2); Tmppaint.settextsize (30); Tmppaint.setcolor (Color.gray); Tmppaint.setstrokewidth (4); Canvas.drawcircle (0, 0, ten, tmppaint); Tmppaint.setstyle (Style.fill); Tmppaint.setcolor (Color.yellow); Canvas.drawcircle (0, 0, 5, tmppaint); Canvas.rotate (Mseconds * 6, 0f, 0f); Canvas.drawline (0, 0, -135, paint); Tmppaint = null; Canvas = null; }}
error log display and cause analysis:
It's not a big problem to look at the two graphs above, but if you download my source code and run it, you may find that the program hangs when your pointer walks for about 20 seconds. When you view the log, you will find the following error message:
Is there a feeling that there is a damn oom problem, and honestly I feel the same way. This may be because invalidate () did not clean up the recycling of resources, and the custom control here is inherited view, without the use of double buffering technology, causing the program to crash. and the resource recycling here I did some work, but the problem still exists. So I started looking for another way to solve the problem--surfaceview.
-----------------------------------------Split-------------------------------------------
Correct Example:Leading knowledge learning-dirty rectangles:
The so-called dirty rectangle refreshes, meaning that only the newly changed parts of the rectangular region, and other useless parts will not be refreshed, so as to reduce the waste of resources. We can declare which part of the canvas we need by assigning a parameter to the canvas when we get it, so that we can just get control of that section. (Reference from: http://www.linuxidc.com/Linux/2012-02/54367. HTM) In this example, a global refresh is used.
Leading Knowledge learning- double buffering:
On the concept of double buffering, here is a reference to the Baidu Encyclopedia (click to enter).
If I have to say it in layman's terms, I think it should look like this: there is a darkroom with a deep-rooted painter who is responsible for drawing pictures. The darkroom provides a small window, which is used to show the pictures painted by the painter. There is also a painter's assistant in the darkroom, who is responsible for showing the artist's drawings at a certain speed in this small window (a certain speed on this side must be slower than the painter's painting).
Example Demonstration:Run Show:
See above the run is not feeling very chord? When I write it, I feel much better than the picture. Let's take a moment to learn the process of implementing it.
the first thing to do
1.extends Surfaceview
2.implements Surfaceholder.callback
3. Customizing a thread
Step Two: logical function implementation
Based on the last inappropriate version, here are some references to the above logical functions.
Draw the second hand:
/** * Draw the second hand * 2015-2-4 * * /private void Drawsecondhand (canvas canvas) { Paint handpaint = new Paint ( Mpaint); Handpaint.setstrokewidth (2); Handpaint.setstyle (Style.fill); int angle = (mseconds + 25) * 6; Calculated angle canvas.rotate (angle, 0f, 0f); Canvas.drawline (0, 0, -135, mpaint); }
draw the minute hand:
/** * Draw the minute hand * 2015-2-4 * * /private void Drawminutehand (canvas canvas) { Paint handpaint = new Paint ( Mpaint); Handpaint.setstrokewidth (2); Handpaint.setstyle (Style.fill); Canvas.save (); int angle = (mminutes + 25) * 6; Calculated angle canvas.rotate (angle, 0f, 0f); Canvas.drawline (0, 0, -110, mpaint); Canvas.restore (); }
The number of lines from the second hand to the minute hand code is significantly more, and how does this extra line of code work?
When we draw the minute hand we can see the phrase: canvas.rotate (angle, 0f, 0f); it's the function of rotating the canvas angle degrees, and if we don't make a state save on the canvas when we draw the minute hand, then the next time we draw the hour, it will be the logic of the rotation. , in order to avoid these unnecessary troubles, we need to restore the its first after saving.
draw the hour hand:
/** * Draw the hour hand * 2015-2-4 * /private void Drawhourhand (canvas canvas) { Paint handpaint = new Paint ( Mpaint); Handpaint.setstyle (Style.fill); Handpaint.setstrokewidth (8); Canvas.save (); int angle = (((mhours%) * 5 +) * 6) + (Mminutes * 6 * 5/60); Calculated angle canvas.rotate (angle, 0f, 0f); Canvas.drawline (0, 0, -90, handpaint); Canvas.restore (); }
The hour hand drawing is almost identical to the minute hand, and the only thing to note is the calculation of the angle when the hour is drawn. If you only calculate by the number of hours, it will always point to the large scale. Never point to the part between two large ticks, in order to solve this problem, we need to add a number of minutes together to calculate. That is, the hour of n minutes and how many angles are shifted.
Custom Thread
The use of Surfaceview requires a mechanism for locking. That is to say, I do not allow to be disturbed when drawing, there is an exclusive concept. This can be done with the following code:
class Drawthread extends Thread {private Surfaceholder holder; public Boolean Isrun; Public Drawthread (Surfaceholder holder) {this.holder = holder; Isrun = true; } @Override public void Run () {while (isrun) {canvas canvas = null; try {synchronized (holder) {canvas = Holder.lockcanvas (NULL); Canvas.drawcolor (Color.Black); Drawclock (canvas); Holder.unlockcanvasandpost (canvas); Unlock the canvas and submit the picture Thread.Sleep (1000); }} catch (Interruptedexception e) {e.printstacktrace (); } } } }
You can see that after I have finished the lock, there is a line of Canvas.drawcolor (Color.Black) on the canvas, and the code operation. I think you should understand why. Right! is clear screen! Without this code, the last drawing was not cleared, which made the entire interface feel messy. Let's take a look at this. Canvas is dynamically drawn in the absence of a clear screen and with only one pointer.
You can clearly see the inside of the clock on the line label, the white part of the 1.1 point to deepen, which is strongly explained because the last graphic residue caused.
Well, using Surfaceview and Canvas to learn about custom clocks is here, and if you still have some things you don't quite understand, welcome to my previous blog, "Android custom Controls Leading Basics Learning (i)--canvas" Learning , or to communicate with me in the form of comments.
Advanced Android UI Programming-use SURFACEVIEWT and canvas for dynamic clocks