Search code examples
javaandroidandroid-canvascountdowncountdowntimer

Android CountDownTimer and draw on canvas


I am creating Christmas countdown and I need to count down every second and need to draw on canvas how many days, hours, minutes and seconds to Christmas.

I could get the working output, but I feel like there is good way to achieve this than what I do now.

Here is the code, and I removed some unnecessary codes like imports, unimplemented methods etc.

 public class SnowFall extends Activity {

SampleView textDraw;
Paint paint;
Rect bounds;
int durationMilli;
Duration duration;

static String days;
static String hours;
static String minutes;
static String seconds;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Duration duration = new Duration();
    int futureTime = duration.cal_duration();
    MyTime tm = new MyTime(futureTime, 1000);
    tm.start();

    textDraw = new SampleView(this);

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    LinearLayout linearLayout = new LinearLayout(this);
    linearLayout.setOrientation(LinearLayout.VERTICAL);
    linearLayout.setBackgroundColor(Color.TRANSPARENT);
    textDraw.setBackgroundDrawable(getResources().getDrawable(
            R.drawable.background_image_1));
    linearLayout.addView(textDraw);
    setContentView(linearLayout);

}

public class MyTime extends CountDownTimer {

    public MyTime(long millisInFuture, long countDownInterval) {
        super(millisInFuture, countDownInterval);

    }

    @Override
    public void onTick(long millisUntilFinished) {

        SampleView view = new SampleView(getApplicationContext());
        int durationInMilliSec = (int) millisUntilFinished;
        Log.d("Joda Time", String.valueOf(durationInMilliSec));

        try {
            int x = durationInMilliSec / 1000;
            seconds = String.valueOf(x % 60);
            x /= 60;
            minutes = String.valueOf(x % 60);
            x /= 60;
            hours = String.valueOf(x % 24);
            x /= 24;
            days = String.valueOf(x);

            view.invalidate();
        } catch (Exception e) {

        }

    }

}

private static class SampleView extends View {
    private Paint mPaint;
    private float mX;
    private float[] mPos;

    private Path mPath;
    private Paint mPathPaint;

    private static final int DY = 45;
    private static final String POSTEXT = "Positioned";

    private static void makePath(Path p) {
        p.moveTo(10, 0);
        p.cubicTo(100, -50, 200, 50, 300, 0);
    }

    private float[] buildTextPositions(String text, float y, Paint paint) {
        float[] widths = new float[text.length()];
        // initially get the widths for each char
        int n = paint.getTextWidths(text, widths);
        // now popuplate the array, interleaving spaces for the Y values
        float[] pos = new float[n * 2];
        float accumulatedX = 0;
        for (int i = 0; i < n; i++) {
            pos[i * 2 + 0] = accumulatedX;
            pos[i * 2 + 1] = y;
            accumulatedX += widths[i];
        }
        return pos;
    }

    public SampleView(Context context) {
        super(context);
        setFocusable(true);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(40);
        Typeface typeface = Typeface.createFromAsset(context.getAssets(),
                "font.ttf");
        mPaint.setTypeface(typeface);

        mPos = buildTextPositions(POSTEXT, 0, mPaint);

        mPath = new Path();
        makePath(mPath);

        mPathPaint = new Paint();
        mPathPaint.setAntiAlias(true);
        mPathPaint.setColor(0x800000FF);
        mPathPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Paint p = mPaint;
        float x = mX;
        float y = 0;

        if (days != null && hours != null && minutes != null
                && seconds != null) {
            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("There is", x, y, p);

            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText(days + " Days, " + hours + " Hours", x, y, p);

            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText(minutes + " Minutes and " + seconds
                    + " seconds", x, y, p);

            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("until christmas.", x, y, p);

            invalidate();

        } else {

            // draw the normal strings
            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("Something Wrong", x, y, p);

            canvas.translate(0, DY);
            p.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("Check your device time.", x, y, p);

        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int ow, int oh) {
        super.onSizeChanged(w, h, ow, oh);
        mX = w * 0.5f; // remember the center of the screen
    }

}

} 

And there is small problem with counting how many days, because it shows that there is only 10 days for Christmas. I didn't know anything about graphics on android, worked hard on this.

package in.isuru.animation;

import org.joda.time.DateTime;
import org.joda.time.Seconds;

public class Duration {

public int cal_duration(){
    DateTime start = new DateTime(DateTime.now());
    DateTime end = new DateTime(2012, 12, 25, 0, 0, 0 ,0);
    int differenceSeconds = Seconds.secondsBetween(end, start).getSeconds();

    return differenceSeconds * 1000;
}

}

The problem is on every Tick it calculate days, hours, minutes and seconds. Please give me feedback on whether this is good or is there any better way?


Solution

  • Your numbers will be wrong because you're overflowing the int data type with milliseconds. The max value of an int is 2^31 ~ 2 x 10^9. There are about 155 x 10^9 milliseconds left until Christmas.

    Just compute seconds and you'll be okay. Or you could use a 64-bit long instead of int. But you multiply by 1000 to get milliseconds and then divide by 1000 immediately! This doesn't make much sense.

    Better yet follow through wiht joda time!

    Since you are already using it, why not construct a period?

    DateTime start = DateTime.now();  // Note your code makes an unneeded copy here
    DateTime end = new DateTime(2012, 12, 25, 0, 0, 0);
    Period p = new Period(start, end);
    

    The period has getters for years, months, days, hour, minutes, seconds. This will avoid the error prone calculation completely.