我正在尝试编码的表盘无法正常工作。这是代码:
package mnx.watch;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.wearable.watchface.CanvasWatchFaceService;
import android.support.wearable.watchface.WatchFaceStyle;
import android.text.format.Time;
import android.view.SurfaceHolder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
/**
* Analog watch face with a ticking second hand. In ambient mode, the second hand isn't shown. On
* devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient mode.
*/
public class WatchFace extends CanvasWatchFaceService {
/**
* 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 implements SensorEventListener{
static final int MSG_UPDATE_TIME = 0;
Bitmap mBackgroundBitmap;
Bitmap mBackgroundScaledBitmap;
Bitmap mBackgroundBitmapamb;
Bitmap mBackgroundScaledBitmapamb;
Bitmap minMap;
Bitmap minSMap;
Matrix minMatrix;
Bitmap hrMap;
Bitmap hrSMap;
Matrix hrMatrix;
Bitmap secMap;
Bitmap secSMap;
Matrix secMatrix;
Bitmap ziMap;
Bitmap ziSMap;
Matrix ziMatrix;
Matrix lunaMatrix;
Bitmap minMapamb;
Bitmap minSMapamb;
Bitmap hrMapamb;
Bitmap hrSMapamb;
Bitmap busola;
Bitmap busolaMap;
Matrix busolaMatrix;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mMagnetometer;
private float[] mLastAccelerometer = new float[3];
private float[] mLastMagnetometer = new float[3];
private boolean mLastAccelerometerSet = false;
private boolean mLastMagnetometerSet = false;
private float[] mR = new float[9];
private float[] mOrientation = new float[3];
private float mCurrentDegree = 0f;
boolean mAmbient;
Time mTime;
/**
* 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;
}
}
};
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mTime.clear(intent.getStringExtra("time-zone"));
mTime.setToNow();
}
};
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;
@Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
setWatchFaceStyle(new WatchFaceStyle.Builder(WatchFace.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = WatchFace.this.getResources();
Drawable backgroundDrawable = resources.getDrawable(R.drawable.fata, null);
mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
Drawable backgroundambDrawable = resources.getDrawable(R.drawable.background_amb1, null);
mBackgroundBitmapamb = ((BitmapDrawable) backgroundambDrawable).getBitmap();
Drawable minuteDrawable = resources.getDrawable(R.drawable.minute,null);
minMap = ((BitmapDrawable) minuteDrawable).getBitmap();
minMatrix = new Matrix();
Drawable hourDrawable = resources.getDrawable(R.drawable.hour,null);
hrMap = ((BitmapDrawable)hourDrawable).getBitmap();
hrMatrix = new Matrix();
Drawable secDrawable = resources.getDrawable(R.drawable.second,null);
secMap = ((BitmapDrawable)secDrawable).getBitmap();
secMatrix = new Matrix();
Drawable minuteambDrawable = resources.getDrawable(R.drawable.minute_amb,null);
minMapamb = ((BitmapDrawable) minuteambDrawable).getBitmap();
Drawable hourambDrawable = resources.getDrawable(R.drawable.hour_amb,null);
hrMapamb = ((BitmapDrawable)hourambDrawable).getBitmap();
Drawable ziDrawable = resources.getDrawable(R.drawable.zi,null);
ziMap = ((BitmapDrawable)ziDrawable).getBitmap();
ziMatrix = new Matrix();
lunaMatrix = new Matrix();
Drawable busolaDrawable = resources.getDrawable(R.drawable.busola,null);
busola = ((BitmapDrawable)busolaDrawable).getBitmap();
busolaMatrix = new Matrix();
mTime = new Time();
}
@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);
}
@Override
public void onTimeTick() {
super.onTimeTick();
invalidate();
}
@Override
public void onAmbientModeChanged(boolean inAmbientMode) {
super.onAmbientModeChanged(inAmbientMode);
if (mAmbient != inAmbientMode) {
mAmbient = inAmbientMode;
invalidate();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
@Override
public void onDraw(Canvas canvas, Rect bounds) {
mTime.setToNow();
int width = bounds.width();
int height = bounds.height();
if(mBackgroundScaledBitmap == null
|| mBackgroundScaledBitmap.getWidth() != width
|| mBackgroundScaledBitmap.getHeight() != height){
mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,width,height,true);
}
mBackgroundScaledBitmapamb = Bitmap.createScaledBitmap(mBackgroundBitmapamb,width,height,false);
// Draw the background.
minSMap = Bitmap.createScaledBitmap(minMap,width,height,true);
hrSMap = Bitmap.createScaledBitmap(hrMap,width,height,true);
secSMap = Bitmap.createScaledBitmap(secMap,width,height,true);
minSMapamb = Bitmap.createScaledBitmap(minMapamb,width,height,true);
hrSMapamb = Bitmap.createScaledBitmap(hrMapamb,width,height,true);
ziSMap = Bitmap.createScaledBitmap(ziMap,ziMap.getWidth()-23,ziMap.getHeight()-23,true);
busolaMap = Bitmap.createScaledBitmap(busola,busola.getWidth()/3 - 10,busola.getHeight()/3 - 10,true);
// 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.
int day = mTime.weekDay;
int month = mTime.month ;
SimpleDateFormat dfDate = new SimpleDateFormat("dd");
String date = "";
Calendar c = Calendar.getInstance();
date=dfDate.format(c.getTime());
float ziRot = (day-1) * (360/7);
int lunaRot = (month) * (360/12) + 15;
float centerX = width / 2f;
float centerY = height / 2f;
float widthp;
float secRot = mTime.second * 6;
int minutes = mTime.minute;
float minRot = minutes * 6;
float hrRot = (float) (mTime.hour*30+mTime.minute*0.5);
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setTextSize(20);
mPaint.setTypeface(Typeface.SANS_SERIF);
widthp=mPaint.measureText(date);
ziMatrix.setRotate(ziRot, ziSMap.getWidth() / 2, ziSMap.getHeight() / 2);
ziMatrix.postTranslate(centerX - 129, centerY - 114);
lunaMatrix.setRotate(lunaRot,ziSMap.getWidth() / 2, ziSMap.getHeight() / 2);
lunaMatrix.postTranslate(centerX - 13 , centerY -114);
secMatrix.setRotate(secRot, secSMap.getWidth() / 2, secSMap.getHeight() / 2);
secMatrix.postTranslate(centerX - secSMap.getWidth() / 2, centerY - secSMap.getHeight() / 2);
minMatrix.setRotate(minRot, minSMap.getWidth() / 2, minSMap.getHeight() / 2);
minMatrix.postTranslate(centerX - minSMap.getWidth() / 2, centerY - minSMap.getHeight() / 2);
hrMatrix.setRotate(hrRot, hrSMap.getWidth() / 2, hrSMap.getHeight() / 2);
hrMatrix.postTranslate(centerX - hrSMap.getWidth() / 2, centerY - hrSMap.getHeight() / 2);
busolaMatrix.setRotate(mCurrentDegree , busolaMap.getWidth()/2,busolaMap.getHeight()/2);
busolaMatrix.postTranslate(centerX - busolaMap.getWidth()/2 ,centerY +25);
if (!mAmbient) {
canvas.drawBitmap(mBackgroundScaledBitmap,0,0, null);
canvas.drawBitmap(ziSMap,ziMatrix,null);
canvas.drawBitmap(ziSMap,lunaMatrix,null);
canvas.drawText(date,centerX - widthp/2,centerY - 80 ,mPaint);
canvas.drawBitmap(busolaMap,busolaMatrix,null);
canvas.drawBitmap(minSMap, minMatrix,null);
canvas.drawBitmap(hrSMap, hrMatrix,null);
canvas.drawBitmap(secSMap, secMatrix, null);
}
else {
canvas.drawBitmap(mBackgroundScaledBitmapamb, 0, 0, null);
canvas.drawBitmap(hrSMapamb, hrMatrix, null);
canvas.drawBitmap(minSMapamb, minMatrix, null);
}
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
registerReceiver();
registerSensor();
// Update time zone in case it changed while we weren't visible.
mTime.clear(TimeZone.getDefault().getID());
mTime.setToNow();
} else {
unregisterReceiver();
unregisterSensor();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
private void registerReceiver() {
if (mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
WatchFace.this.registerReceiver(mTimeZoneReceiver, filter);
}
private void unregisterReceiver() {
if (!mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = false;
WatchFace.this.unregisterReceiver(mTimeZoneReceiver);
}
private void registerSensor(){
mSensorManager.registerListener(this,mAccelerometer,SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_NORMAL);
}
private void unregisterSensor(){
mSensorManager.unregisterListener(this, mAccelerometer);
mSensorManager.unregisterListener(this, mMagnetometer);
}
/**
* Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
* or stops it if it shouldn't be running but currently is.
*/
private void updateTimer() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
/**
* Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
* only run when we're visible and in interactive mode.
*/
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
@Override
public void onSensorChanged(SensorEvent event){
if (event.sensor == mAccelerometer){
System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
mLastAccelerometerSet = true;
}
else if (event.sensor ==mMagnetometer){
System.arraycopy(event.values,0,mLastMagnetometer,0,event.values.length);
mLastMagnetometerSet = true;
}
if (mLastAccelerometerSet && mLastMagnetometerSet) {
SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
SensorManager.getOrientation(mR, mOrientation);
float degree = (float) Math.floor(Math.toDegrees(mOrientation[0]));
if (degree < 0) {
degree = 360 + degree;
}
if (mCurrentDegree < -300 && -degree > -60) {
mCurrentDegree = mCurrentDegree + 360;
} else if (mCurrentDegree > -60 && -degree < -300) {
mCurrentDegree = mCurrentDegree - 360;
}
mCurrentDegree = (0.9f *mCurrentDegree) + (0.1f*-degree);
invalidate();
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy){}
}
}
我做了一个测试并在没有指南针的情况下运行代码,它可以工作,手表指针似乎有点粗糙,但没关系,它有效。在我在程序中引入代码后,它没有显示任何错误,但在我点击运行后,我的智能手表需要一段时间,但它显示了一条错误消息:
不幸的是,Watch 已停止。
这是调试:
06-26 21:18:49.864 18676-18676/mnx.watch E/AndroidRuntime:致命异常:主进程:mnx.watch,PID:18676 java.lang.NullPointerException:尝试调用虚拟方法'boolean android.hardware.SensorManager .registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int)' 在 mnx.watch.WatchFace$Engine.registerSensor(WatchFace.java:374) 的 mnx.watch.WatchFace$Engine.onVisibilityChanged 的空对象引用上(WatchFace.java:341) 在 android.service.service.wallpaper.WallpaperService$Engine.updateSurface(WallpaperService.java:809) 在 android.service.wallpaper.WallpaperService$Engine.attach(WallpaperService.java:860) 在 android.service。壁纸.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:1147) 在 com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java :135) 在 android.app.ActivityThread.main(ActivityThread.java:5254) 在 java.lang.reflect.Method.invoke(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:372) 在com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)app.ActivityThread.main(ActivityThread.java:5254) 在 java.lang.reflect.Method.invoke(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:372) 在 com.android.internal。 os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)app.ActivityThread.main(ActivityThread.java:5254) 在 java.lang.reflect.Method.invoke(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:372) 在 com.android.internal。 os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)