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

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 到指定位置。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 每天五分钟玩转深度学习pytorch:L1正则化和L2正则化的应用
  • Xcdoe快速更新安装的小Tips
  • 超详细超实用!!!AI编程之cursor编写设计模式迪米特法则实例(八)
  • 【高级数据结构】树状数组
  • 时间序列分析算法
  • srm供应商管理系统排名 6款好用的SRM系统推荐
  • 基于51单片机的两路电压检测(ADC0808)
  • 第二章 SQL语言简介
  • 5分钟内不能重复发送验证码!
  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-24
  • 速盾:凡科建站开cdn了吗?
  • Android 短信验证码自动填充
  • Python实现回归分析
  • QT事件过滤器(1)
  • <刷题笔记> 二叉搜索树与双向链表注意事项
  • [译]Python中的类属性与实例属性的区别
  • 【comparator, comparable】小总结
  • bootstrap创建登录注册页面
  • canvas绘制圆角头像
  • javascript 总结(常用工具类的封装)
  • JavaScript创建对象的四种方式
  • Logstash 参考指南(目录)
  • Mithril.js 入门介绍
  • MySQL用户中的%到底包不包括localhost?
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • php中curl和soap方式请求服务超时问题
  • 回流、重绘及其优化
  • 老板让我十分钟上手nx-admin
  • 如何优雅地使用 Sublime Text
  • 入手阿里云新服务器的部署NODE
  • 思否第一天
  • 微服务核心架构梳理
  • 小程序 setData 学问多
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • # C++之functional库用法整理
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (1) caustics\
  • (二十三)Flask之高频面试点
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .NET 服务 ServiceController
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .NET成年了,然后呢?
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • .Net转Java自学之路—基础巩固篇十三(集合)
  • :中兴通讯为何成功
  • @Autowired 与@Resource的区别
  • @ConditionalOnProperty注解使用说明
  • @JsonFormat 和 @DateTimeFormat 的区别
  • [1181]linux两台服务器之间传输文件和文件夹
  • [20160902]rm -rf的惨案.txt
  • [Apio2012]dispatching 左偏树