Android TV RecyclerView列表获得焦点左右换行

        在TV上,用RecyclerView显示一个列表,飞鼠遥控左右遥控获得Item焦点,到最后一个进行右键换行,是不能做到的,因此需要监听key事件处理换行。

效果图如下

代码实现

Item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="10dp"android:clickable="true"android:focusable="true"android:gravity="center"android:focusableInTouchMode="true"android:background="@drawable/focusable_view_bg"android:orientation="vertical"><ImageViewandroid:id="@+id/img"android:layout_width="100dp"android:layout_height="100dp"android:src="@drawable/girl1"android:scaleType="fitXY" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Test1" /></LinearLayout>

focusable_view_bg.xml 获得焦点和悬浮

在drawable创建focusable_view_bg.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android"><!-- 悬浮 --><item android:state_hovered="true"><shape><corners android:radius="15dp" /><solid android:color="#66000000" /><stroke android:width="2dp" android:color="#fff000" /></shape></item><!-- 获得焦点 --><item android:state_focused="true"><shape><corners android:radius="15dp" /><stroke android:width="2dp" android:color="#fff000" /><solid android:color="#66000000" /></shape></item></selector>

activity_main.xml布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/btn_move_left"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="左移动"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btn_move_right"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="右移动"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btn_enter"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_list"android:layout_width="0dp"android:layout_height="0dp"android:focusable="true"android:clickable="true"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_move_right" /></androidx.constraintlayout.widget.ConstraintLayout>

Adapter类

package com.dfg.recyclerviewfocus;import android.content.Context;
import android.graphics.Bitmap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;import java.util.ArrayList;
import java.util.Map;public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private Context context;private ArrayList<Map<String, Object>> list;private OnItemClickListener itemClickListener;private OnIconKeyListener iconKeyListener;public void setOnItemClickListener(OnItemClickListener itemClickListener) {this.itemClickListener = itemClickListener;}public void setOnIconKeyListener(OnIconKeyListener iconKeyListener) {this.iconKeyListener = iconKeyListener;}public MyAdapter(Context context, ArrayList<Map<String, Object>> list) {this.context = context;this.list = list;}static class ViewHolderItem extends RecyclerView.ViewHolder {ImageView imgAppPic;//app图片TextView tvAppName;// app 名字public ViewHolderItem(View view) {super(view);imgAppPic = view.findViewById(R.id.img);tvAppName = view.findViewById(R.id.title);}}@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);ViewHolderItem viewHolderItem = new ViewHolderItem(view);return viewHolderItem;}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {ViewHolderItem holder = (ViewHolderItem) viewHolder;Map<String, Object> item = list.get(position);holder.imgAppPic.setImageBitmap((Bitmap) item.get("icon"));holder.tvAppName.setText(item.get("title").toString());holder.itemView.setOnClickListener(v -> {if (itemClickListener != null) {itemClickListener.itemClick(v, holder.getAdapterPosition());}});holder.itemView.setOnKeyListener(new View.OnKeyListener() {@Overridepublic boolean onKey(View v, int keyCode, KeyEvent event) {if(iconKeyListener!=null) {return iconKeyListener.onKey(v,keyCode,event,holder.getAdapterPosition());}return false;}});}@Overridepublic int getItemCount() {return list.size();}// 点击 Item 回调interface OnItemClickListener {void itemClick(View view, int position);}// 回调Keyinterface OnIconKeyListener{boolean onKey(View v, int keyCode, KeyEvent event,int position);}
}

MainActivity

public class MainActivity extends AppCompatActivity {private String TAG = "MainActivity";private Button btnMoveRight;private Button btnMoveLeft;private Button btnEnter;private RecyclerView recyclerView;private MyAdapter myAdapter;private ArrayList<Map<String, Object>> list = new ArrayList<>();// 列数,网格布局中每行4个Itemprivate int numColumns = 4;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();setData();click();}public void init() {btnMoveRight = findViewById(R.id.btn_move_right);btnMoveLeft = findViewById(R.id.btn_move_left);btnEnter = findViewById(R.id.btn_enter);recyclerView = findViewById(R.id.recycler_list);}/*** 设置数据源并初始化RecyclerView*/public void setData() {for (int i = 0; i < 30; i++) {Map map = new HashMap();map.put("icon", BitmapFactory.decodeResource(getResources(), R.drawable.girl1));map.put("title", "test" + i);list.add(map);}myAdapter = new MyAdapter(getApplicationContext(), list);GridLayoutManager gridLayoutManager = new GridLayoutManager(this, numColumns);recyclerView.setLayoutManager(gridLayoutManager);recyclerView.setAdapter(myAdapter);}public void click() {// 右移按钮点击事件btnMoveRight.setOnClickListener(v -> {try {// 查找当前获得焦点的视图View focusedView = recyclerView.findFocus();// 如果RecyclerView没有获得焦点if (focusedView != null) {// 获取RecyclerView的子类第0个itemint position = recyclerView.getChildAdapterPosition(focusedView);Log.d(TAG, "当前获得焦点的Item位置: " + position);Runtime.getRuntime().exec("input keyevent 22");} else {Log.d(TAG, "没有任何Item获得焦点");if (recyclerView.getLayoutManager() != null) {// 如果没有获得焦点的视图,默认让第一个可见项获得焦点int firstPosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();View positionChild = recyclerView.getLayoutManager().findViewByPosition(firstPosition);if (positionChild != null) {positionChild.requestFocus();// 让第一个Item获得焦点}}}} catch (Exception e) {throw new RuntimeException(e);}});// 左移按钮点击事件btnMoveLeft.setOnClickListener(v -> {try {// 查找当前获得焦点的视图View focusedView = recyclerView.findFocus();// 如果RecyclerView没有获得焦点if (focusedView != null) {// 获取RecyclerView的子类第0个itemint position = recyclerView.getChildAdapterPosition(focusedView);Log.d(TAG, "当前获得焦点的Item位置: " + position);} else {Log.d(TAG, "没有任何Item获得焦点");if (recyclerView.getLayoutManager() != null) {// 如果没有获得焦点的视图,默认让第一个可见项获得焦点int firstPosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();View positionChild = recyclerView.getLayoutManager().findViewByPosition(firstPosition);if (positionChild != null) {positionChild.requestFocus();// 让第一个Item获得焦点}}}Runtime.getRuntime().exec("input keyevent 21");} catch (IOException e) {throw new RuntimeException(e);}});// 确认按钮点击事件btnEnter.setOnClickListener(v -> {try {Runtime.getRuntime().exec("input keyevent 66");} catch (IOException e) {throw new RuntimeException(e);}});myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {@Overridepublic void itemClick(View view, int position) {Toast.makeText(getApplicationContext(), list.get(position).get("title").toString(), Toast.LENGTH_SHORT).show();}});// 设置RecyclerView Item键盘事件监听myAdapter.setOnIconKeyListener(new MyAdapter.OnIconKeyListener() {@Overridepublic boolean onKey(View v, int keyCode, KeyEvent event, int position) {// 获取按键动作类型final int action = event.getAction();// 检查按键是否为按下动作final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);// 标记按键是否被处理boolean wasHandled = false;switch (keyCode) {// 左键按下事件case KeyEvent.KEYCODE_DPAD_LEFT:// 不是手指抬起操作if (handleKeyEvent) {// 如果当前的 Item 是 LinearLayoutif (v instanceof LinearLayout) {// 当前当前的 Item父类 是 RecyclerViewif (v.getParent() instanceof RecyclerView) {// 如果当前项在一列最后一项 或 第0项if (position % numColumns == 0) {// position的位置一定要 >= 0,因为这里要进行换行了if (position - 1 >= 0) {if (recyclerView.getLayoutManager() != null) {// 这里进行位置 -1,如果是屏幕看不到上一行,就会为NullView positionChild = recyclerView.getLayoutManager().findViewByPosition(position - 1);if (positionChild != null) {// 将焦点移动到前一个ItempositionChild.requestFocus();} else {// 如果当前屏幕看不到上一个Item时,这里就会为 null,然后上滑到前一项。// 平滑滚动到前一项recyclerView.smoothScrollToPosition(position - 1);try {// 再次执行左键按下Runtime.getRuntime().exec("input keyevent 21");} catch (IOException e) {throw new RuntimeException(e);}}wasHandled = true;}}}}}}break;case KeyEvent.KEYCODE_DPAD_RIGHT:// 不是手指抬起操作if (handleKeyEvent) {// 如果当前的 Item 是 LinearLayoutif (v instanceof LinearLayout) {// 当前当前的 Item父类 是 RecyclerViewif (v.getParent() instanceof RecyclerView) {// 当前的位置+1 < adapter的item总数if (position + 1 < myAdapter.getItemCount()) {// 如果 当前位置+1 % 列数 =0,表示下一个是下一行了if ((position + 1) % numColumns == 0) {if (recyclerView.getLayoutManager() != null) {// 获取下一个item,如果是屏幕看不到下一行,就会为NullView positionChild = recyclerView.getLayoutManager().findViewByPosition(position + 1);if (positionChild != null) {// 将焦点移动到下一个ItempositionChild.requestFocus();} else {// 如果当前屏幕看不到下一个Item时,这里就会为 null,然后下滑到前一项。// 平滑滚动到下一项recyclerView.smoothScrollToPosition(position + 1);try {Runtime.getRuntime().exec("input keyevent 22");} catch (IOException e) {throw new RuntimeException(e);}}}// 返回true,事件自己消费处理了。wasHandled = true;}} else if (position + 1 == myAdapter.getItemCount()) {wasHandled = true;}}}}break;}return wasHandled;}});}
}

RecyclerView相关方法

  • recyclerView.getLayoutManager().findViewByPosition(positon):获取当前显示的某个位置的子视图。
  • recyclerView.getChildAdapterPosition(View):获取某个子视图在适配器中的位置。
  • recyclerView.smoothScrollToPosition(positon):平滑滚动 RecyclerView 到指定位置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/143126.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

红帽 Quay- 配置镜像代理缓存

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 Quay 3.12 环境中验证 说明&#xff1a;可先根据《红帽 Quay - 安装篇》完成 Quay 安装。 镜像代理缓存功能 Quay 的镜像代理缓存功能可以将用户拉取的远程镜像保存到本地 Quay 的 proxy cache 中&am…

C++速通LeetCode中等第2题-最长连续序列

方法一&#xff0c;排序后遍历&#xff0c;后减前1&#xff0c;计数&#xff0c; 相等跳过&#xff0c;后减前&#xff01;1就保存。 class Solution { public:int longestConsecutive(vector<int>& nums) {vector<int> ans;int count 1;sort(nums.begin(),n…

Facebook的用户隐私保护:从争议到革新

Facebook早期的数据收集方式引发了隐私担忧。平台的快速增长和用户数据的大规模收集使得隐私问题逐渐显现。尤其是在2018年&#xff0c;剑桥分析事件暴露了数千万用户数据被不当使用的问题。这一事件揭示了Facebook在数据保护方面的严重漏洞&#xff0c;引发了公众对隐私保护的…

C++中的const \static \this

目录 前言 一、const关键字 1、const修饰类的成员变量 2、const修饰类的成员函数 3、const修饰类的对象 二、static关键字 1、static修饰类中的成员变量 1. 共享性 2. 初始化 3. 访问权限 4. 内存分配 5. 不依赖于对象 2、static修饰类中的成员函数 三、this关键字…

「C++系列」异常处理

【人工智能教程】&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站&#xff1a;【人工智能教程】 文章目录 一、异常处理1. 基本概念2. 示例代码3. 注意事项 二、常见的异常类…

三维天地创新方案助力实验室信息自动化技术深入发展

实验室环境条件控制非常重要,它直接影响着最终的实验或检测结果。例如不同的实验室对于温湿度有不同的要求,这就给实验室温湿度监测与采集带来了一定的困难。 三维天地自主研发的实验室信息管理系统(SW-LIMS)提出了一种检化验记录温湿度自动采集的创新方案,这一方案致力于实现…

一键文本提示实现图像对象高质量剪切与透明背景生成

按照提示词裁剪 按照边框裁剪 要实现您描述的功能,即通过一个文本提示就能自动从图片中切割出指定的对象并生成一个带有透明背景的新图像,这需要一个结合了先进的计算机视觉技术和自然语言处理能力的系统。这样的系统可以理解输入的文本指令,并将其转化为对图像内容的精确分…

解决nginx代理SSE接口的响应没有流式返回

目录 现象原来的nginx配置解决 现象 前后端分离的项目&#xff0c;前端访问被nginx反向代理的后端SSE接口&#xff0c;预期是流式返回&#xff0c;但经常是很久不响应&#xff0c;一响应全部结果一下子都返回了。查看后端项目的日志&#xff0c;响应其实是流式产生的。推测是n…

大数据新视界 --大数据大厂之探索ES:大数据时代的高效搜索引擎实战攻略

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Markdown书写技巧深度解析

引言 在数字化时代&#xff0c;文本编辑与格式化的效率与美观性显得尤为重要。Markdown&#xff0c;作为一种轻量级的标记语言&#xff0c;以其简洁的语法和高效的文档转换能力&#xff0c;在多个领域得到广泛应用。本文将全面探讨Markdown的由来、定义、原理、内部流程、应用场…

构建自己的文生图工具:Python + Stable Diffusion + CUDA

构建自己的文生图工具&#xff1a;Python Stable Diffusion CUDA 前言概述环境搭建安装PyTorch安装Stable Diffusion编写Python代码结论结语 前言 在这个数字化和人工智能飞速发展的时代&#xff0c;图像生成技术正逐渐成为现实。想象一下&#xff0c;只需输入几个关键词&…

el-form动态标题和输入值,并且最后一个输入框不校验

需求&#xff1a;给了固定的label&#xff0c;叫xx单位&#xff0c;要输入单位的信息&#xff0c;但是属性名称都一样的&#xff0c;UI画图也是表单的形式&#xff0c;所以改为动态添加的形式&#xff0c;实现方式也很简单&#xff0c;循环就完事了&#xff0c;连着表单校验也动…

探索Facebook的黑暗面:数字化社交的双面剑

Facebook作为全球最大的社交平台&#xff0c;改变了我们的沟通和互动方式。虽然它带来了便利&#xff0c;但也存在不少隐忧。本文将探讨Facebook的负面影响&#xff0c;包括隐私问题、信息操控、心理健康危机及社交表面化等。 一、隐私问题&#xff1a;数据收集的隐忧 Facebo…

2024蓝桥杯省B好题分析

题解来自洛谷&#xff0c;作为学习 目录 宝石组合 数字接龙 爬山 拔河 宝石组合 # [蓝桥杯 2024 省 B] 宝石组合## 题目描述在一个神秘的森林里&#xff0c;住着一个小精灵名叫小蓝。有一天&#xff0c;他偶然发现了一个隐藏在树洞里的宝藏&#xff0c;里面装满了闪烁着美…

乐vs悲观锁,重vs轻量级锁,公vs非公平锁,不vs可重入锁,等等锁策略

这里讲的“乐观锁”“悲观锁”“轻量级锁”等等&#xff0c;都不是一个锁&#xff0c;而是一类锁。 比如&#xff1a;我们班有“带眼镜”的同学&#xff0c;这里“带眼镜”并不是指一个人&#xff0c;而是指一类人。 并且这里的锁&#xff0c;并不局限于Java&#xff0c;而是只…

优化数据的抓取规则:减少无效请求

在爬取房价信息的过程中&#xff0c;如何有效过滤无效链接、减少冗余请求&#xff0c;是提升数据抓取效率的关键。本文将介绍如何优化爬虫抓取贝壳等二手房平台中的房价、小区信息&#xff0c;并通过代理IP、多线程、User-Agent和Cookies的设置&#xff0c;确保数据抓取的稳定性…

(娱乐)魔改浏览器-任务栏图标右上角加提示徽章

一、目标&#xff1a; windows中&#xff0c;打开chromium&#xff0c;任务栏中会出现一个chromium的图标。我们的目标是给这个图标的右上角&#xff0c;加上"有1条新消息"的小提示图标&#xff0c;也叫徽章(badge)注意&#xff1a;本章节纯属娱乐&#xff0c;有需要…

手脱简单upx

大一下的事情&#xff0c;补个档 手动脱壳の新年快乐 查壳&#xff0c;有壳&#xff0c;UPX X32dbg打开文件&#xff0c;查看初始断点 点击PUSHAD跟进&#xff0c;CTRL*设置EIP&#xff0c;开始F8步过&#xff0c;寻找ESP寄存器第一次单个变红的地址 此时的内存窗口 开始步过…

esp32核心跑分程序

https://github.com/ochrin/coremark/tree/esp32 最近一直捣腾esp32s3 (Sense) 做微型摄像。过程中发现一款不错的跑分软件&#xff0c;特此记一笔。 其中针对esp32s3各类参数设定&#xff08;用idf.py menuconfig)&#xff0c;做个记录。 CPU Frequency去240MHz&#xff08…

【H2O2|全栈】关于CSS(6)CSS基础(五)

目录 CSS基础知识 前言 准备工作 网页项目规范 创建项目 布局 补充一部分属性 outline border-radius 预告和回顾 后话 CSS基础知识 前言 本系列博客将分享层叠样式表&#xff08;CSS&#xff09;有关的知识点。 本期博客主要分享的是网页项目规范&#xff0c;ou…