ARTICLE DETAIL

资讯详情

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

希沃六代机侧边栏抽选插件开发:辅助功能服务实现随机点名

希沃六代机侧边栏抽选插件开发:辅助功能服务实现随机点名 1. 六代希沃一体机侧边栏抽选功能的需求拆解1.1 为什么要在侧边栏做随机抽选六代希沃一体机在教室里的使用频率极高老师上课最常用的两个动作一个是调出白板书写另一个就是点名或者分组抽人回答问题。原厂系统自带的随机抽选功能通常藏在工具栏的二级菜单里点开要经过两到三次跳转一节课下来光是找按钮就浪费不少时间。侧边栏是常驻在屏幕边缘的一条快捷区域把抽选功能挂到侧边栏上等于把最常用的工具从抽屉里拿出来摆在桌面上一步就能点到。这个需求本质上属于系统级快捷入口的定制不是简单的装个软件。六代希沃一体机底层是定制化的安卓系统侧边栏属于系统UI的一部分普通应用没有权限直接往里塞按钮。所以整个项目的核心难点在于如何在不破坏原系统稳定性的前提下把自定义功能注入到侧边栏的交互流程里。适合阅读这篇内容的人包括学校的信息技术老师、负责班班通设备维护的运维人员、以及想给教室一体机做功能扩展的开发者。哪怕你之前没接触过安卓系统定制只要跟着思路走也能理解整个方案的来龙去脉。1.2 六代机与老机型的关键差异六代希沃一体机相比前几代最大的变化是系统权限收得更紧侧边栏的组件化程度更高。老款机型可以通过替换系统APK的方式直接修改侧边栏六代机如果这么做大概率会触发系统签名校验导致侧边栏直接崩溃甚至整机卡在开机动画。我实测过几台不同批次的六代机发现侧边栏的包名和组件结构在不同系统版本之间还有细微差别。有的批次侧边栏是独立进程有的批次已经合并到SystemUI里面。这就意味着不能拿一个方案通吃所有机器必须先确认目标机器的系统版本和侧边栏实现方式。注意动手之前一定要先查清楚机器的具体型号和系统版本号路径在“设置-关于本机”里面。不同版本对应的操作方式可能完全不同盲目操作有风险。1.3 插件化思路的引入既然不能直接改系统那就换一条路用插件的方式挂载。所谓插件化就是写一个独立的安卓应用通过系统提供的辅助功能服务或者悬浮窗权限在侧边栏区域绘制一个自己的按钮点击后弹出抽选界面。这个方案的好处是不动系统文件卸载插件就能完全恢复原状风险可控。热词里提到的“插件”这个词在这个场景下非常贴切。它不是一个传统意义上的浏览器插件而是一个利用安卓系统开放能力实现的轻量级功能模块。你可以把它理解成给侧边栏贴了一张便利贴便利贴上写着你想要的功能撕掉便利贴桌子还是原来的桌子。2. 核心实现方案与技术选型2.1 三种可行路径的对比在六代希沃一体机上实现侧边栏抽选我梳理出三条路每条路的门槛和风险都不一样。方案实现方式优点缺点适用人群辅助功能服务利用AccessibilityService监听侧边栏区域绘制悬浮按钮不需要root权限相对好申请需要引导用户手动开启辅助功能大多数老师和技术人员悬浮窗叠加申请SYSTEM_ALERT_WINDOW权限在侧边栏位置画按钮实现简单兼容性好部分系统版本会限制悬浮窗层级有安卓开发基础的人系统APK替换直接修改侧边栏APK并重新签名体验最原生风险极高容易变砖专业ROM开发者对于绝大多数教室场景我推荐走辅助功能服务这条路。它平衡了功能完整性和安全性而且希沃六代机对辅助功能的限制不算严格开启之后可以稳定运行。2.2 辅助功能服务的注册与配置辅助功能服务的核心是一个继承自AccessibilityService的类在onServiceConnected回调里做初始化。关键配置在res/xml/accessibility_service_config.xml文件里需要声明监听的应用包名和事件类型。accessibility-service android:accessibilityEventTypestypeWindowStateChanged|typeWindowContentChanged android:accessibilityFeedbackTypefeedbackGeneric android:accessibilityFlagsflagDefault|flagRetrieveInteractiveWindows android:canRetrieveWindowContenttrue android:notificationTimeout100 android:packageNamescom.seewo.sidebar /这里packageNames要填希沃侧边栏的实际包名不同版本可能不一样可以用adb shell dumpsys window | grep mCurrentFocus命令在侧边栏展开时抓取。notificationTimeout设成100毫秒是为了让响应更跟手设太大按钮会出现明显延迟。2.3 悬浮按钮的绘制与定位按钮的绘制用WindowManager来实现类型选TYPE_ACCESSIBILITY_OVERLAY这个类型在辅助功能服务里可以直接使用不需要额外申请悬浮窗权限。定位逻辑要考虑到侧边栏的展开和收起状态侧边栏收起时按钮贴在屏幕右边缘展开时按钮跟着侧边栏一起移动。WindowManager.LayoutParams params new WindowManager.LayoutParams( dpToPx(48), dpToPx(48), WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); params.gravity Gravity.TOP | Gravity.END; params.y screenHeight / 3;按钮的纵向位置我习惯放在屏幕高度三分之一处这个位置老师伸手就能够到又不会挡住侧边栏原有的功能图标。按钮的图标建议用矢量图在不同分辨率的一体机上都能保持清晰。2.4 抽选逻辑的数据结构设计抽选功能本身不复杂但要做到“不重复抽同一个人”和“抽完一轮自动重置”需要在数据结构上花点心思。我用一个ArrayList存学生名单再用一个Set记录已经抽过的索引。每次抽选时从Set的补集里随机取一个取完加入Set。当Set的大小等于名单大小时清空Set重新开始。private ListString studentList new ArrayList(); private SetInteger pickedIndices new HashSet(); private String pickOne() { if (pickedIndices.size() studentList.size()) { pickedIndices.clear(); } ListInteger remaining new ArrayList(); for (int i 0; i studentList.size(); i) { if (!pickedIndices.contains(i)) { remaining.add(i); } } int idx remaining.get(new Random().nextInt(remaining.size())); pickedIndices.add(idx); return studentList.get(idx); }这个逻辑比单纯用Random简单粗暴地抽要靠谱得多。我见过有老师用纯随机抽选结果一节课抽了同一个学生三次场面一度很尴尬。用Set记录的方式可以保证一轮之内每个人被抽到的概率完全均等。3. 完整实操流程与关键环节3.1 开发环境搭建与项目初始化开发工具用Android Studio版本建议用2022年之后的稳定版。新建项目时选择“Empty Views Activity”最低SDK版本设成Android 8.0API 26因为希沃六代机的系统底层是安卓9到安卓11之间设太低没必要设太高可能装不上。项目建好之后在AndroidManifest.xml里注册辅助功能服务service android:name.SidebarPickService android:exportedtrue android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE intent-filter action android:nameandroid.accessibilityservice.AccessibilityService / /intent-filter meta-data android:nameandroid.accessibilityservice android:resourcexml/accessibility_service_config / /serviceexported必须设成true否则系统找不到这个服务。permission那行是固定写法不能改。3.2 学生名单的导入与管理名单的导入方式我设计了两种一种是在应用界面里手动输入用逗号或者换行分隔另一种是读取本地txt文件文件放在/sdcard/Download/student_list.txt。第二种方式更适合老师因为可以在电脑上编辑好再拷到一体机上。private void loadFromFile() { File file new File(Environment.getExternalStorageDirectory(), Download/student_list.txt); if (!file.exists()) return; try (BufferedReader br new BufferedReader(new FileReader(file))) { String line; while ((line br.readLine()) ! null) { line line.trim(); if (!line.isEmpty()) { studentList.add(line); } } } catch (IOException e) { e.printStackTrace(); } }文件编码要用UTF-8否则中文名字会变成乱码。我踩过这个坑用Windows记事本默认的ANSI编码保存导入进去全是问号排查了半天才发现是编码问题。3.3 抽选动画与结果展示抽选结果如果直接弹出来课堂效果会差很多。加一个滚动动画让名字快速跳动两三秒再定格学生的注意力会被完全吸引过来。动画用ValueAnimator实现在onAnimationUpdate回调里不断刷新TextView的文字。ValueAnimator animator ValueAnimator.ofInt(0, 30); animator.setDuration(2500); animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(animation - { int frame (int) animation.getAnimatedValue(); if (frame 28) { resultText.setText(studentList.get( new Random().nextInt(studentList.size()))); } else { resultText.setText(pickedName); } }); animator.start();减速插值器让动画前快后慢最后定格的时候有一种“揭晓”的仪式感。实测下来这个两秒多的动画是课堂效果最好的时长太短没感觉太长学生就失去耐心了。3.4 侧边栏联动与状态同步按钮要跟着侧边栏的状态走侧边栏展开时按钮右移收起时按钮贴边。这个通过监听侧边栏的窗口状态变化来实现。在onAccessibilityEvent里判断事件类型如果是TYPE_WINDOW_STATE_CHANGED并且包名是希沃侧边栏就读取侧边栏的窗口位置更新按钮的x坐标。Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { if (com.seewo.sidebar.equals(event.getPackageName())) { Rect rect new Rect(); event.getSource().getBoundsInScreen(rect); updateButtonPosition(rect.left); } } }updateButtonPosition里用WindowManager.updateViewLayout来移动按钮。这里要注意更新布局的操作必须在主线程执行如果在回调线程里直接调用会抛异常。3.5 开机自启与权限保持教室一体机每天关机重启插件必须能自动启动。辅助功能服务本身在系统设置里开启之后是持久化的重启不会丢失。但悬浮按钮需要在服务连接时重新创建这个在onServiceConnected里做就行。另外要引导老师把应用加入电池优化白名单否则系统可能在后台把服务杀掉。路径在“设置-应用管理-本应用-电池-不优化”。六代机的电池管理策略不算激进但加上白名单更保险。4. 常见问题与排查技巧实录4.1 按钮不显示或位置错乱这是反馈最多的问题。排查顺序是这样的先确认辅助功能服务是否真的开启了在“设置-辅助功能-已安装的服务”里看状态然后确认悬浮窗层级是否被系统限制有些系统版本会把TYPE_ACCESSIBILITY_OVERLAY的层级压得很低被侧边栏盖住。如果是层级问题可以尝试改用TYPE_APPLICATION_OVERLAY但需要额外申请悬浮窗权限。位置错乱通常是屏幕旋转或者分辨率变化导致的。六代机有些型号支持横竖屏切换切换之后要重新计算按钮坐标。我在onConfigurationChanged里加了重新定位的逻辑切换屏幕方向后按钮位置就正常了。4.2 抽选结果重复出现前面说了用Set记录已抽索引可以解决一轮内的重复问题。但有一种情况例外如果老师在抽选过程中手动修改了名单比如临时加了一个学生Set里的索引就和名单对不上了。我的处理方式是在名单变更时清空Set重新开始一轮。虽然这样会丢失之前的抽选记录但总比抽到错误的人要好。还有一种隐蔽的重复如果名单里有重名的学生抽选结果显示出来是一样的名字看起来像重复了。这个不是逻辑问题是数据问题。建议在导入名单时自动检测重名并提示或者在名字后面加上学号后缀。4.3 服务被系统回收安卓系统在内存紧张时会回收后台服务辅助功能服务虽然优先级较高但也不是完全免疫。我遇到过一体机连续播放视频一小时后抽选按钮消失的情况。解决办法是在onServiceConnected里注册一个前台通知把服务提升为前台服务级别。通知内容可以写“抽选功能运行中”对老师来说也是一种状态提示。private void startForegroundNotification() { NotificationChannel channel new NotificationChannel( pick_channel, 抽选服务, NotificationManager.IMPORTANCE_LOW); NotificationManager nm getSystemService(NotificationManager.class); nm.createNotificationChannel(channel); Notification notification new Notification.Builder(this, pick_channel) .setContentTitle(抽选功能运行中) .setSmallIcon(R.drawable.ic_pick) .build(); startForeground(1, notification); }IMPORTANCE_LOW让通知不发出声音不会打扰上课。4.4 常见问题速查表现象可能原因排查方法解决方式按钮完全不显示辅助功能未开启检查设置里的服务状态重新开启服务按钮被侧边栏遮挡悬浮窗层级过低用布局边界查看层级改用TYPE_APPLICATION_OVERLAY点击按钮无反应服务被回收查看应用是否还在运行加前台通知保活抽选结果乱码文件编码不对用十六进制查看文件头转成UTF-8编码重启后功能失效电池优化杀进程查看电池使用记录加入白名单动画卡顿主线程阻塞用Profiler看帧率把名单加载放到子线程4.5 实操心得与避坑建议第一个心得是关于按钮大小的。我一开始设的是36dp觉得小巧不挡事但实际用起来老师反馈说太小了手指点不准。后来改成48dp点击准确率明显提升。一体机屏幕大按钮适当大一点反而更好用。第二个心得是关于抽选范围的。有些老师想按小组抽有些想按性别抽我在设置里加了筛选条件可以按标签过滤名单。这个功能看起来简单但实际用起来很受欢迎尤其是分组教学的时候。第三个心得是关于数据备份的。名单数据我存在SharedPreferences里但SharedPreferences在应用卸载后会丢失。建议加一个导出功能把名单存成txt文件放到Download目录换机器或者重装的时候直接导入就行。提示所有操作建议先在非教学时间测试确认稳定后再投入课堂使用。上课途中出问题影响的是几十个学生的宝贵时间。5. 功能扩展与长期维护思路5.1 从抽选延伸到课堂互动抽选功能跑通之后侧边栏这个入口还可以挂更多东西。比如课堂计时器、随机分组、积分记录。这些功能的实现逻辑和抽选类似都是轻量级的交互工具挂在侧边栏上比藏在系统菜单里实用得多。我目前正在把积分记录加进去学生被抽到回答问题后可以一键加分期末统计的时候直接导出表格。5.2 多机型适配的注意事项希沃六代机有不同的配置版本侧边栏的包名和窗口行为可能有差异。我的做法是在应用启动时自动检测当前机型的侧边栏包名检测方法是用PackageManager查询已安装应用里包含“sidebar”关键词的包。检测到之后把包名存到配置里后续监听就用这个包名。这样一套代码可以适配多个批次不用为每个批次单独打包。5.3 版本更新与系统升级的应对希沃会不定期推送系统更新更新之后侧边栏的实现方式可能变化导致插件失效。我的应对策略是保持插件的独立性尽量不依赖侧边栏的内部实现细节只依赖窗口状态变化这个通用事件。这样即使侧边栏改版只要它还是一个窗口插件就能继续工作。另外建议关注系统更新日志如果更新说明里提到侧边栏改动提前在测试机上验证一下。5.4 实际使用中的体会我在三所学校的六代机上部署了这个方案运行了一个学期整体稳定性不错。最让我意外的是老师们自发开发出了新用法有的用抽选功能做课堂提问有的用来选值日生还有的用来随机分组做小组竞赛。一个简单的工具到了实际场景里用法会比设计者想的多得多。最后分享一个小技巧如果学校有多台一体机可以把配置好的应用导出APK用U盘批量安装省去每台机器重新配置的麻烦。安装完之后只需要在每台机器上手动开启一次辅助功能服务就行其他配置项可以通过导入配置文件的方式同步。
返回列表