ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android Wear 自定义表盘开发完全指南:从设计、服务实现到性能优化(android-training-course-in-chinese 实战教程)

Android Wear 自定义表盘开发完全指南:从设计、服务实现到性能优化(android-training-course-in-chinese 实战教程) 文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载本文以 android-training-course-in-chinese 仓库的 wearables/watch-faces 课程为主线系统讲解在 Android Wear 设备上创建自定义表盘watch face的全流程。表盘本质上是运行在可穿戴设备上的动态数字画布用颜色、动画与上下文信息表达时间。阅读完本文你将掌握表盘工程配置、CanvasWatchFaceService服务实现、绘制与系统 UI 协调、配置 Activity 接入以及节能优化等完整实战技能。一、表盘开发的整体蓝图Android Wear 的表盘是一个动态的数字画布它通过颜色、动画和相关的上下文信息来表示时间。Android Wear 配套应用companion app为用户提供了不同风格和形状的表盘当用户选择可穿戴设备应用或配套应用上可用的表盘时可穿戴设备会提供表盘的预览并让用户设置选项。Android Wear 允许开发者创建自定义表盘。当用户安装一个包含表盘的可穿戴应用的手持式应用时表盘会出现在手持设备上的 Android Wear 配套应用中以及可穿戴设备上的表盘选择器里。这正是 创建可穿戴的应用 课程中提到的打包机制——用户不会直接将应用下载到可穿戴设备而是将可穿戴应用打包进手持应用安装手持应用时系统自动安装可穿戴部分。本节课程由以下 6 个 Lesson 组成本文将其完整展开Lesson主题核心内容设计表盘设计准则与实现策略屏幕形状适配、显示模式、实现方案选型构建表盘服务工程配置与服务实现依赖、权限、CanvasWatchFaceService回调绘制表盘初始化与绘制资源加载、定时器、系统 UI、模式切换、onDraw()在表盘上显示信息上下文数据集成数据获取任务、自定义定时器、重绘提供配置 Activity用户客制化Intent 元数据、可穿戴/配套配置界面定位常见的问题兼容性适配屏幕形状检测、Card 容纳、系统指示图标、相对尺寸优化性能和电池使用时间性能与节能位图裁剪、滤镜、帧率、CPU 睡眠开发工具建议本课程推荐使用 Android Studio 进行 Android Wear 开发因为它提供了工程初始配置、库包含和方便的打包流程这些功能在 ADTEclipse中是没有的。课程中的全部教程均假定使用 Android Studio。二、设计表盘准则与实现策略设计 Android Wear 表盘类似于设计传统表盘是一场清晰地显示时间的练习。但 Wear 设备为表盘提供了更多高级功能鲜艳的色彩、动态的背景、动画和数据整合。因此必须同时考虑更多的设计因素。2.1 六大设计准则为方形和圆形的设备作出规划设计应该可以运行在方形和圆形的 Android Wear 设备上包括那些使用感知形状的 LayoutBoxInsetLayout的设备。在 wearables/ui/layouts.md 中介绍的BoxInsetLayout继承自FrameLayout通过app:layout_box属性取值如left|top、all让子 view 自动落在圆形屏幕的安全区域内方形屏幕上窗口间隔为 0layout_box会被忽略。支持所有的显示模式表盘必须同时支持有限颜色的环境模式ambient mode和全彩色动画的交互模式interactive mode。优化特殊屏幕的技术在环境模式下表盘应让大部分像素保持黑色。根据屏幕技术需要避免使用大块白色像素仅使用黑色和白色并禁用抗锯齿anti-aliasing。容纳系统 UI 组件设计应确保系统指示图标可见当 notification cards 出现在屏幕上时用户依然能看到时间。整合数据表盘可以利用配套手机设备上的传感器和蜂窝数据连接显示天气、下一个日程事件等上下文数据。提供设置选项让用户可在可穿戴应用或 Android Wear 配套应用上配置表盘的某些设计特征如颜色和尺寸。2.2 创建实现策略完成设计后需要决定如何获得数据并完成绘制。大部分实现方案由三部分组成一幅或多幅背景图片接收所需数据的应用代码在背景图片上绘制文本和形状的应用代码实现策略的关键决策点如下双背景策略一般在交互模式和环境模式使用两幅不同的背景图片环境模式背景通常为全黑。屏幕密度Android Wear 设备的屏幕密度hdpi应设计为320×320 像素以同时兼容方形和圆形设备。背景图片的四角在圆形设备上不可见。按需缩放代码中可以检测设备屏幕尺寸若分辨率低于图片则按比例缩小背景。为了提升性能只对背景图片缩放一次并保存缩放后的 bitmap这正是后续 绘制表盘 中mBackgroundScaledBitmap缓存机制的由来。数据获取节流仅在需要时运行代码检索上下文数据并保存结果使每次绘制时可重用数据——例如不必每隔一分钟刷新一次天气。模式差异化绘制为延长电池寿命环境模式下的绘制代码应相对简单通常用一组有限颜色绘制形状轮廓交互模式才使用全色彩、复杂形状、渐变和动画。三、构建表盘服务工程配置与核心回调表盘在可穿戴应用中实现为服务Service并随应用打包。当用户安装包含表盘的手持式应用后表盘在配套应用和可穿戴表盘选择器中可用用户选择后可穿戴设备显示表盘并按需调用其服务的回调方法。3.1 在 Android Studio 中创建工程按以下步骤创建包含wear和mobile两个模块的工程打开 Android Studio。创建新工程Welcome界面点击New Project或在File菜单选择New Project。填写应用名点击Next。选择Phone and Tablet尺寸系数。在Minimum SDK下拉菜单选择API 18。选择Wear尺寸系数。在Minimum SDK下拉菜单选择API 21点击Next。选择Add No Activity在接下来的两个界面点击Next。点击Finish。在 IDE 窗口点击View Tool Windows Project。工程创建完成后Android Studio 会自动向build.gradle添加所需依赖条目其中包含Wearable Support 库提供可继承以实现表盘的基类CanvasWatchFaceService等Google Play services client 库play-services与play-services-wearable用于在配套设备和可穿戴应用之间通过 可穿戴数据层 API 同步数据项。若使用 Eclipse ADT需单独下载 Wearable Support 库并将其作为依赖包含进工程。3.2 声明权限表盘需要PROVIDE_BACKGROUND和WAKE_LOCK两项权限需在可穿戴应用和手持式应用的 manifest 文件manifest节点下同时声明manifest ... uses-permission android:namecom.google.android.permission.PROVIDE_BACKGROUND / uses-permission android:nameandroid.permission.WAKE_LOCK / ... /manifestCaution:手持式应用必须包含所有在可穿戴应用中声明的权限。3.3 实现服务和回调方法表盘实现为服务当表盘处于活动状态时系统会在时间改变或出现重要事件如切换到环境模式、收到新通知时调用服务方法服务实现据此将表盘绘制到屏幕上。实现一个表盘需要继承CanvasWatchFaceService与CanvasWatchFaceService.Engine并重写 Engine 的回调方法这些类都包含在 Wearable Support 库中。核心骨架如下public class AnalogWatchFaceService extends CanvasWatchFaceService { Override public Engine onCreateEngine() { /* provide your watch face implementation */ return new Engine(); } /* implement service callback methods */ private class Engine extends CanvasWatchFaceService.Engine { Override public void onCreate(SurfaceHolder holder) { super.onCreate(holder); /* initialize your watch face */ } Override public void onPropertiesChanged(Bundle properties) { super.onPropertiesChanged(properties); /* get device features (burn-in, low-bit ambient) */ } Override public void onTimeTick() { super.onTimeTick(); /* the time changed */ } Override public void onAmbientModeChanged(boolean inAmbientMode) { super.onAmbientModeChanged(inAmbientMode); /* the wearable switched between modes */ } Override public void onDraw(Canvas canvas, Rect bounds) { /* draw your watch face */ } Override public void onVisibilityChanged(boolean visible) { super.onVisibilityChanged(visible); /* the watch face became visible or invisible */ } } }Note:Android SDK 中的WatchFace示例示范了如何通过继承CanvasWatchFaceService实现模拟和数字表盘示例位于android-sdk/samples/android-21/wearable/WatchFace目录。CanvasWatchFaceService还提供了类似View.invalidate()的重绘机制在主 UI 线程中调用invalidate()从其它线程调用postInvalidate()即可请求系统重新绘制表盘。3.4 注册表盘服务在可穿戴应用的 manifest 文件中注册服务实现系统会在安装后使用该服务信息使表盘在配套应用和表盘选择器中可用。完整注册代码service android:name.AnalogWatchFaceService android:labelstring/analog_name android:allowEmbeddedtrue android:taskAffinity android:permissionandroid.permission.BIND_WALLPAPER meta-data android:nameandroid.service.wallpaper android:resourcexml/watch_face / meta-data android:namecom.google.android.wearable.watchface.preview android:resourcedrawable/preview_analog / meta-data android:namecom.google.android.wearable.watchface.preview_circular android:resourcedrawable/preview_analog_circular / intent-filter action android:nameandroid.service.wallpaper.WallpaperService / category android:name com.google.android.wearable.watchface.category.WATCH_FACE / /intent-filter /service关键元数据说明com.google.android.wearable.watchface.preview定义表盘预览图。配套应用和表盘选择器在展示表盘时使用该预览图可运行 Wear 设备或模拟器上的表盘并截图获取。hdpi 屏幕的 Wear 设备上预览图通常为320×320 像素。com.google.android.wearable.watchface.preview_circular圆形预览图。若表盘同时包含圆形和方形预览图配套应用和表盘选择器会按手表形状选择合适的一张若没有圆形预览图方形和圆形设备都会使用方形预览图圆形设备上会被圆形裁剪。android.service.wallpaper指定包含wallpaper节点的watch_face.xml资源文件?xml version1.0 encodingUTF-8? wallpaper xmlns:androidhttp://schemas.android.com/apk/res/android /可穿戴应用可以包含多个表盘——必须为每个表盘实现分别添加一个 service 节点。四、绘制表盘初始化、绘制与系统 UI 协调配置好工程并添加表盘服务类后就可以编写初始化和绘制代码了。本节以 Android SDKWatchFace示例中的AnalogWatchFaceService为例讲解系统如何调用表盘服务方法。示例中的大部分服务实现如初始化、检测设备功能可复用到任意表盘。4.1 初始化表盘系统加载服务时应分配并初始化表盘需要的大部分资源加载位图资源、创建自定义动画定时器、配置颜色风格等。这些操作通常只执行一次并复用结果以提升性能、方便维护。初始化分三步声明变量 → 在Engine.onCreate()中初始化组件 → 在Engine.onVisibilityChanged()中初始化自定义定时器。第一步声明变量。需要在Engine实现中为可复用资源声明成员变量包括图形对象背景位图、时钟指针等位图资源自定义定时器系统每分钟通知一次时间变化但自定义动画需要按更高频率刷新需用Handler实现定时器时区变化接收器用户旅行调整时区时系统会广播事件服务必须注册广播接收器以更新时间。WatchFace示例的变量声明自定义定时器通过Handler每秒计数一次计数时调用invalidate()触发重绘private class Engine extends CanvasWatchFaceService.Engine { static final int MSG_UPDATE_TIME 0; /* a time object */ Time mTime; /* 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) { mTime.clear(intent.getStringExtra(time-zone)); mTime.setToNow(); } }; /* service methods (see other sections) */ ... }第二步在Engine.onCreate()中初始化组件。包括加载背景图片、创建绘制图形对象的风格和色彩、分配保存时间的对象、配置系统 UIOverride 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); 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 an object to hold the time */ mTime new Time(); }背景位图在系统初始化表盘时只加载一次图形风格用Paint实例化之后在onDraw()中复用这些风格绘制组件。第三步初始化自定义定时器。通过定时器的计数频率开发者决定交互模式下表盘的更新频率从而支持自定义动画和视觉效果。Note:在环境模式下系统不会可靠地调用自定义定时器。环境模式的更新应依赖onTimeTick()。定时器的启动条件是表盘可见 且 设备处于交互模式private void updateTimer() { mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME); if (shouldTimerBeRunning()) { mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME); } } private boolean shouldTimerBeRunning() { return isVisible() !isInAmbientMode(); }在Engine.onVisibilityChanged()中启动定时器并注册时区接收器Override public void onVisibilityChanged(boolean visible) { super.onVisibilityChanged(visible); if (visible) { registerReceiver(); // Update time zone in case it changed while we werent visible. mTime.clear(TimeZone.getDefault().getID()); mTime.setToNow(); } else { unregisterReceiver(); } // Whether the timer should be running depends on whether were visible and // whether were in ambient mode), so we may need to start or stop the timer updateTimer(); }registerReceiver()/unregisterReceiver()的实现如下注册Intent.ACTION_TIMEZONE_CHANGED过滤器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); }4.2 在环境模式下更新表盘环境模式下系统每分钟调用一次Engine.onTimeTick()通常每分钟更新一次已足够。大部分表盘实现只需在onTimeTick()中重绘Override public void onTimeTick() { super.onTimeTick(); invalidate(); }4.3 配置系统 UI表盘不应干涉系统 UI 组件。若表盘背景较亮或在屏幕底部附近显示信息可能需要配置 notification cards 的尺寸或启用背景保护。Android Wear 允许表盘配置以下几个方面指定第一个 notification card 离屏幕的距离指定系统是否将时间绘制在表盘上在环境模式下显示或隐藏 notification card用纯色背景保护系统指针指定系统指针的位置。创建WatchFaceStyle实例并传入Engine.setWatchFaceStyle()方法Override public void onCreate(SurfaceHolder holder) { super.onCreate(holder); /* configure the system UI */ setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this) .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT) .setBackgroundVisibility(WatchFaceStyle .BACKGROUND_VISIBILITY_INTERRUPTIVE) .setShowSystemUiTime(false) .build()); ... }上述配置将 card 设为一行高、背景仅对中断性通知显示、不显示系统时间因为表盘自己绘制时间。系统 UI 风格可以在表盘实现的任意时刻配置例如用户选择白色背景时可为系统指针添加背景保护。4.4 获得设备屏幕信息系统确定设备屏幕属性时调用Engine.onPropertiesChanged()例如设备是否使用低比特率环境模式、屏幕是否需要烧毁保护Override public void onPropertiesChanged(Bundle properties) { super.onPropertiesChanged(properties); mLowBitAmbient properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false); mBurnInProtection properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false); }绘制时应考虑这两个属性低比特率环境模式屏幕在环境模式下为每种颜色提供更少的比特切换到环境模式时应禁用抗锯齿和位图滤镜烧毁保护环境模式下避免使用大块白色像素且不要将内容放在离屏幕边缘 10 像素范围内系统会周期性移动内容防止像素烧毁。4.5 响应两种模式间的变化设备在环境模式和交互模式间转换时系统调用Engine.onAmbientModeChanged()实现应做出必要调整并调用invalidate()重绘Override public void onAmbientModeChanged(boolean inAmbientMode) { super.onAmbientModeChanged(inAmbientMode); if (mLowBitAmbient) { boolean antiAlias !inAmbientMode; mHourPaint.setAntiAlias(antiAlias); mMinutePaint.setAntiAlias(antiAlias); mSecondPaint.setAntiAlias(antiAlias); mTickPaint.setAntiAlias(antiAlias); } invalidate(); updateTimer(); }4.6 绘制表盘系统调用Engine.onDraw()参数为Canvas实例和绘制区域bounds。bounds说明任意内插的区域如圆形设备底部的下巴。绘制步骤首次调用onDraw()时缩放背景以匹配画布检查设备处于环境模式还是交互模式处理任何图形计算在画布上绘制背景位图使用Canvas方法绘制表盘。Override public void onDraw(Canvas canvas, Rect bounds) { // Update the time mTime.setToNow(); int width bounds.width(); int height bounds.height(); // Draw the background, scaled to fit. if (mBackgroundScaledBitmap null || mBackgroundScaledBitmap.getWidth() ! width || mBackgroundScaledBitmap.getHeight() ! height) { mBackgroundScaledBitmap Bitmap.createScaledBitmap(mBackgroundBitmap, width, height, true /* filter */); } 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 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; // 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); }该实现按当前时间计算指针位置使用onCreate()中初始化的图形风格在背景位图上绘制指针。其中秒针只在交互模式绘制环境模式不显示。五、在表盘上显示上下文信息创建自定义表盘不仅可以用丰富的方式显示时间还可以在用户扫视设备时显示相关信息。表盘可以通过 可穿戴数据层 API 与可穿戴设备上的应用通信。某些情况下需要在工程的手持式应用模块中创建 activity从互联网或用户配置文件检索数据再分享给表盘。5.1 创建丰富的体验设计和实现上下文感知表盘前先回答四个问题想要包含什么类型的数据可以从哪里获得数据数据多久会显著变化如何表达数据使用户瞥一眼就明白含义Wear 设备通常与带 GPS 或蜂窝数据连接的配套设备配对因此可以整合位置、日历事件、社交媒体、图片、股票报价、新闻事件、体育比分等数据。但并非所有类型的数据都适合表盘需要权衡与用户的相关性当没有配对设备或互联网断开时表盘应优雅降级处理。由于表盘是一直在运行的应用必须使用高效节能的方法获取数据。例如每 10 分钟而非每 1 分钟获取一次天气并缓存结果在设备从环境模式切换到交互模式时刷新上下文数据此时用户最可能瞥一眼手表。屏幕空间有限且用户通常只看一两秒因此应归纳上下文信息。有时用图形和颜色表达信息效果最好——例如表盘根据天气改变背景图片。5.2 添加数据到表盘WatchFace示例的CalendarWatchFaceService从用户配置文件获取日程数据显示接下来 24 小时有多少个会议。实现包含上下文数据的表盘需要三步提供检索数据的任务 → 创建自定义定时器周期调用任务或监听外部数据变化 → 用更新数据重绘表盘。第一步提供任务检索数据。在CanvasWatchFaceService.Engine实现中创建继承AsyncTask的类/* Asynchronous task to load the meetings from the content provider and * report the number of meetings back using onMeetingsLoaded() */ private class LoadMeetingsTask extends AsyncTaskVoid, Void, Integer { Override protected Integer doInBackground(Void... voids) { long begin System.currentTimeMillis(); Uri.Builder builder WearableCalendarContract.Instances.CONTENT_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, begin DateUtils.DAY_IN_MILLIS); final Cursor cursor getContentResolver() .query(builder.build(), null, null, null, null); int numMeetings cursor.getCount(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, Num meetings: numMeetings); } return numMeetings; } Override protected void onPostExecute(Integer result) { /* get the number of meetings and set the next timer tick */ onMeetingsLoaded(result); } }Wearable Support 库的WearableCalendarContract类可直接访问配套设备用户的日历事件。任务检索完数据后调用回调方法。第二步创建自定义定时器。用Handler通过线程消息队列发送和处理延时消息周期性更新数据private class Engine extends CanvasWatchFaceService.Engine { ... int mNumMeetings; private AsyncTaskVoid, Void, Integer mLoadMeetingsTask; /* Handler to load the meetings once a minute in interactive mode. */ final Handler mLoadMeetingsHandler new Handler() { Override public void handleMessage(Message message) { switch (message.what) { case MSG_LOAD_MEETINGS: cancelLoadMeetingTask(); mLoadMeetingsTask new LoadMeetingsTask(); mLoadMeetingsTask.execute(); break; } } }; ... }在onVisibilityChanged()中根据可见性启动/停止定时器Override public void onVisibilityChanged(boolean visible) { super.onVisibilityChanged(visible); if (visible) { mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS); } else { mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS); cancelLoadMeetingTask(); } }第三步用更新的数据重绘表盘。任务检索完数据后调用invalidate()将数据保存为Engine成员变量供onDraw()访问private void onMeetingsLoaded(Integer result) { if (result ! null) { mNumMeetings result; invalidate(); } if (isVisible()) { mLoadMeetingsHandler.sendEmptyMessageDelayed( MSG_LOAD_MEETINGS, LOAD_MEETINGS_DELAY_MS); } }回调方法将结果存入成员变量、销毁 view触发重绘并调度下一个定时器再次运行任务。六、提供配置 Activity让用户客制化表盘一些表盘提供配置参数让用户客制化外观和行为例如选择背景颜色、在两个时区中选择感兴趣的时区。配置界面可以是可穿戴应用的 activity、手持应用的 activity或两者皆有——用户可启动可穿戴设备上的配置界面也可启动 Android Wear 配套应用中的配套配置界面。6.1 指定配置 Activity 的 Intent在可穿戴应用 manifest 的服务声明部分添加元数据项声明配置 activity 的 Intentservice android:name.DigitalWatchFaceService ... / !-- companion configuration activity -- meta-data android:name com.google.android.wearable.watchface.companionConfigurationAction android:value com.example.android.wearable.watchface.CONFIG_DIGITAL / !-- wearable configuration activity -- meta-data android:name com.google.android.wearable.watchface.wearableConfigurationAction android:value com.example.android.wearable.watchface.CONFIG_DIGITAL / ... /service元数据值必须在应用的包名之前定义。配置 activity 为此 intent 注册 intent filter系统在用户想配置表盘时启动该 intent。若表盘只包含配套或可穿戴配置 activity 之一只需包含对应的元数据项。6.2 创建可穿戴配置 Activity可穿戴配置 activity 只提供有限的客制化选择复杂的菜单在小屏幕上很难导航应提供二元选择和少量选项覆盖表盘的主要方面。在可穿戴应用 manifest 中声明如下 intent filteractivity android:name.DigitalWatchFaceWearableConfigActivity android:labelstring/digital_config_name intent-filter action android:name com.example.android.wearable.watchface.CONFIG_DIGITAL / category android:name com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION / category android:nameandroid.intent.category.DEFAULT / /intent-filter /activity该 intent filter 的 action 名称必须与 6.1 节定义的 intent 一致。在配置 activity 中构建简单 UI 提供选择用户做出选择后使用可穿戴数据层 API将配置变化传达给表盘。6.3 创建配套配置 Activity配套配置 activity 允许用户访问全套客制化选择手持设备屏幕更大更容易与复杂菜单互动例如显示复杂的颜色选择器。在手持应用 manifest 中声明activity android:name.DigitalWatchFaceCompanionConfigActivity android:labelstring/app_name intent-filter action android:name com.example.android.wearable.watchface.CONFIG_DIGITAL / category android:name com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION / category android:nameandroid.intent.category.DEFAULT / /intent-filter /activity6.4 创建监听器服务为接收配置 activity 更新的配置参数需要在可穿戴应用中创建实现可穿戴数据层 API 的WearableListenerService服务。表盘实现可在配置参数改变时重新绘制表盘。可参考WatchFace示例中的DigitalWatchFaceConfigListenerService与DigitalWatchFaceService类。七、定位常见问题与兼容性适配创建自定义表盘与创建 notification 和可穿戴特有 activity 的方法不同本节解决实现第一个表盘时常见的问题。7.1 检测屏幕形状部分 Wear 设备屏幕为方形部分为圆形圆形设备可能在底部包含插入部分下巴。在运行时通过重写onApplyWindowInsets()检测屏幕形状private class Engine extends CanvasWatchFaceService.Engine { boolean mIsRound; int mChinSize; Override public void onApplyWindowInsets(WindowInsets insets) { super.onApplyWindowInsets(insets); mIsRound insets.isRound(); mChinSize insets.getSystemWindowInsetBottom(); } ... }重新绘制表盘时检查mIsRound和mChinSize的值来适配设计。7.2 容纳 Card收到 notification 时card 可能遮盖屏幕很大一部分取决于 系统 UI 风格。表盘应确保 card 出现时用户仍能看到时间模拟表盘通常需要调整如缩小表盘避免被 card 覆盖数字表盘的显示时间区域通常不会被 card 覆盖一般无需调整使用WatchFaceService.getPeekCardPosition()方法确定 card 上方可用于调整表盘的空间。在环境模式下 card 背景是透明的。若表盘在环境模式下 card 附近包含详细信息可考虑在 card 上方绘制黑色方块确保用户能读到 card 内容。7.3 配置系统指示图标创建WatchFaceStyle实例时可配置系统指示图标的位置与背景保护setStatusBarGravity()设置状态栏的位置setHotwordIndicatorGravity()设置热词的位置setViewProtection()用灰色半透明背景保护状态栏和热词。系统指示图标是白色的若表盘背景明亮则尤其必要。7.4 使用相对尺寸不同厂商的 Wear 设备屏幕尺寸和分辨率不同表盘应使用相对尺寸而非绝对像素值绘制时用Canvas.getWidth()/Canvas.getHeight()获得画布尺寸以屏幕尺寸比例定位图片若重新绘制组件响应 card则根据屏幕里 card 上方剩余空间的比例重新绘制。八、优化性能与电池使用时间表盘在设备上一直运行高效使用电池十分重要。除良好的 notification cards 和系统指示图标外还需确保动画流畅、服务不执行多余计算。8.1 减小位图资源的尺寸每次重绘时onDraw()中的图像组件时钟指针等往往需要旋转甚至缩放。资源越大转换运算量越大大图形资源的转换会大幅降低动画帧率。提升性能需要不要使用比需要更大的图像组件删除边缘周围多余的透明像素有时可减小 97% 的大小。这不仅能提升动画性能也能节省电量。8.2 合并位图资源经常一起绘制的位图可以合并到同一个图形资源中。例如交互模式下将背景图片和计数标记组合避免每次重绘时绘制两个全屏位图。8.3 绘制可缩放位图时禁用抗锯齿使用Canvas.drawBitmap()绘制可缩放位图时可通过Paint实例设置选项。用setAntiAlias()禁用抗锯齿可提升性能——该设置对位图绘制没有任何视觉效果。使用位图滤镜对于绘制在其它组件上的位图资源可在同一Paint实例上用setFilterBitmap()打开位图滤镜放大后的时钟指针边缘会更平滑。Note:在低比特率环境模式下系统不能可靠渲染图片颜色无法保证滤镜效果。因此在环境模式下应禁用位图滤镜。8.4 将复杂操作移到 onDraw() 外系统每次重绘都会调用onDraw()应只放置更新表盘的关键操作避免在其中加载图片和其它资源调整图片大小分配对象运行帧与帧之间不会改变的计算。这些操作通常放在Engine.onCreate()中图片大小调整可在Engine.onSurfaceChanged()之前进行该方法提供画布大小。性能分析可使用 Android Device Monitor重点确保onDraw()实现运行时间短且调用一致。8.5 节能最佳做法降低动画帧频动画消耗大量计算资源和电量。大部分动画在每秒 30 帧下看起来流畅应避免超过 30 fps。让 CPU 睡眠动画和内容的小变化会唤醒 CPU。表盘应在动画之间让 CPU 睡眠——例如交互模式下每隔一秒用动画短脉冲下一秒让 CPU 睡眠。即使短暂的睡眠也能有效降低电量消耗。为最大化电池寿命应谨慎使用动画闪烁动画在闪烁时也会唤醒 CPU 并耗电。监控电量消耗在 Android Wear 配套应用的Settings Watch battery下可查看可穿戴设备中不同进程的电量消耗。Android 5.0 的电池优化可参考 Project Volta 计划。九、课程脉络速览本课程的 6 个 Lesson 构成了一条从设计到发布的完整链路设计表盘先定设计准则与实现策略双背景、320×320 画布、数据节流构建表盘服务工程配置、权限声明、CanvasWatchFaceService骨架与 manifest 注册绘制表盘初始化资源、定时器、系统 UI 配置、屏幕属性、模式切换与onDraw()在表盘上显示信息AsyncTask检索数据 Handler定时刷新 invalidate()重绘提供配置 Activity声明配置 Intent、可穿戴/配套配置界面、WearableListenerService监听定位常见的问题 与 优化性能和电池使用时间屏幕形状适配、Card 容纳、相对尺寸、位图裁剪与节能。配套的可穿戴 UI 指南见 创建自定义布局BoxInsetLayout感知形状布局数据同步机制见 可穿戴数据层应用打包与运行见 创建并运行可穿戴应用。赞分享文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载相关推荐Android Wear 表盘配置 Activity 实战指南从 Intent 元数据到数据层同步android-training-course-in-chineseAndroid Wear 表盘配置 Activity 实战指南从 Intent 元数据到数据层同步android training course in ch文档教程移动开发Android 自定义 View 性能优化实战从 60fps 渲染目标到硬件加速android-training-course-in-chinese 解读Android 自定义 View 性能优化实战从 60fps 渲染目标到硬件加速android training course in chinese 解读文档教程移动开发Android 可穿戴应用开发实战从 Notification 同步到数据层与表盘android-training-course-in-chineseAndroid 可穿戴应用开发实战从 Notification 同步到数据层与表盘android training course in chinese 本文档教程移动开发上一篇从0到1Loggifly与Vaultwarden集成实现登录异常实时告警下一篇copymanga下载管理器深度剖析PausableDownloader与AutoDownloadHandler的工作原理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表