我看了几个地方,包括在这里,我找到的答案没有帮助。我还查看了我正在使用的模板的代码,但没有看到哪里出错了,所以如果这看起来是个坏问题,我很抱歉。但我正在制作表盘,并试图从我的引擎 WeatherWatchFaceEngine 扩展它。但是当我这样做时,我得到了这个问题标题中的错误。我做错了什么?
这是表盘的代码:
public class NimbusSplashAnalog extends WeatherWatchFaceService {
/**
* 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);
/**
* Handler message id for updating the time periodically in interactive mode.
*/
private static final int MSG_UPDATE_TIME = 0;
@Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends WeatherWatchFaceEngine {
Paint mBackgroundPaint;
Paint mHandPaint;
boolean mAmbient;
Time mTime;
final Handler mUpdateTimeHandler = new EngineHandler(this);
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(NimbusSplashAnalog.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = NimbusSplashAnalog.this.getResources();
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(resources.getColor(R.color.analog_background));
mHandPaint = new Paint();
mHandPaint.setColor(resources.getColor(R.color.analog_hands));
mHandPaint.setStrokeWidth(resources.getDimension(R.dimen.analog_hand_stroke));
mHandPaint.setAntiAlias(true);
mHandPaint.setStrokeCap(Paint.Cap.ROUND);
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;
if (mLowBitAmbient) {
mHandPaint.setAntiAlias(!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();
// Draw the background.
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mBackgroundPaint);
// 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;
float secRot = mTime.second / 30f * (float) Math.PI;
int minutes = mTime.minute;
float minRot = minutes / 30f * (float) Math.PI;
float hrRot = ((mTime.hour + (minutes / 60f)) / 6f) * (float) Math.PI;
float secLength = centerX - 20;
float minLength = centerX - 40;
float hrLength = centerX - 80;
if (!mAmbient) {
float secX = (float) Math.sin(secRot) * secLength;
float secY = (float) -Math.cos(secRot) * secLength;
canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mHandPaint);
}
float minX = (float) Math.sin(minRot) * minLength;
float minY = (float) -Math.cos(minRot) * minLength;
canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mHandPaint);
float hrX = (float) Math.sin(hrRot) * hrLength;
float hrY = (float) -Math.cos(hrRot) * hrLength;
canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHandPaint);
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
registerReceiver();
// Update time zone in case it changed while we weren't visible.
mTime.clear(TimeZone.getDefault().getID());
mTime.setToNow();
} else {
unregisterReceiver();
}
// 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);
NimbusSplashAnalog.this.registerReceiver(mTimeZoneReceiver, filter);
}
private void unregisterReceiver() {
if (!mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = false;
NimbusSplashAnalog.this.unregisterReceiver(mTimeZoneReceiver);
}
/**
* 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();
}
/**
* 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);
}
}
}
private static class EngineHandler extends Handler {
private final WeakReference<NimbusSplashAnalog.Engine> mWeakReference;
public EngineHandler(NimbusSplashAnalog.Engine reference) {
mWeakReference = new WeakReference<>(reference);
}
@Override
public void handleMessage(Message msg) {
NimbusSplashAnalog.Engine engine = mWeakReference.get();
if (engine != null) {
switch (msg.what) {
case MSG_UPDATE_TIME:
engine.handleUpdateTimeMessage();
break;
}
}
}
}
}
这是我从中扩展的引擎/服务的代码:
public abstract class WeatherWatchFaceService extends CanvasWatchFaceService {
public class WeatherWatchFaceEngine extends CanvasWatchFaceService.Engine
implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
DataApi.DataListener, NodeApi.NodeListener {
protected static final int MSG_UPDATE_TIME = 0;
protected long UPDATE_RATE_MS;
protected static final long WEATHER_INFO_TIME_OUT = DateUtils.HOUR_IN_MILLIS * 6;
protected final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Time zone changed
mWeatherInfoReceivedTime = 0;
mTime.clear(intent.getStringExtra("time-zone"));
mTime.setToNow();
}
};
/**
* Handler to update the time periodically in interactive mode.
*/
protected final Handler mUpdateTimeHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_TIME:
invalidate();
if (shouldUpdateTimerBeRunning()) {
long timeMs = System.currentTimeMillis();
long delayMs = UPDATE_RATE_MS - (timeMs % UPDATE_RATE_MS);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
requireWeatherInfo();
}
break;
}
}
};
protected int mTheme = 3;
protected int mTimeUnit = ConverterUtil.TIME_UNIT_12;
protected AssetManager mAsserts;
protected Bitmap mWeatherConditionDrawable;
protected GoogleApiClient mGoogleApiClient;
protected Paint mBackgroundPaint;
protected Paint mDatePaint;
protected Paint mDateSuffixPaint;
protected Paint mDebugInfoPaint;
protected Paint mTemperatureBorderPaint;
protected Paint mTemperaturePaint;
protected Paint mTemperatureSuffixPaint;
protected Paint mTimePaint;
protected Resources mResources;
protected String mWeatherCondition;
protected String mWeatherConditionResourceName;
protected Time mSunriseTime;
protected Time mSunsetTime;
protected Time mTime;
protected boolean isRound;
protected boolean mLowBitAmbient;
protected boolean mRegisteredService = false;
protected int mBackgroundColor;
protected int mBackgroundDefaultColor;
protected int mRequireInterval;
protected int mTemperature = Integer.MAX_VALUE;
protected int mTemperatureScale;
protected long mWeatherInfoReceivedTime;
protected long mWeatherInfoRequiredTime;
private String mName;
public WeatherWatchFaceEngine(String name) {
mName = name;
mGoogleApiClient = new GoogleApiClient.Builder(WeatherWatchFaceService.this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Wearable.API)
.build();
}
@Override
public void onConnected(Bundle bundle) {
log("Connected: " + bundle);
getConfig();
Wearable.NodeApi.addListener(mGoogleApiClient, this);
Wearable.DataApi.addListener(mGoogleApiClient, this);
requireWeatherInfo();
}
@Override
public void onConnectionSuspended(int i) {
log("ConnectionSuspended: " + i);
}
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (int i = 0; i < dataEvents.getCount(); i++) {
DataEvent event = dataEvents.get(i);
DataMap dataMap = DataMap.fromByteArray(event.getDataItem().getData());
log("onDataChanged: " + dataMap);
fetchConfig(dataMap);
}
}
@Override
public void onPeerConnected(Node node) {
log("PeerConnected: " + node);
requireWeatherInfo();
}
@Override
public void onPeerDisconnected(Node node) {
log("PeerDisconnected: " + node);
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
log("ConnectionFailed: " + connectionResult);
}
@Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
setWatchFaceStyle(new WatchFaceStyle.Builder(WeatherWatchFaceService.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
.setAmbientPeekMode(WatchFaceStyle.AMBIENT_PEEK_MODE_HIDDEN)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
mResources = WeatherWatchFaceService.this.getResources();
mAsserts = WeatherWatchFaceService.this.getAssets();
mDebugInfoPaint = new Paint();
mDebugInfoPaint.setColor(Color.parseColor("White"));
mDebugInfoPaint.setTextSize(20);
mDebugInfoPaint.setAntiAlias(true);
mTime = new Time();
mSunriseTime = new Time();
mSunsetTime = new Time();
mRequireInterval = mResources.getInteger(R.integer.weather_default_require_interval);
mWeatherInfoRequiredTime = System.currentTimeMillis() - (DateUtils.SECOND_IN_MILLIS * 58);
mGoogleApiClient.connect();
}
@Override
public void onDestroy() {
log("Destroy");
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
super.onDestroy();
}
@Override
public void onInterruptionFilterChanged(int interruptionFilter) {
super.onInterruptionFilterChanged(interruptionFilter);
log("onInterruptionFilterChanged: " + interruptionFilter);
}
@Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
mLowBitAmbient = properties.getBoolean(WatchFaceService.PROPERTY_LOW_BIT_AMBIENT, false);
log("onPropertiesChanged: LowBitAmbient=" + mLowBitAmbient);
}
@Override
public void onTimeTick() {
super.onTimeTick();
log("TimeTick");
invalidate();
requireWeatherInfo();
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
log("onVisibilityChanged: " + visible);
if (visible) {
mGoogleApiClient.connect();
registerTimeZoneService();
// Update time zone in case it changed while we weren't visible.
mTime.clear(TimeZone.getDefault().getID());
mTime.setToNow();
} else {
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
Wearable.DataApi.removeListener(mGoogleApiClient, this);
Wearable.NodeApi.removeListener(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
unregisterTimeZoneService();
}
// 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();
}
protected Paint createTextPaint(int color, Typeface typeface) {
Paint paint = new Paint();
paint.setColor(color);
if (typeface != null)
paint.setTypeface(typeface);
paint.setAntiAlias(true);
return paint;
}
protected boolean shouldUpdateTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
protected void fetchConfig(DataMap config) {
if (config.containsKey(Consts.KEY_WEATHER_UPDATE_TIME)) {
mWeatherInfoReceivedTime = config.getLong(Consts.KEY_WEATHER_UPDATE_TIME);
}
if (config.containsKey(Consts.KEY_WEATHER_CONDITION)) {
String cond = config.getString(Consts.KEY_WEATHER_CONDITION);
if (TextUtils.isEmpty(cond)) {
mWeatherCondition = null;
} else {
mWeatherCondition = cond;
}
}
if (config.containsKey(Consts.KEY_WEATHER_TEMPERATURE)) {
mTemperature = config.getInt(Consts.KEY_WEATHER_TEMPERATURE);
if (mTemperatureScale != ConverterUtil.FAHRENHEIT) {
mTemperature = ConverterUtil.convertFahrenheitToCelsius(mTemperature);
}
}
if (config.containsKey(Consts.KEY_WEATHER_SUNRISE)) {
mSunriseTime.set(config.getLong(Consts.KEY_WEATHER_SUNRISE) * 1000);
log("SunriseTime: " + mSunriseTime);
}
if (config.containsKey(Consts.KEY_WEATHER_SUNSET)) {
mSunsetTime.set(config.getLong(Consts.KEY_WEATHER_SUNSET) * 1000);
log("SunsetTime: " + mSunsetTime);
}
if (config.containsKey(Consts.KEY_CONFIG_TEMPERATURE_SCALE)) {
int scale = config.getInt(Consts.KEY_CONFIG_TEMPERATURE_SCALE);
if (scale != mTemperatureScale) {
if (scale == ConverterUtil.FAHRENHEIT) {
mTemperature = ConverterUtil.convertCelsiusToFahrenheit(mTemperature);
} else {
mTemperature = ConverterUtil.convertFahrenheitToCelsius(mTemperature);
}
}
mTemperatureScale = scale;
}
if (config.containsKey(Consts.KEY_CONFIG_THEME)) {
mTheme = config.getInt(Consts.KEY_CONFIG_THEME);
}
if (config.containsKey(Consts.KEY_CONFIG_TIME_UNIT)) {
mTimeUnit = config.getInt(Consts.KEY_CONFIG_TIME_UNIT);
}
if (config.containsKey(Consts.KEY_CONFIG_REQUIRE_INTERVAL)) {
mRequireInterval = config.getInt(Consts.KEY_CONFIG_REQUIRE_INTERVAL);
}
invalidate();
}
protected void getConfig() {
log("Start getting Config");
Wearable.NodeApi.getLocalNode(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetLocalNodeResult>() {
@Override
public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
Uri uri = new Uri.Builder()
.scheme("wear")
.path(Consts.PATH_CONFIG + mName)
.authority(getLocalNodeResult.getNode().getId())
.build();
getConfig(uri);
uri = new Uri.Builder()
.scheme("wear")
.path(Consts.PATH_WEATHER_INFO)
.authority(getLocalNodeResult.getNode().getId())
.build();
getConfig(uri);
}
});
}
protected void getConfig(Uri uri) {
Wearable.DataApi.getDataItem(mGoogleApiClient, uri)
.setResultCallback(
new ResultCallback<DataApi.DataItemResult>() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
log("Finish Config: " + dataItemResult.getStatus());
if (dataItemResult.getStatus().isSuccess() && dataItemResult.getDataItem() != null) {
fetchConfig(DataMapItem.fromDataItem(dataItemResult.getDataItem()).getDataMap());
}
}
}
);
}
protected void log(String message) {
Log.d(WeatherWatchFaceService.this.getClass().getSimpleName(), message);
}
protected void registerTimeZoneService() {
//TimeZone
if (mRegisteredService) {
return;
}
mRegisteredService = true;
// TimeZone
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
WeatherWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
}
protected void requireWeatherInfo() {
if (!mGoogleApiClient.isConnected())
return;
long timeMs = System.currentTimeMillis();
// The weather info is still up to date.
if ((timeMs - mWeatherInfoReceivedTime) <= mRequireInterval)
return;
// Try once in a min.
if ((timeMs - mWeatherInfoRequiredTime) <= DateUtils.MINUTE_IN_MILLIS)
return;
mWeatherInfoRequiredTime = timeMs;
Wearable.MessageApi.sendMessage(mGoogleApiClient, "", Consts.PATH_WEATHER_REQUIRE, null)
.setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
@Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
log("SendRequireMessage:" + sendMessageResult.getStatus());
}
});
}
protected void unregisterTimeZoneService() {
if (!mRegisteredService) {
return;
}
mRegisteredService = false;
//TimeZone
WeatherWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
}
protected void updateTimer() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldUpdateTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
}
}
任何帮助将不胜感激:)(这也是我第一次真正制作表盘,所以请原谅我)