홈>
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
-
답변 # 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는 소스 코드를 확인할 수 있습니다
관련 자료
- r - 사용자 지정 함수에서 생성 된 목록의 n 개의 데이터 프레임 하위 집합을 전역 환경의 개체에 할당
- Android 사용자 정의 라이브러리 모듈 BindingAdapter 속성이 인식되지 않음
- Android의 그림 모드에서 그림의 사용자 정의 화면 크기를 어떻게 만듭니 까?
- java - 문자열로 날짜와 시간을 얻을 수있는 사용자 정의 클래스를 만들었습니다 객체에 액세스하고 클래스에 데이터를 설정하고 싶습니다
- android에서 opencv java를 사용하여 두 이미지의 얼굴 인식을 어떻게 수행 할 수 있습니까?
- Android 경고 대화 상자 사용자 정의 패널 및 버튼 패널 높이가 0입니다
- 내 Android 스튜디오 드로어 블 리소스 폴더는 매우 복잡한 방식으로 가져옵니다
- Android Studio 36 이상에 대한 사용자 지정 프로젝트 템플릿을 생성하려면 어떻게해야합니까?
- kotlin - 매개 변수가있는 사용자 정의 바인딩 어댑터 리스너 메소드를 사용하는 Android 사용자 정의보기
- xamarin.android - Android 경고 대화 상자 사용자 정의보기가 상위 높이와 일치하지 않습니다
- 애플리케이션 재설치 후 Android 외부 저장소의 이미지에 액세스
- Android 사용자 정의 대화 상자에 키보드가 나타나지 않습니다
- java - 기계적 인조 인간 - 맞춤형 arrayadapter :listitems에 대한 클릭이 인식되지 않습니다
- 조각의 목록을 Android의 사용자 지정 뷰 클래스로 보내는 방법은 무엇입니까?
- Android는 xxhdpi 폴더에만 이미지를 넣을 수 있습니까?
- android - EditText 용 사용자 정의 커서 드로어 블은 API Q +에서 무시됩니다
- Flutter Android에서 사용자 정의보기를 실제 시작 화면으로 설정하는 방법
- xml - Android에서 작동하지 않는 사용자 정의 백그라운드 리소스
- kotlin - Android 사용자 지정 ConstraintLayout은 ConstraintLayout 높이가 변경 될 때 자식 높이를 업데이트 할 수 없습니다
- material design - Android Studio 41 이후 Android Background Drawable이 버튼에서 작동하지 않음
트렌드
- OpenCv의 폴더에서 여러 이미지 읽기 (python)
- 파이썬 셀레늄 모든 "href"속성 가져 오기
- git commit - 자식 - 로컬 커밋 된 파일에 대한 변경을 취소하는 방법
- html - 자바 스크립트 - 클릭 후 변경 버튼 텍스트 변경
- JSP에 대한 클래스를 컴파일 할 수 없습니다
- javascript - 현재 URL에서 특정 div 만 새로 고침/새로 고침
- jquery - JavaScript로 현재 세션 값을 얻으시겠습니까?
- javascript - swiperjs에서 정지, 재생 버튼 추가
- vue.js - axios를 사용하여 서버에 이미지를 업로드하는 방법
- python - 문자열에서 특정 문자 제거
드로어 블 리소스의 비트 맵 만들기 :
캔버스에 필요한 모든 변환을 수행하고 비트 맵을 그립니다 :