当前位置: 首页 > news >正文

MTK-Android12-系统设置一级菜单-适配遥控器

提示:MTK-Android12-系统设置一级菜单-适配遥控器

文章目录

  • 前言-需求-存在的问题
    • 前言-场景
    • 存在问题
      • 遥控器问题
      • 系统源码问题
  • 一、参考资料
  • 二、实现方案
    • 1、修改源码文件
    • 2、具体实践
      • 修改方案
      • 修改原理
  • 三、实际效果
  • 四、思路扩展-源码分析
    • 1、实现方案和Android13 版本对比分析
    • 2、新方案畅享
      • 方案难点
      • 新方案思路
  • 总结

前言-需求-存在的问题

前言-场景

Android12 MTK 平台,6769方案 的大屏产品,接遥控器 发现遥控器在系统设置一集菜单最下方的菜单可以获取的遥控器焦点,但是对用户不可见。 6769 本身是手机方案,拿来直接用作大屏商显产品,可能就本身存在一定问题,需要修复。 接入遥控器后,系统设置一级菜单可以实现遥控器下拉到最下方的一级菜单,用户可见。

存在问题

遥控器问题

如下,如果是白色遥控器 本身还可以点触效果,滑动页面 ,但是使用的是黑色遥控器就没有这个功能了。 那么就一定要通过 上下左右切换焦点的功能,达到每个功能性UI 焦点可达

系统源码问题

如下,实际问题:密码和账号、快霸 下面还有子菜单,遥控器焦点可达,但是UI不可见。 一定要用手上拉才能看见实际菜单。

一、参考资料

MTK-Android13-设置模块一级菜单添加遥控器功能 : 偏系统应用层适配,但是和当前版本不一致,可借鉴

Android软件适配遥控器需求-案例经验分享:偏应用层相关适配

MTK-Android12-适配蓝牙遥控器按键 : 偏系统层适配遥控器按键值、功能性适配。

二、实现方案

1、修改源码文件

文件路径:/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/homepage/SettingsHomepageActivity.java

2、具体实践

修改方案

具体修改如下:

  • 屏蔽://initHomepageContainer();
privatevoidinitHomepageContainer(){finalViewview=findViewById(R.id.homepage_container);// Prevent inner RecyclerView gets focus and invokes scrolling.view.setFocusableInTouchMode(true);view.requestFocus();}
  • onCreate方法中,添加代码如下:
  • 给列表底部加留白,最后几项完全显示
importandroid.view.View;importandroid.view.ViewTreeObserver;importandroidx.core.widget.NestedScrollView;importandroid.widget.FrameLayout;importandroid.util.Log;importandroid.view.ViewGroup;importandroidx.recyclerview.widget.RecyclerView;importandroid.view.ViewParent;importandroid.graphics.Rect;finalViewGroupmainContent=findViewById(R.id.main_content);mainContent.getViewTreeObserver().addOnGlobalLayoutListener(newViewTreeObserver.OnGlobalLayoutListener(){@OverridepublicvoidonGlobalLayout(){mainContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);// 找到列表RecyclerViewrecyclerView=findRecyclerView(mainContent);if(recyclerView==null)return;// 🔥 关键:给列表底部设置 400dp 留白,让最后几项可以完全滚动显示!intbottomPadding=(int)(getResources().getDisplayMetrics().density*400);recyclerView.setPadding(recyclerView.getPaddingLeft(),recyclerView.getPaddingTop(),recyclerView.getPaddingRight(),bottomPadding// 底部加大量空白);recyclerView.setClipToPadding(false);}});

修改原理

  • 屏蔽掉焦点占用
  • 直接给 RecyclerView 加足够的底部留白,让最后几项能完全滚上来。

三、实际效果

四、思路扩展-源码分析

1、实现方案和Android13 版本对比分析

  • 之前Mtk 平台Android13版本,解决方案:屏蔽://initHomepageContainer();就可以了的,但是在Android12版本中无效。

2、新方案畅享

方案难点

我们解决方案其实没有从UI滑动上面考虑来解决,只是兜底、保底 方案, 拼凑一个View 出来。 实际上 我们保证列表上划。
难点是:NestedScrollView里面嵌套了FrameLayoutFrameLayout中最终嵌套的是TopLevelSettings下面的各种PreferenceFragmentCompat,可以这么理解吧: 很难捕获到真正的焦点、焦点冲突了。

NestedScrollView**这个才能继续往上滑!**LinearLayoutFrameLayoutRecyclerView**这个已经滑到底了,滑不动了!**
<androidx.core.widget.NestedScrollViewandroid:id="@+id/main_content_scrollable_container"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="com.android.settings.widget.HomepageAppBarScrollingViewBehavior"><LinearLayoutandroid:id="@+id/homepage_container"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><FrameLayoutandroid:id="@+id/contextual_cards_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="@dimen/contextual_card_side_margin"android:layout_marginEnd="@dimen/contextual_card_side_margin"/><FrameLayoutandroid:id="@+id/main_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:animateLayoutChanges="true"android:background="?android:attr/windowBackground"/></LinearLayout></androidx.core.widget.NestedScrollView>

新方案思路

基于焦点冲突问题,那么就是解决焦点冲突,所以可以尝试监听,然后找到对应的焦点,然后滑动对应的View. 这个方案可以自行研究,这里暂不讨论, 我自己尝试解决没有解决出来。
如下代码是调试代码,可以借鉴、找方向、定位问题:

/* NestedScrollView scrollView = findViewById(R.id.main_content_scrollable_container); scrollView.setNestedScrollingEnabled(false); getWindow().getDecorView().setOnFocusChangeListener((v, hasFocus) -> { View focusView = getCurrentFocus(); if (focusView != null && focusView != scrollView) { scrollView.post(() -> { Log.d(TAG,"==========setOnFocusChangeListener smoothScrollBy==========="); int[] pos = new int[2]; focusView.getLocationInWindow(pos); scrollView.smoothScrollBy(0, pos[1] - 100); }); } });*/Log.d(TAG,"=======onCreate==============1111=");/*final NestedScrollView scrollView = findViewById(R.id.main_content_scrollable_container); scrollView.setSmoothScrollingEnabled(true); getWindow().getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener((oldFocus, newFocus) -> { if (newFocus == null) return; int[] location = new int[2]; newFocus.getLocationInWindow(location); int focusBottom = location[1] + newFocus.getHeight(); int screenHeight = getResources().getDisplayMetrics().heightPixels; Log.d(TAG,"==========addOnGlobalFocusChangeListener focusBottom:"+focusBottom+" screenHeight:"+screenHeight); //==========addOnGlobalFocusChangeListener focusBottom:1179 screenHeight:1008 //==========addOnGlobalFocusChangeListener focusBottom:1297 screenHeight:1008 if (focusBottom > screenHeight - 100) { scrollView.post(() -> { scrollView.smoothScrollBy(0, 300); }); } }); *//* final NestedScrollView scrollView = findViewById(R.id.main_content_scrollable_container); scrollView.setSmoothScrollingEnabled(true); getWindow().getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener((oldFocus, newFocus) -> { if (newFocus == null || scrollView == null) return; int[] location = new int[2]; newFocus.getLocationOnScreen(location); int focusBottom = location[1] + newFocus.getHeight(); int screenHeight = getResources().getDisplayMetrics().heightPixels; Log.d(TAG, "addOnGlobalFocusChangeListener focusBottom:" + focusBottom + " screenHeight:" + screenHeight); if (focusBottom > screenHeight - 100) { scrollView.post(() -> { int scrollY = newFocus.getTop() - scrollView.getTop(); Log.d(TAG, "addOnGlobalFocusChangeListener scrollY:" + scrollY+" newFocus.getTop():"+newFocus.getTop()+" scrollView.getTop():"+scrollView.getTop()); scrollView.scrollTo(0, scrollY); }); } }); *//* final NestedScrollView scrollView = findViewById(R.id.main_content_scrollable_container); final ViewGroup mainContent = findViewById(R.id.main_content); mainContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mainContent.getViewTreeObserver().removeOnGlobalLayoutListener(this); RecyclerView recyclerView = findRecyclerView(mainContent); if (recyclerView == null){ Log.d(TAG, "wfc addOnGlobalLayoutListener recyclerView == null return " ); return; } getWindow().getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener((oldFocus, newFocus) -> { if (newFocus == null || recyclerView == null){ Log.d(TAG, "wfc addOnGlobalFocusChangeListener newFocus == null || recyclerView == null" ); return; } int[] loc = new int[2]; newFocus.getLocationInWindow(loc); int bottom = loc[1] + newFocus.getHeight(); int screenHeight = getResources().getDisplayMetrics().heightPixels; Log.d(TAG, "wfc addOnGlobalFocusChangeListener bottom: " + bottom + " screenHeight: " + screenHeight); if (bottom > screenHeight - 150) { recyclerView.post(() -> { int position = recyclerView.getChildAdapterPosition(newFocus); Log.d(TAG,"wfc ============== smoothScrollToPosition position:"+position); recyclerView.scrollBy(0, 350); }); } }); } });*//* final NestedScrollView scrollView = findViewById(R.id.main_content_scrollable_container); getWindow().getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener((oldFocus, newFocus) -> { if (newFocus == null) return; newFocus.post(() -> { newFocus.getWindowVisibleDisplayFrame(new Rect()); }); }); */finalViewGroupmainContent=findViewById(R.id.main_content);mainContent.getViewTreeObserver().addOnGlobalLayoutListener(newViewTreeObserver.OnGlobalLayoutListener(){@OverridepublicvoidonGlobalLayout(){mainContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);RecyclerViewrecyclerView=findRecyclerView(mainContent);if(recyclerView==null)return;intbottomPadding=(int)(getResources().getDisplayMetrics().density*400);recyclerView.setPadding(recyclerView.getPaddingLeft(),recyclerView.getPaddingTop(),recyclerView.getPaddingRight(),bottomPadding);recyclerView.setClipToPadding(false);}});// ==========================================================}// modify by fangchen startprivateRecyclerViewfindRecyclerView(ViewGroupparent){for(inti=0;i<parent.getChildCount();i++){Viewchild=parent.getChildAt(i);if(childinstanceofRecyclerView){return(RecyclerView)child;}if(childinstanceofViewGroup){RecyclerViewrv=findRecyclerView((ViewGroup)child);if(rv!=null)returnrv;}}returnnull;}// modify by fangchen end

总结

  • 这里解决了Mtk平台 Android12 版本,系统设置不适配遥控器问题,针对性讨论了解决方案
  • 对比不同版本、不同的解决方案、找到实际解决方案并解决问题
  • 焦点问题才是核心,不太好调试,可以自行调试并解决。
http://www.cnnetsun.cn/news/2499101.html

相关文章:

  • 【限时解密】ElevenLabs未公开的瑞典文语料权重配置表:仅限前200名开发者获取的/sv-SE/声道微调参数
  • AI翻唱魔法师:5分钟免费打造专业级AI音乐作品的终极指南
  • 系统设计:十万级并发电商商品详情页,如何设计
  • 使用 Taotoken CLI 工具一键配置团队开发环境中的模型接入参数
  • 从TTL到差分信号:手把手图解RS232/RS485电平转换电路,避坑STM32串口配置
  • 2026 高炉炼铁智能化技术全景与演进路径~系列文章00:高炉炼铁智能化的产业变革与2026技术全景
  • Product Hunt 每日热榜 | 2026-05-21
  • 安科士(AndXe)QSFP+ 40G SR4 光模块:数据中心短距高速互联的理想之选
  • 以图灵机为喻!交互式教程助开发者理解CRDT工作原理
  • 黑客教你月入过万小技巧:SRC漏洞挖掘_怎么挖漏洞赚钱
  • VR安全带防坠落体验平台助力高空作业安全培训
  • 程序员需求攀升:数字化浪潮下的行业必然
  • LangGraph 并发执行:为什么你的多 Agent 总是“一个卡住全军覆没”?
  • 小资金期货量化用什么软件:成本敏感型的现实选项
  • 2026 年苏州地面互动品牌,创新魅力等你来发现!
  • 旅游应该注意什么
  • 【ai员工】windows Pixelle Studio 部署并运行
  • 抖音批量下载器终极指南:3步轻松搞定无水印视频下载
  • Layerdivider智能图像分层工具:3分钟搞定专业PSD分层的终极指南
  • 顶级研究员Karpathy跳槽Anthropic,押注预训练,AI行业格局或生变?
  • 技术架构深度剖析:如何构建专业的浏览器资源嗅探扩展
  • 如何管理Taotoken的API Key并设置访问控制与审计日志
  • 野兽派≠高饱和!20年数字绘画师逆向工程MJ底层渲染管线,发现3类被官方文档隐瞒的风格触发器
  • JeecgBoot 双流程引擎选型指南:协同工作 vs Flowable,别再用错了!
  • 2026年阿里云OpenClaw/Hermes Agent配置Token Plan搭建保姆教程
  • Windows 11终极优化指南:用Win11Debloat免费提升电脑性能55%
  • 【Android】Apktool M安卓逆向反编译工具必备 可一定程度平替mt管理器
  • 终极指南:如何在Android设备上实现Zwift离线骑行模拟
  • 我靠测试知识付费实现月入2w+的故事
  • 通过Python脚本示例快速上手Taotoken的流式响应与函数调用