1

I'm developing a watch face and want to rotate the second's hand 1.2 degrees in 1/5 of a second.

Why these numbers?

1) 360 degrees / 60 seconds = 6 degress per second

2) 6 degrees / 5 = 1.2 degrees (rotates 1.2 degrees in a fifth of a second).

Below is the normal rotation (6 degress in a second).

enter image description here

The image above spends one second to rotate 6 degrees.

My question is: How to spends one fifth of a second to rotate 1.2 degress?

This is my code:

private void drawClassicSecondsHand(Canvas canvas) {

    final int seconds = (mCalendar.get(Calendar.SECOND));
    final float secondsRotation = seconds * 6;

    float originRadius = complicationHandBitmap.getWidth() / 2f;

    float secondsAreaCenterX = mCenterX - (float) (mCenterX * 0.45);
    canvas.rotate(secondsRotation, secondsAreaCenterX, mCenterY);
    classicSecondsComplication.draw(
            canvas,
            secondsAreaCenterX,
            mCenterY,
            classicComplicationRadius,
            complicationHandBitmap,
            originRadius);
}

The above method is called in the onDraw method of my watch face service.

public void onDraw(Canvas canvas, Rect bounds) {

   ...
   drawClassicSecondsHand(Canvas canvas);
}
TofferJ
  • 4,216
  • 1
  • 31
  • 43
Pablo Darde
  • 3,855
  • 6
  • 29
  • 47

2 Answers2

2

I'm not sure where your onDraw method is called from right now, but updating continuously is not good from a battery life point of view.

If you create a new project in Android Studio and select Wear OS and the analog watch face template you'll see a better way to do it. At the very bottom they set up a message handler that will trigger redraws of the watch face at a specific interval (in interactive mode).

/**
 * Handle updating the time periodically in interactive mode.
 */
private void handleUpdateTimeMessage() {
    invalidate();
    if (shouldTimerBeRunning()) {
        long timeMs = System.currentTimeMillis();
        long delayMs = INTERACTIVE_UPDATE_RATE_MS
                - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
        mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
    }
}

(There are more parts to it than the snippet above, but everything is in the sample watch face.)

In this example the INTERACTIVE_UPDATE_RATE_MS is set to a second. All you have to do is set it to 200ms, and your watch face will update five times per second. Rotate the canvas 1.2 degrees each time and you'll have the desired second hand movement.

TofferJ
  • 4,216
  • 1
  • 31
  • 43
1

You need to include the millisecond in the calculation.

You should also get rid of excessive parentheses and the unnecessary cast.

final int seconds = mCalendar.get(Calendar.SECOND);
final int millis = mCalendar.get(Calendar.MILLISECOND);
final float secondsRotation = (seconds + millis / 1000f) * 6;

The f at the end of the 1000f literal makes it a float, preventing integer math truncation issues.
That means millis is promoted to float, so the result of division is float.
That in turn promotes seconds to float, so the result of addition is float.
That in turn promotes 6 to float, so the result of multiplication is float.
There is then no need to cast the result.

Andreas
  • 138,167
  • 8
  • 112
  • 195
  • Thanks, @Andreas, but, how can I apply the wished logic of moves the second's hand five times per second? – Pablo Darde Oct 30 '18 at 23:31
  • @PabloDarde How did you make it move once per second? Your question is about calculating the angle with higher precision. If you have a question about changing your existing code, which presumably runs the *shown code* once per second, to run the new code 5 times per second, then create a new question asking that, and *show your current code* for running once per second. – Andreas Oct 30 '18 at 23:32
  • Basically, I used the code above. I used the `cavas.rotate(...)` method, and set the first argument (degrees) as `seconds * 6`. – Pablo Darde Oct 30 '18 at 23:36
  • @PabloDarde That will rotate the hand to the correct angle *once*. To make the hand move, you need to re-execute the code. You have not shown how you're currently re-executing the code every second, or five times per second, or whenever. If you have questions about how to do that, create a new question and ask how to do it (*after* researching it, since there should be many answers for that already out there). **This question** is about calculating angle with more precision, and **it has been answered**. – Andreas Oct 30 '18 at 23:40
  • appreciate your answer. This question is not about calculate the angle, I know how to calculate the angle. I wrote all the calculus in my question. this is a Wear OS question. The rotate canvas method is called every time the `onDraw` method is called (about 60 fps), however, with the `canvas.rotate` method I can override these fps calls with the `Calendar.SECOND` argument. I don't know how to manipulate the `Calendar.SECOND` and the `Calendar.MILLISECOND` in order to achieve my goal. – Pablo Darde Oct 30 '18 at 23:48
  • @PabloDarde You wrote the calculation that only calculates in 6 degree increments, and ask **how to calculate** with more precision (1.2 degree increments). As such, the question is *entirely* about calculating the angle. --- Nowhere in the question do you mention `onDraw` or mention that your code is *called/executed* 60 times per second. If so, then your code is setting the *same angle* 60 times per second. If you simply update the calculation as shown, the hand will move **every time** the `onDraw` method is called, and the hand will move smoothly. – Andreas Oct 30 '18 at 23:58
  • If you (meaning the watch API) has method for controlling the FPS, and you need help doing that, create a new question and show what code you currently have for controlling the FPS, and we can help adjust that as needed. – Andreas Oct 31 '18 at 00:00