android wear-Drawing Watch Faces

desaco發表於2016-01-16

> Drawing Watch Faces

 After you have configured your project and added a class that implements the watch face service, you can start writing code to initialize and draw your custom watch face.

 When the system loads your service, you should allocate and initialize most of the resources that your watch face needs, including loading bitmap resources, creating timer objects to run custom animations, configuring paint styles, and performing other computations. You can usually perform these operations only once and reuse their results. 

 >To initialize your watch face, follow these steps:

  1. Declare variables for a custom timer, graphic objects, and other elements.
  2. Initialize the watch face elements in the Engine.onCreate() method.
  3. Initialize the custom timer in the Engine.onVisibilityChanged() method.
private class Engine extends CanvasWatchFaceService.Engine {
    static final int MSG_UPDATE_TIME = 0;

    Calendar mCalendar;

    // device features
    boolean mLowBitAmbient;

    // graphic objects
    Bitmap mBackgroundBitmap;
    Bitmap mBackgroundScaledBitmap;
    Paint mHourPaint;
    Paint mMinutePaint;
    ...

    // handler to update the time once a second in interactive mode
    final Handler mUpdateTimeHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_UPDATE_TIME:
                    invalidate();
                    if (shouldTimerBeRunning()) {
                        long timeMs = System.currentTimeMillis();
                        long delayMs = INTERACTIVE_UPDATE_RATE_MS
                                - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
                        mUpdateTimeHandler
                            .sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
                    }
                    break;
            }
        }
    };

    // receiver to update the time zone
    final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mCalendar.setTimeZone(TimeZone.getDefault());
            invalidate();
        }
    };

    // service methods (see other sections)
    ...
}

In the example above, the custom timer is implemented as a Handler instance that sends and processes delayed messages using the thread's message queue. For this particular watch face, the custom timer ticks once every second. When the timer ticks, the handler calls the invalidate() method and the system then calls the<a href="http://developer.android.com/reference/android/support/wearable/watchface/CanvasWatchFaceService.Engine.html#onDraw(android.graphics.Canvas, android.graphics.Rect)" onDraw() method to redraw the watch face.

@Override
public void onCreate(SurfaceHolder holder) {
    super.onCreate(holder);

    // configure the system UI (see next section)
    ...

    // load the background image
    Resources resources = AnalogWatchFaceService.this.getResources();
    Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null);
    mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();

    // create graphic styles
    mHourPaint = new Paint();
    mHourPaint.setARGB(255, 200, 200, 200);
    mHourPaint.setStrokeWidth(5.0f);
    mHourPaint.setAntiAlias(true);
    mHourPaint.setStrokeCap(Paint.Cap.ROUND);
    ...

    // allocate a Calendar to calculate local time using the UTC time and time zone
    mCalendar = Calendar.getInstance();
}

The background bitmap is loaded only once when the system initializes the watch face. The graphic styles are instances of the Paint class. Use these styles to draw the elements of your watch face inside theEngine.onDraw() method, as described in Drawing Your Watch Face.

The AnalogWatchFaceService class schedules the next timer tick if required as follows:
private void updateTimer() {
    mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
    if (shouldTimerBeRunning()) {
        mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
    }
}

private boolean shouldTimerBeRunning() {
    return isVisible() && !isInAmbientMode();
}

@Override
public void onVisibilityChanged(boolean visible) {
    super.onVisibilityChanged(visible);

    if (visible) {
        registerReceiver();

        // Update time zone in case it changed while we weren't visible.
        mCalendar.setTimeZone(TimeZone.getDefault());
    } else {
        unregisterReceiver();
    }

    // Whether the timer should be running depends on whether we're visible and
    // whether we're in ambient mode, so we may need to start or stop the timer
    updateTimer();
}
>  TheregisterReceiver() and unregisterReceiver() methods are implemented as follows:
private void registerReceiver() {
    if (mRegisteredTimeZoneReceiver) {
        return;
    }
    mRegisteredTimeZoneReceiver = true;
    IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
    AnalogWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
}

private void unregisterReceiver() {
    if (!mRegisteredTimeZoneReceiver) {
        return;
    }
    mRegisteredTimeZoneReceiver = false;
    AnalogWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
}

The following snippet shows how to implement the onDraw() method:

@Override
public void onDraw(Canvas canvas, Rect bounds) {
    // Update the time
    mCalendar.setTimeInMillis(System.currentTimeMillis());

    // Constant to help calculate clock hand rotations
    final float TWO_PI = (float) Math.PI * 2f;

    int width = bounds.width();
    int height = bounds.height();

    canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);

    // Find the center. Ignore the window insets so that, on round watches
    // with a "chin", the watch face is centered on the entire screen, not
    // just the usable portion.
    float centerX = width / 2f;
    float centerY = height / 2f;

    // Compute rotations and lengths for the clock hands.
    float seconds = mCalendar.get(Calendar.SECOND) +
                    mCalendar.get(Calendar.MILLISECOND) / 1000f;
    float secRot = seconds / 60f * TWO_PI;
    float minutes = mCalendar.get(Calendar.MINUTE) + seconds / 60f;
    float minRot = minutes / 60f * TWO_PI;
    float hours = mCalendar.get(Calendar.HOUR) + minutes / 60f;
    float hrRot = hours / 12f * TWO_PI;

    float secLength = centerX - 20;
    float minLength = centerX - 40;
    float hrLength = centerX - 80;

    // Only draw the second hand in interactive mode.
    if (!isInAmbientMode()) {
        float secX = (float) Math.sin(secRot) * secLength;
        float secY = (float) -Math.cos(secRot) * secLength;
        canvas.drawLine(centerX, centerY, centerX + secX, centerY +
                        secY, mSecondPaint);
    }

    // Draw the minute and hour hands.
    float minX = (float) Math.sin(minRot) * minLength;
    float minY = (float) -Math.cos(minRot) * minLength;
    canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY,
                    mMinutePaint);
    float hrX = (float) Math.sin(hrRot) * hrLength;
    float hrY = (float) -Math.cos(hrRot) * hrLength;
    canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY,
                    mHourPaint);
}

相關文章