>

Android 착용 용 시계 Face를 만들려고 한 주가되었습니다. 시작으로 Google 공식 문서를 따르고 소스가있는 Android 공식 시계 얼굴 앱 튜토리얼을 찾았습니다. 코드

따라서 현재의 문제는 Google 문서에서캔버스를 사용하여 아날로그 시계 모드를 만드는 것입니다. 시계 바늘은페인트

다이얼 핸드를 만들기위한 코드 샘플은 다음과 같습니다

   public class AnalogWatchFaceService extends CanvasWatchFaceService {
private static final String TAG = "AnalogWatchFaceService";
/**
 * Update rate in milliseconds for interactive mode. We update once a second to advance the
 * second hand.
 */
private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
@Override
public Engine onCreateEngine() {
    return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
    static final int MSG_UPDATE_TIME = 0;
    static final float TWO_PI = (float) Math.PI * 2f;
    Paint mHourPaint;
    Paint mMinutePaint;
    Paint mSecondPaint;
    Paint mTickPaint;
    boolean mMute;
    Calendar mCalendar;
    /** 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:
                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
                        Log.v(TAG, "updating 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;
            }
        }
    };
    final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mCalendar.setTimeZone(TimeZone.getDefault());
            invalidate();
        }
    };
    boolean mRegisteredTimeZoneReceiver = false;
    /**
     * Whether the display supports fewer bits for each color in ambient mode. When true, we
     * disable anti-aliasing in ambient mode.
     */
    boolean mLowBitAmbient;
    Bitmap mBackgroundBitmap;
    Bitmap mBackgroundScaledBitmap;
    @Override
    public void onCreate(SurfaceHolder holder) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onCreate");
        }
        super.onCreate(holder);
        setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
                .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
                .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
                .setShowSystemUiTime(false)
                .build());
        Resources resources = AnalogWatchFaceService.this.getResources();
        Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg, null /* theme */);
        mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
        mHourPaint = new Paint();
        mHourPaint.setARGB(255, 200, 200, 200);
        mHourPaint.setStrokeWidth(5.f);
        mHourPaint.setAntiAlias(true);
        mHourPaint.setStrokeCap(Paint.Cap.ROUND);
        mMinutePaint = new Paint();
        mMinutePaint.setARGB(255, 200, 200, 200);
        mMinutePaint.setStrokeWidth(3.f);
        mMinutePaint.setAntiAlias(true);
        mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
        mSecondPaint = new Paint();
        mSecondPaint.setARGB(255, 255, 0, 0);
        mSecondPaint.setStrokeWidth(2.f);
        mSecondPaint.setAntiAlias(true);
        mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
        mTickPaint = new Paint();
        mTickPaint.setARGB(100, 255, 255, 255);
        mTickPaint.setStrokeWidth(2.f);
        mTickPaint.setAntiAlias(true);
        mCalendar = Calendar.getInstance();
    }
    @Override
    public void onDestroy() {
        mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
        super.onDestroy();
    }
    @Override
    public void onPropertiesChanged(Bundle properties) {
        super.onPropertiesChanged(properties);
        mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
        }
    }
    @Override
    public void onTimeTick() {
        super.onTimeTick();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
        }
        invalidate();
    }
    @Override
    public void onAmbientModeChanged(boolean inAmbientMode) {
        super.onAmbientModeChanged(inAmbientMode);
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
        }
        if (mLowBitAmbient) {
            boolean antiAlias = !inAmbientMode;
            mHourPaint.setAntiAlias(antiAlias);
            mMinutePaint.setAntiAlias(antiAlias);
            mSecondPaint.setAntiAlias(antiAlias);
            mTickPaint.setAntiAlias(antiAlias);
        }
        invalidate();
        // Whether the timer should be running depends on whether we're in ambient mode (as well
        // as whether we're visible), so we may need to start or stop the timer.
        updateTimer();
    }
    @Override
    public void onInterruptionFilterChanged(int interruptionFilter) {
        super.onInterruptionFilterChanged(interruptionFilter);
        boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
        if (mMute != inMuteMode) {
            mMute = inMuteMode;
            mHourPaint.setAlpha(inMuteMode ? 100 : 255);
            mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
            mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
            invalidate();
        }
    }
    @Override
    public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mBackgroundScaledBitmap == null
                || mBackgroundScaledBitmap.getWidth() != width
                || mBackgroundScaledBitmap.getHeight() != height) {
            mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
                    width, height, true /* filter */);
        }
        super.onSurfaceChanged(holder, format, width, height);
    }
    @Override
    public void onDraw(Canvas canvas, Rect bounds) {
        mCalendar.setTimeInMillis(System.currentTimeMillis());
        int width = bounds.width();
        int height = bounds.height();
        // Draw the background, scaled to fit.
        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;
        // Draw the ticks.
        float innerTickRadius = centerX - 10;
        float outerTickRadius = centerX;
        for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
            float tickRot = tickIndex * TWO_PI / 12;
            float innerX = (float) Math.sin(tickRot) * innerTickRadius;
            float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
            float outerX = (float) Math.sin(tickRot) * outerTickRadius;
            float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
            canvas.drawLine(centerX + innerX, centerY + innerY,
                    centerX + outerX, centerY + outerY, mTickPaint);
        }
        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;
        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);
        }
        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);
    }

}

전체 코드는 공식 샘플 앱에서 찾을 수 있습니다. 아래에서 Google 공식 튜토리얼을 사용하여 만든 응용 프로그램의 스크린 샷을 찾을 수 있습니다.

시계 바늘을 드로어 블 이미지로 교체하는 방법을 아는 사람이 있습니까? . 도움을 주시면 감사하겠습니다.


  • 답변 # 1

    드로어 블 리소스의 비트 맵 만들기 :

    Bitmap hourHand = BitmapFactory.decodeResource(context.getResources(), R.drawable.hour_hand);
    
    

    캔버스에 필요한 모든 변환을 수행하고 비트 맵을 그립니다 :

    canvas.save();
    canvas.rotate(degrees, px, py);
    canvas.translate(dx, dy);
    canvas.drawBitmap(hourHand, centerX, centerY, null); // Or use a Paint if you need it
    canvas.restore();
    
    

  • 답변 # 2

    캔버스에서 비트 맵을 회전하려면 다음 방법을 사용하십시오

    /**
     * To rotate bitmap on canvas
     *
     * @param canvas         : canvas on which you are drawing
     * @param handBitmap     : bitmap of hand
     * @param centerPoint    : center for rotation
     * @param rotation       : rotation angle in form of seconds
     * @param offset         : offset of bitmap from center point (If not needed then keep it 0)
     */
    public void rotateBitmap(Canvas canvas, Bitmap handBitmap, PointF centerPoint, float rotation, float offset) {
        canvas.save();
        canvas.rotate(secondRotation - 90, centerPoint.x, centerPoint.y);
        canvas.drawBitmap(handBitmap, centerPoint.x - offset, centerPoint.y - handBitmap.getHeight() / Constants.INTEGER_TWO, new Paint(Paint.FILTER_BITMAP_FLAG));
        canvas.restore();
    }
    
    

  • 답변 # 3

    답변이 약간 늦었지만 다른 사람들에게 도움이 될 수 있습니다

    canvas.save()
    val antialias = Paint()
    antialias.isAntiAlias = true
    antialias.isFilterBitmap = true
    antialias.isDither = true
    canvas.rotate(secondsRotation - minutesRotation, centerX, centerY)
    canvas.drawBitmap(
         secondsHandBitmap,
         centerX - 10,
         centerY - 160,
         antialias
    )
    canvas.restore()
    
    

    여기서 공개 Git Repo는 소스 코드를 확인할 수 있습니다

관련 자료

  • 이전 ansible을 사용하여 원격 공유 경로로 복사
  • 다음 opengl - 가로로 감싸는 파노라마 이미지의 큐브 맵