The Pizza Timer - Threading/Drawing on Canvas

Tutorials with advanced 'difficulty' and more Lines of Code.

The Pizza Timer - Threading/Drawing on Canvas

Postby plusminus » Wed Nov 28, 2007 2:51 am

The Image Timer - Threading/Drawing on Canvas


What is this: Having determined a huge market niche I decided to make the Pizza-Timer Killer-Application every one (or man) needs to survive :!:

What this tutorial includes:
  • Drawing on canvas'. Especially and Text.
  • Threading (continuously update the GUI/PizzaCountdown)
  • Correct Updating of the GUI/Views using a Handler
  • Notifications
See also: ListActivity - Functionality

:?: Problems/Questions: post right below...

Difficulty: 2 of 5

What it will look like:
(During Cuntdown)
Image

(Cuntdown Finished)
Image

(Notification even when PizzaTimer was 'sent to background' (,BUT not yet killed :!:)
Image


Description:
To accomplish our goal of creating the PizzaTimer we have to do the following things:
  • Design our own Pizza-View, that has a pizza-picture as background and can draw Arcs and the time on its canvas.
  • Creare a Timer-Thread, that 'ticks' every second.
  • Update the PizzaView at every tick of the Timer-Thread
  • Do some start/stop/settings stuff

0:So lets start with the PizzaView which is capable of displaying a Pizza as it background and do some painting on itself (its canvas).

At first, we set up the class-fields of PizzaView.java. Two int-values, one that takes the time, already passed and the other one takes the total time until the pizza is ready to be defeated :roll: . We also create various Paints at the beginning, so we do not have to switch the colors/thickness always later on.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         // The arc-line will be really thick <img src="http://www.anddev.org/images/smilies/icon_exclaim.gif" alt=":!:" title="Exclamation" />
  2.         protected final int ARCSTROKEWIDTH = 20;
  3.  
  4.         // Set startup-values
  5.         protected int mySecondsPassed = 0;
  6.         protected int mySecondsTotal = 0;
  7.        
  8.         // Our Paint-ing-Device (Pen/Pencil/Brush/Whatever...)
  9.         protected final Paint myArcSecondPaint = new Paint();
  10.         protected final Paint myArcMinutePaint = new Paint();
  11.         protected final Paint myCountDownTextPaint = new Paint();
  12.         protected final Paint myPizzaTimeTextPaint = new Paint();
Parsed in 0.033 seconds, using GeSHi 1.0.8.4


1: The Constructor - does nothing, except setting its the background and setting up the Paint's.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         // ===========================================================
  2.         // Constructors
  3.         // ===========================================================
  4.         public PizzaView(Context context) {
  5.                 super(context);
  6.                 this.setBackground(getResources().getDrawable(R.drawable.pizza));
  7.                
  8.                 // Black text for the countdown
  9.                 this.myCountDownTextPaint.setARGB(150, 0, 0, 0);
  10.                 this.myCountDownTextPaint.setTextSize(110);
  11.                 this.myCountDownTextPaint.setFakeBoldText(true);
  12.                
  13.                 // Orange text for the IT PIZZA TIME
  14.                 this.myPizzaTimeTextPaint.setARGB(255, 255, 60, 10);
  15.                 this.myPizzaTimeTextPaint.setTextSize(110);
  16.                 this.myPizzaTimeTextPaint.setFakeBoldText(true);
  17.                
  18.                 // Our arc fill be a lookthrough-red.
  19.                 this.myArcMinutePaint.setARGB(150, 170, 0, 0);
  20.                 this.myArcMinutePaint.setAntiAlias(true);
  21.                 this.myArcMinutePaint.setStyle(Style.STROKE);
  22.                 this.myArcMinutePaint.setStrokeWidth(ARCSTROKEWIDTH);
  23.                
  24.                 this.myArcSecondPaint.setARGB(200, 255, 130, 20);
  25.                 this.myArcSecondPaint.setAntiAlias(true);
  26.                 this.myArcSecondPaint.setStyle(Style.STROKE);
  27.                 this.myArcSecondPaint.setStrokeWidth(ARCSTROKEWIDTH / 3);
  28.         }
Parsed in 0.034 seconds, using GeSHi 1.0.8.4


2:
We also create two setters for the mySecondsXYZ-fields, to modify them during runtime.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         // ===========================================================
  2.         // Getter & Setter
  3.         // ===========================================================
  4.        
  5.         public void updateSecondsPassed(int someSeconds){
  6.                 this.mySecondsPassed = someSeconds;
  7.         }
  8.        
  9.         public void updateSecondsTotal(int totalSeconds){
  10.                 this.mySecondsTotal = totalSeconds;
  11.         }
Parsed in 0.035 seconds, using GeSHi 1.0.8.4

3:The last thing to do in our PizzaView.java is obviously the onDraw(...)-Method, as we want to display a count-down. This is basically just some maths, like determining the percentage done and calculating that value against the 360°. It is basically like any ordinary analog clock. We also put the number of the minutes (if timeleft >= 60) or the seconds remaining (if timeleft < 60) to the middle of the screen.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         @Override
  2.         protected void onDraw(Canvas canvas) {
  3.                 /* Calculate the time left,
  4.                  * until our pizza is finished. */
  5.                 int secondsLeft = this.mySecondsTotal - this.mySecondsPassed;
  6.                
  7.                 // Check if pizza is already done
  8.                 if(secondsLeft <= 0){
  9.                         /* Draw the "! PIZZA !"-String
  10.                          *  to the middle of the screen */
  11.                         String itIsPizzaTime = getResources().getString(
  12.                                                 R.string.pizza_countdown_end);
  13.                         canvas.drawText(itIsPizzaTime,
  14.                                                                 10, (this.getHeight() / 2) + 30,
  15.                                                                 this.myPizzaTimeTextPaint);                    
  16.                 }else{
  17.                         // At least one second left
  18.                         float angleAmountMinutes = ((this.mySecondsPassed * 1.0f)
  19.                                                                                         / this.mySecondsTotal)
  20.                                                                                         * 360;
  21.                         float angleAmountSeconds = ((60 -secondsLeft % 60) * 1.0f)
  22.                                                                                         / 60
  23.                                                                                         * 360;
  24.        
  25.                         /* Calculate an Rectangle,
  26.                          * with some spacing to the edges */
  27.                         RectF arcRect = new RectF(ARCSTROKEWIDTH / 2,
  28.                                                                 ARCSTROKEWIDTH / 2,
  29.                                                                 this.getWidth() - ARCSTROKEWIDTH / 2,
  30.                                                                 this.getHeight() - ARCSTROKEWIDTH / 2);
  31.  
  32.                         // Draw the Minutes-Arc into that rectangle            
  33.                         canvas.drawArc(arcRect, -90, angleAmountMinutes,
  34.                                                                                         this.myArcMinutePaint);
  35.                        
  36.                         // Draw the Seconds-Arc into that rectangle    
  37.                         canvas.drawArc(arcRect, -90, angleAmountSeconds,
  38.                                                                                         this.myArcSecondPaint);
  39.                        
  40.                         String timeDisplayString;
  41.                         if(secondsLeft > 60) // Show minutes
  42.                                 timeDisplayString = "" + (secondsLeft / 60);
  43.                         else // Show seconds when less than a minute
  44.                                 timeDisplayString = "" + secondsLeft;
  45.                        
  46.                         // Draw the remaining time.
  47.                         canvas.drawText(timeDisplayString,
  48.                                         this.getWidth() / 2 - (30 * timeDisplayString.length()),
  49.                                         this.getHeight()/ 2 + 30,
  50.                                         this.myCountDownTextPaint);
  51.                 }
  52.         }
Parsed in 0.041 seconds, using GeSHi 1.0.8.4

The PizzaView.java is now completed :!:
4:Lets get into the "time-managment logic" in the file "PizzaTimer.java".

Clearly the GUI needs to be update constantly(every second), as we want to see the time tick down :!:
The way this done is not that ... "self understanding". I'll explain it a bit first and then everything should become clear with the picture below :!:
:idea: The "problem" is, that for security reasons, only the Thread that created the View is allowed to do sth. with it, like redrawing it with an invalidate();-call. So we need an Object 'in' the GUI-Thread that receives messages that mean like: "Hey 'Main-Thread' update your gui!". We will use a so called Handler for that.

  • There will be a Thread, that sends a message that means "UPDATEYOURGUI" to the Handler.
  • In the GUI-Thread, when the Handler receives the Message("UPDATEYOURGUI") it causes an invalidate(); to all participating Views.
Image
:arrow: If this is still not clear, feel free to ask your question :!:


5: Our actual PizzaTimer-Application does not contain much more than the things in the Picture :!:

Lets first take a look at all the fields of PizzaTimer.java:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         protected static final int DEFAULTSECONDS = 60 * 12;  // 12 MInutes
  2.         /* The value of these IDs is random!
  3.          * they are just needed to be recognized */
  4.         protected static final int SECONDPASSEDIDENTIFIER = 0x1337;
  5.         protected static final int GUIUPDATEIDENTIFIER = 0x101;
  6.         protected static final int PIZZA_NOTIFICATION_ID = 0x1991;
  7.        
  8.         /** is the countdown running at the moment ?*/
  9.         protected boolean running = false;
  10.         /** Seconds passed so far */
  11.         protected int mySecondsPassed = 0;
  12.         /** Seconds to be passed totally */
  13.         protected int mySecondsTotal = DEFAULTSECONDS;
  14.        
  15.         /* Thread that sends a message
  16.          * to the handler every second */
  17.         Thread myRefreshThread = null;
  18.         // One View is all that we see.
  19.         PizzaView myPizzaView = null;
Parsed in 0.038 seconds, using GeSHi 1.0.8.4


6: As mentioned above, we need the Handler-Object (which is also a field of our PizzaTimer.java) :

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         /* The Handler that receives the messages
  2.          * sent out by myRefreshThread every second */
  3.         Handler myPizzaViewUpdateHandler = new Handler(){
  4.                 /** Gets called on every message that is received */
  5.                 // @Override
  6.                 public void handleMessage(Message msg) {
  7.                         switch (msg.what) {
  8.                                 case PizzaTimer.SECONDPASSEDIDENTIFIER:
  9.                                         // We identified the Message by its What-ID
  10.                                         if (running) {
  11.                                                 // One second has passed
  12.                                                 mySecondsPassed++;
  13.                                                 if(mySecondsPassed == mySecondsTotal){
  14.                                                 // Time is finished, lets display a notification!
  15.                                         // Get the notification manager serivce.
  16.                                         NotificationManager nm = (NotificationManager)
  17.                                                 getSystemService(NOTIFICATION_SERVICE);
  18.  
  19.                                         /* The id we use here happens to be the
  20.                                          * id of the text we display. You can use
  21.                                          * any int here that is unique within
  22.                                          * your application. */
  23.                                         nm.notifyWithText(PIZZA_NOTIFICATION_ID,
  24.                                                 getText(R.string.pizza_notification_text),
  25.                                                 NotificationManager.LENGTH_LONG, null);
  26.                                                 }
  27.                                         }
  28.                                         // No break here --> runs into the next case
  29.                                 case PizzaTimer.GUIUPDATEIDENTIFIER:
  30.                                         // Redraw our Pizza !!
  31.                                         myPizzaView.updateSecondsPassed(mySecondsPassed);
  32.                                         myPizzaView.updateSecondsTotal(mySecondsTotal);
  33.                                         myPizzaView.invalidate();
  34.                                         break;
  35.                         }
  36.                         super.handleMessage(msg);
  37.                 }
  38.         };
Parsed in 0.040 seconds, using GeSHi 1.0.8.4


7: Lets take a look at the onCreate-Activity.
It creates a new PizzaView and registers it as what we want to see.
It also creates a new Thread (secondCountDownRunner), that will send a message to the Handler we created above.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.     /** Called when the activity is first created. */
  2.     @Override
  3.     public void onCreate(Bundle icicle) {
  4.         super.onCreate(icicle);
  5.                
  6.         this.myPizzaView = new PizzaView(this);
  7.         this.myPizzaView.updateSecondsTotal(PizzaTimer.DEFAULTSECONDS);
  8.         setContentView(this.myPizzaView);
  9.        
  10.         this.myRefreshThread = new Thread(new secondCountDownRunner());
  11.         this.myRefreshThread.start();
  12.     }
Parsed in 0.037 seconds, using GeSHi 1.0.8.4


8: The Runnable we used to create this.myRefreshThread:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         class secondCountDownRunner implements Runnable{
  2.                 // @Override
  3.                 public void run() {
  4.                         while(!Thread.currentThread().isInterrupted()){
  5.                                 Message m = new Message();
  6.                                 m.what = PizzaTimer.SECONDPASSEDIDENTIFIER;
  7.                                 PizzaTimer.this.myPizzaViewUpdateHandler.sendMessage(m);
  8.                                 try {
  9.                                         Thread.sleep(1000);
  10.                                 } catch (InterruptedException e) {
  11.                                         Thread.currentThread().interrupt();
  12.                                 }
  13.                         }
  14.                 }
  15.     }
Parsed in 0.038 seconds, using GeSHi 1.0.8.4


9: And thats it, what is left are only a small menu, to reset the counter and the reactions on some Buttons (here the whole KeyPad):
9.1: The menu:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.     @Override
  2.         public boolean onCreateOptionsMenu(Menu menu) {
  3.                 menu.add(0,0,getResources().getString(R.string.menu_reset));
  4.                 return super.onCreateOptionsMenu(menu);
  5.         }
  6.  
  7.         @Override
  8.         public boolean onMenuItemSelected(int featureId, Item item) {
  9.                 switch(item.getId()){
  10.                         case 0:
  11.                                 // Reset the counter and stop it
  12.                                 this.mySecondsTotal = PizzaTimer.DEFAULTSECONDS;
  13.                                 this.mySecondsPassed = 0;
  14.                                 this.running = false;
  15.                                 return true;
  16.                 }
  17.                 return super.onMenuItemSelected(featureId, item);
  18.         }
Parsed in 0.038 seconds, using GeSHi 1.0.8.4

9.2: ...and the KeyPad-Reactions:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         @Override
  2.         public boolean onKeyDown(int keyCode, KeyEvent event) {
  3.                 Message m = new Message();
  4.                 m.what = PizzaTimer.GUIUPDATEIDENTIFIER;
  5.                
  6.                 switch(keyCode){
  7.                         case KeyEvent.KEYCODE_DPAD_UP:
  8.                                 this.mySecondsTotal += 60; // One minute later
  9.                                 break;
  10.                         case KeyEvent.KEYCODE_DPAD_DOWN:
  11.                                 this.mySecondsTotal -= 60; // One minute earlier
  12.                                 break;
  13.                         case KeyEvent.KEYCODE_DPAD_CENTER:
  14.                                 this.running = !this.running; // START / PAUSE
  15.                                 break;
  16.                         case KeyEvent.KEYCODE_DPAD_LEFT:
  17.                                 this.mySecondsTotal += 1; // One second later
  18.                                 break;
  19.                         case KeyEvent.KEYCODE_DPAD_RIGHT:
  20.                                 this.mySecondsTotal -= 1; // One second earlier
  21.                                 break;
  22.                         default:
  23.                                 return super.onKeyDown(keyCode, event);
  24.                 }
  25.                
  26.                 this.myPizzaViewUpdateHandler.sendMessage(m);
  27.                 return true;
  28.         }
Parsed in 0.040 seconds, using GeSHi 1.0.8.4


So thats it, we are done :!:

The Full Source:
Download the background-pizza "/res/drawable"
:arrow: Download

"/src/your_package_structure/PizzaTimer.java"
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.anddev.android.pizzatimer;
  2.  
  3. import android.app.Activity;
  4. import android.app.NotificationManager;
  5. import android.os.Bundle;
  6. import android.os.Handler;
  7. import android.os.Message;
  8. import android.view.KeyEvent;
  9. import android.view.Menu;
  10. import android.view.Menu.Item;
  11.  
  12. public class PizzaTimer extends Activity {
  13.  
  14.         protected static final int DEFAULTSECONDS = 60 * 12;  // 12 MInutes
  15.         /* The value of these IDs is random!
  16.          * they are just needed to be recognized */
  17.         protected static final int SECONDPASSEDIDENTIFIER = 0x1337;
  18.         protected static final int GUIUPDATEIDENTIFIER = 0x101;
  19.         protected static final int PIZZA_NOTIFICATION_ID = 0x1991;
  20.        
  21.         /** is the countdown running at the moment ?*/
  22.         protected boolean running = false;
  23.         /** Seconds passed so far */
  24.         protected int mySecondsPassed = 0;
  25.         /** Seconds to be passed totally */
  26.         protected int mySecondsTotal = DEFAULTSECONDS;
  27.        
  28.         /* Thread that sends a message
  29.          * to the handler every second */
  30.         Thread myRefreshThread = null;
  31.         // One View is all that we see.
  32.         PizzaView myPizzaView = null;
  33.        
  34.         /* The Handler that receives the messages
  35.          * sent out by myRefreshThread every second */
  36.         Handler myPizzaViewUpdateHandler = new Handler(){
  37.                 /** Gets called on every message that is received */
  38.                 // @Override
  39.                 public void handleMessage(Message msg) {
  40.                         switch (msg.what) {
  41.                                 case PizzaTimer.SECONDPASSEDIDENTIFIER:
  42.                                         // We identified the Message by its What-ID
  43.                                         if (running) {
  44.                                                 // One second has passed
  45.                                                 mySecondsPassed++;
  46.                                                 if(mySecondsPassed == mySecondsTotal){
  47.                                                         // Time is finished, lets display a notification!
  48.                                         // Get the notification manager serivce.
  49.                                         NotificationManager nm = (NotificationManager)
  50.                                                 getSystemService(NOTIFICATION_SERVICE);
  51.  
  52.                                         /* The id we use here happens to be the
  53.                                          * id of the text we display. You can use
  54.                                          * any int here that is unique within
  55.                                          * your application. */
  56.                                         nm.notifyWithText(PIZZA_NOTIFICATION_ID,
  57.                                                 getText(R.string.pizza_notification_text),
  58.                                                 NotificationManager.LENGTH_LONG, null);
  59.                                                 }
  60.                                         }
  61.                                         // No break here --> runs into the next case
  62.                                 case PizzaTimer.GUIUPDATEIDENTIFIER:
  63.                                         // Redraw our Pizza !!
  64.                                         myPizzaView.updateSecondsPassed(mySecondsPassed);
  65.                                         myPizzaView.updateSecondsTotal(mySecondsTotal);
  66.                                         myPizzaView.invalidate();
  67.                                         break;
  68.                         }
  69.                         super.handleMessage(msg);
  70.                 }
  71.         };
  72.        
  73.     /** Called when the activity is first created. */
  74.     @Override
  75.     public void onCreate(Bundle icicle) {
  76.         super.onCreate(icicle);
  77.                
  78.         this.myPizzaView = new PizzaView(this);
  79.         this.myPizzaView.updateSecondsTotal(PizzaTimer.DEFAULTSECONDS);
  80.         setContentView(this.myPizzaView);
  81.        
  82.         this.myRefreshThread = new Thread(new secondCountDownRunner());
  83.         this.myRefreshThread.start();
  84.     }
  85.    
  86.     @Override
  87.         public boolean onCreateOptionsMenu(Menu menu) {
  88.                 menu.add(0,0,getResources().getString(R.string.menu_reset));
  89.                 return super.onCreateOptionsMenu(menu);
  90.         }
  91.  
  92.         @Override
  93.         public boolean onMenuItemSelected(int featureId, Item item) {
  94.                 switch(item.getId()){
  95.                         case 0:
  96.                                 // Reset the counter and stop it
  97.                                 this.mySecondsTotal = PizzaTimer.DEFAULTSECONDS;
  98.                                 this.mySecondsPassed = 0;
  99.                                 this.running = false;
  100.                                 return true;
  101.                 }
  102.                 return super.onMenuItemSelected(featureId, item);
  103.         }
  104.  
  105.         @Override
  106.         public boolean onKeyDown(int keyCode, KeyEvent event) {
  107.                 Message m = new Message();
  108.                 m.what = PizzaTimer.GUIUPDATEIDENTIFIER;
  109.                
  110.                 switch(keyCode){
  111.                         case KeyEvent.KEYCODE_DPAD_UP:
  112.                                 this.mySecondsTotal += 60; // One minute later
  113.                                 break;
  114.                         case KeyEvent.KEYCODE_DPAD_DOWN:
  115.                                 this.mySecondsTotal -= 60; // One minute earlier
  116.                                 break;
  117.                         case KeyEvent.KEYCODE_DPAD_CENTER:
  118.                                 this.running = !this.running; // START / PAUSE
  119.                                 break;
  120.                         case KeyEvent.KEYCODE_DPAD_LEFT:
  121.                                 this.mySecondsTotal += 1; // One second later
  122.                                 break;
  123.                         case KeyEvent.KEYCODE_DPAD_RIGHT:
  124.                                 this.mySecondsTotal -= 1; // One second earlier
  125.                                 break;
  126.                         default:
  127.                                 return super.onKeyDown(keyCode, event);
  128.                 }
  129.                
  130.                 this.myPizzaViewUpdateHandler.sendMessage(m);
  131.                 return true;
  132.         }
  133.    
  134.         class secondCountDownRunner implements Runnable{
  135.                 // @Override
  136.                 public void run() {
  137.                         while(!Thread.currentThread().isInterrupted()){
  138.                                 Message m = new Message();
  139.                                 m.what = PizzaTimer.SECONDPASSEDIDENTIFIER;
  140.                                 PizzaTimer.this.myPizzaViewUpdateHandler.sendMessage(m);
  141.                                 try {
  142.                                         Thread.sleep(1000);
  143.                                 } catch (InterruptedException e) {
  144.                                         Thread.currentThread().interrupt();
  145.                                 }
  146.                         }
  147.                 }
  148.     }
  149. }
Parsed in 0.055 seconds, using GeSHi 1.0.8.4



"/src/your_package_structure/PizzaView.java"
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. // Created by plusminus on 23:08:24 - 27.11.2007
  2. package org.anddev.android.pizzatimer;
  3.  
  4. import android.content.Context;
  5. import android.graphics.Canvas;
  6. import android.graphics.Paint;
  7. import android.graphics.RectF;
  8. import android.graphics.Paint.Style;
  9. import android.view.View;
  10.  
  11.  
  12. public class PizzaView extends View{
  13.         // ===========================================================
  14.         // Fields
  15.         // ===========================================================
  16.        
  17.         protected final int ARCSTROKEWIDTH = 20;
  18.        
  19.         // Set startup-values
  20.         protected int mySecondsPassed = 0;
  21.         protected int mySecondsTotal = 0;
  22.        
  23.         // Our Painting-Device (Pen/Pencil/Brush/Whatever...)
  24.         protected final Paint myArcSecondPaint = new Paint();
  25.         protected final Paint myArcMinutePaint = new Paint();
  26.         protected final Paint myCountDownTextPaint = new Paint();
  27.         protected final Paint myPizzaTimeTextPaint = new Paint();
  28.  
  29.         // ===========================================================
  30.         // Constructors
  31.         // ===========================================================
  32.  
  33.         public PizzaView(Context context) {
  34.                 super(context);
  35.                 this.setBackground(getResources().getDrawable(R.drawable.pizza));
  36.                
  37.                 // Black text for the countdown
  38.                 this.myCountDownTextPaint.setARGB(150, 0, 0, 0);
  39.                 this.myCountDownTextPaint.setTextSize(110);
  40.                 this.myCountDownTextPaint.setFakeBoldText(true);
  41.                
  42.                 // Orange text for the IT PIZZA TIME
  43.                 this.myPizzaTimeTextPaint.setARGB(255, 255, 60, 10);
  44.                 this.myPizzaTimeTextPaint.setTextSize(110);
  45.                 this.myPizzaTimeTextPaint.setFakeBoldText(true);
  46.                
  47.                 // Our minute-arc-paint fill be a lookthrough-red.
  48.                 this.myArcMinutePaint.setARGB(150, 170, 0, 0);
  49.                 this.myArcMinutePaint.setAntiAlias(true);
  50.                 this.myArcMinutePaint.setStyle(Style.STROKE);
  51.                 this.myArcMinutePaint.setStrokeWidth(ARCSTROKEWIDTH);
  52.  
  53.                 // Our minute-arc-paint fill be a less lookthrough-orange.
  54.                 this.myArcSecondPaint.setARGB(200, 255, 130, 20);
  55.                 this.myArcSecondPaint.setAntiAlias(true);
  56.                 this.myArcSecondPaint.setStyle(Style.STROKE);
  57.                 this.myArcSecondPaint.setStrokeWidth(ARCSTROKEWIDTH / 3);
  58.         }
  59.        
  60.         // ===========================================================
  61.         // onXYZ(...) - Methods
  62.         // ===========================================================
  63.        
  64.         @Override
  65.         protected void onDraw(Canvas canvas) {
  66.                 /* Calculate the time left,
  67.                  * until our pizza is finished. */
  68.                 int secondsLeft = this.mySecondsTotal - this.mySecondsPassed;
  69.                
  70.                 // Check if pizza is already done
  71.                 if(secondsLeft <= 0){
  72.                         /* Draw the "! PIZZA !"-String
  73.                          *  to the middle of the screen */
  74.                         String itIsPizzaTime = getResources().getString(
  75.                                                 R.string.pizza_countdown_end);
  76.                         canvas.drawText(itIsPizzaTime,
  77.                                                                 10, (this.getHeight() / 2) + 30,
  78.                                                                 this.myPizzaTimeTextPaint);                    
  79.                 }else{
  80.                         // At least one second left
  81.                         float angleAmountMinutes = ((this.mySecondsPassed * 1.0f)
  82.                                                                                         / this.mySecondsTotal)
  83.                                                                                         * 360;
  84.                         float angleAmountSeconds = ((60 -secondsLeft % 60) * 1.0f)
  85.                                                                                         / 60
  86.                                                                                         * 360;
  87.        
  88.                         /* Calculate an Rectangle,
  89.                          * with some spacing to the edges */
  90.                         RectF arcRect = new RectF(ARCSTROKEWIDTH / 2,
  91.                                                                 ARCSTROKEWIDTH / 2,
  92.                                                                 this.getWidth() - ARCSTROKEWIDTH / 2,
  93.                                                                 this.getHeight() - ARCSTROKEWIDTH / 2);
  94.  
  95.                         // Draw the Minutes-Arc into that rectangle            
  96.                         canvas.drawArc(arcRect, -90, angleAmountMinutes,
  97.                                                                                         this.myArcMinutePaint);
  98.                        
  99.                         // Draw the Seconds-Arc into that rectangle    
  100.                         canvas.drawArc(arcRect, -90, angleAmountSeconds,
  101.                                                                                         this.myArcSecondPaint);
  102.                        
  103.                         String timeDisplayString;
  104.                         if(secondsLeft > 60) // Show minutes
  105.                                 timeDisplayString = "" + (secondsLeft / 60);
  106.                         else // Show seconds when less than a minute
  107.                                 timeDisplayString = "" + secondsLeft;
  108.                        
  109.                         // Draw the remaining time.
  110.                         canvas.drawText(timeDisplayString,
  111.                                         this.getWidth() / 2 - (30 * timeDisplayString.length()),
  112.                                         this.getHeight()/ 2 + 30,
  113.                                         this.myCountDownTextPaint);
  114.                 }
  115.         }
  116.         // ===========================================================
  117.         // Getter & Setter
  118.         // ===========================================================
  119.        
  120.         public void updateSecondsPassed(int someSeconds){
  121.                 this.mySecondsPassed = someSeconds;
  122.         }
  123.        
  124.         public void updateSecondsTotal(int totalSeconds){
  125.                 this.mySecondsTotal = totalSeconds;
  126.         }
  127. }
Parsed in 0.068 seconds, using GeSHi 1.0.8.4


Feel free to ask any question that comes to your mind :!:

Regards,
plusminus
Last edited by plusminus on Tue Jan 08, 2008 3:14 pm, edited 8 times in total.
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Top

Postby rtreffer » Wed Nov 28, 2007 11:57 pm

Typo, "It's pizza time".
Paint supports alignment - Paint.Align.RIGHT - which might make your app easier to understand and better to read :)

Code: Select all
      lenPaint.setTextSize(10);
      lenPaint.setAntiAlias(true);
      lenPaint.setColor(Color.WHITE);
      lenPaint.setTextAlign(Paint.Align.RIGHT);


Anyway, great tutorial :)
root@localhost# : ( ) { : | : & } ; :
rtreffer
Junior Developer
Junior Developer
 
Posts: 15
Joined: Fri Nov 23, 2007 4:14 pm

Postby lemonhead » Tue Dec 04, 2007 9:58 am

i like pizza! :D

but, when the day comes when u make a tutorial with 5 stars difficulty i will give away free beer! :o
lemonhead
Junior Developer
Junior Developer
 
Posts: 11
Joined: Thu Nov 15, 2007 5:16 pm
Location: Brazil (temporarily)

Postby plusminus » Tue Dec 04, 2007 3:26 pm

:lol: it's pretty hard to say how difficult the tutorials are ... :?

If you'd like to get their rank 'adjusted' let me know =)
Btw: it is at least in the Advanced Section ;)

Regards,
plusminus
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Postby evoc » Tue Dec 04, 2007 9:29 pm

really nice application :)
evoc
Freshman
Freshman
 
Posts: 4
Joined: Fri Nov 30, 2007 12:47 pm

Postby polarbear » Sat Dec 08, 2007 5:18 am

Thank you for all the great work. Anyway, I have a bunch errors on this particular tutorial and I'm stuck. Could you do what you did before by posting all the code following the format "/src/your_package_structure/PizzaView.java"? Thanks again.
polarbear
Junior Developer
Junior Developer
 
Posts: 14
Joined: Mon Dec 03, 2007 4:47 am

Top

Postby plusminus » Sat Dec 08, 2007 12:28 pm

Hello polarbear,

I appended the full sources.
Do you guys prefer the full sources written at the end of each tutorial or as an attachment :?: :?: :?:

Regard,
plusminus
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Postby venkat » Sat Dec 08, 2007 2:02 pm

Thank u very much..... :)
venkat
Senior Developer
Senior Developer
 
Posts: 152
Joined: Tue Nov 27, 2007 5:42 am
Location: India

Postby polarbear » Sun Dec 09, 2007 5:01 pm

Hello Plusminus:

Thank you and, yes, to appending the source code. It makes the learning process easier
polarbear
Junior Developer
Junior Developer
 
Posts: 14
Joined: Mon Dec 03, 2007 4:47 am

Yes, please include the full source of the tutorials

Postby jdl » Thu Jan 03, 2008 1:02 am

plusminus wrote:Hello polarbear,

I appended the full sources.
Do you guys prefer the full sources written at the end of each tutorial or as an attachment :?: :?: :?:

Regard,
plusminus


I prefer to download the full source as an attachment. The tutorials are very helpful!

Thanks,

jdl
jdl
Freshman
Freshman
 
Posts: 4
Joined: Thu Jan 03, 2008 12:58 am
Location: USA

hello

Postby jive » Tue Jan 08, 2008 8:43 am

hello, thanks for this tutorial, very helpful.

but i have a problem, eclipse won't let me compile the source as you gave it, it has a problem on this line :
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. class secondCountDownRunner implements Runnable{
  3.  
  4.           @Override
  5.  
  6.           public void run() {              // <---- problem here
  7.  
  8.                while(!Thread.currentThread().isInterrupted()){
  9.  
  10.                     Message m = new Message();
  11.  
  12.                     m.what = PizzaTimer.SECONDPASSEDIDENTIFIER;
  13.  
  14.                     PizzaTimer.this.myPizzaViewUpdateHandler.sendMessage(m);
  15.  
  16.                     try {
  17.  
  18.                          Thread.sleep(1000);
  19.  
  20.                     } catch (InterruptedException e) {
  21.  
  22.                          Thread.currentThread().interrupt();
  23.  
  24.                     }
  25.  
  26.                }
  27.  
  28.           }
  29.  
  30.     }
  31.  
  32.  
Parsed in 0.039 seconds, using GeSHi 1.0.8.4


says i must override a superclass method.

any idea what i am doing wrong ?

thanks
jive
Freshman
Freshman
 
Posts: 3
Joined: Tue Jan 08, 2008 8:38 am

Postby venkat » Tue Jan 08, 2008 10:44 am

Hi jive ,
Can you remove the line @Override , Above Run method.

Regards,
Venkat.
Regards,
Venkat.
venkat
Senior Developer
Senior Developer
 
Posts: 152
Joined: Tue Nov 27, 2007 5:42 am
Location: India

Re: hello

Postby plusminus » Tue Jan 08, 2008 3:12 pm

Hello Jive,

as venkat answered, removing the @Override-Annotation is the solution to your problem.
This happens because the JDK 1.5, you are probably running, does accept @Override-Annotation only for overridden methods of Super-Classes and not for overriding functions of Interfaces we are implementing here.

On JDK 1.6 this Annotation is possible. :roll:

So this is a kind of style-issue.

Regards,
plusminus

Btw: Commented that @Override-Annotation away :)
Image
Image | Android Development Community / Tutorials
User avatar
plusminus
Site Admin
Site Admin
 
Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Re: The Pizza Timer - Threading/Drawing on Canvas

Postby StGabe » Thu Jan 17, 2008 8:50 pm

Using sleep(1000) as a timer isn't going to give you a very good guarantee of accuracy. That just means the thread will sleep for at least 1000ms but it could well be more and your clock will tend to be slow (depending on how well threads are serviced).

It would be far better to use the system clock (that's what it's there for). To do this you would have your thread regularly polling System.currentTimeMillis(). Also, I would put the thread in PizzaTimer and get rid of the (IMO) clunky message passing. It's less code, it provides a general event loop that could be extended with other features (i.e. an animation) and IMO it's cleaner.
StGabe
Once Poster
Once Poster
 
Posts: 1
Joined: Thu Jan 17, 2008 8:36 pm

Re: The Pizza Timer - Threading/Drawing on Canvas

Postby gustavo » Wed Apr 02, 2008 3:05 am

I don´t understand why the handleMessage sees
msg.what = MeuPizzaTimer.GUIUPDATEIDENTIFIER
as default?
gustavo
Junior Developer
Junior Developer
 
Posts: 12
Joined: Sun Mar 02, 2008 2:55 pm

Top
Next

Return to Advanced Tutorials

Who is online

Users browsing this forum: No registered users and 11 guests