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">

    <ImageView
        android:id="@+id/img"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/girl1"
        android:scaleType="fitXY" />

    <TextView
        android: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">


    <Button
        android: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" />

    <Button
        android: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" />

    <Button
        android: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.RecyclerView
        android: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
    @Override
    public 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;
    }

    @Override
    public 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() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if(iconKeyListener!=null) {
                    return iconKeyListener.onKey(v,keyCode,event,holder.getAdapterPosition());
                }
                return false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    // 点击 Item 回调
    interface OnItemClickListener {
        void itemClick(View view, int position);
    }

    // 回调Key
    interface 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个Item
    private int numColumns = 4;

    @Override
    protected 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个item
                    int 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个item
                    int 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() {
            @Override
            public 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() {
            @Override
            public 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 是 LinearLayout
                            if (v instanceof LinearLayout) {
                                // 当前当前的 Item父类 是 RecyclerView
                                if (v.getParent() instanceof RecyclerView) {
                                    // 如果当前项在一列最后一项 或 第0项
                                    if (position % numColumns == 0) {
                                        // position的位置一定要 >= 0,因为这里要进行换行了
                                        if (position - 1 >= 0) {
                                            if (recyclerView.getLayoutManager() != null) {
                                                // 这里进行位置 -1,如果是屏幕看不到上一行,就会为Null
                                                View positionChild = recyclerView.getLayoutManager().findViewByPosition(position - 1);
                                                if (positionChild != null) {
                                                    // 将焦点移动到前一个Item
                                                    positionChild.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 是 LinearLayout
                            if (v instanceof LinearLayout) {
                                // 当前当前的 Item父类 是 RecyclerView
                                if (v.getParent() instanceof RecyclerView) {
                                    // 当前的位置+1 < adapter的item总数
                                    if (position + 1 < myAdapter.getItemCount()) {
                                        // 如果 当前位置+1 % 列数 =0,表示下一个是下一行了
                                        if ((position + 1) % numColumns == 0) {
                                            if (recyclerView.getLayoutManager() != null) {
                                                // 获取下一个item,如果是屏幕看不到下一行,就会为Null
                                                View positionChild = recyclerView.getLayoutManager().findViewByPosition(position + 1);
                                                if (positionChild != null) {
                                                    // 将焦点移动到下一个Item
                                                    positionChild.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 到指定位置。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Layout 布局组件快速搭建

文章目录 设置主题样式变量封装公共布局组件封装 Logo 组件封装 Menu 菜单组件封装 Breadcrumb 面包屑组件封装 TabBar 标签栏组件封装 Main 内容区组件封装 Footer 底部组件封装 Theme 主题组件 经典布局水平布局响应式布局搭建 Layout 布局组件添加 Layout 路由配置启动项目 …

连续数组问题

目录 一题目&#xff1a; 二思路&#xff1a; 三代码&#xff1a; 一题目&#xff1a; leetcode链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二思路&#xff1a; 思路&#xff1a;前缀和&#xff08;第二种&#xff09;化0为-1hash&#xff1a; 这样可以把…

SQL server学习01-SQL server环境配置

目录 一&#xff0c;手动下载及安装 microsoft .net framework 3.5 1&#xff0c;下载 2&#xff0c;安装 二&#xff0c;安装SQL server2014 1&#xff0c;下载 2&#xff0c;安装 3&#xff0c;启动SQL server服务 三&#xff0c;下载及安装Microsoft SQL Server…

高效编程的利器 Jupyter Notebook

目录 前言1. Jupyter Notebook简介1.1 功能特点1.2 使用场景 2. 不同编程工具的对比与效率提升2.1 VS Code&#xff1a;灵活且轻量的代码编辑器2.2 PyCharm&#xff1a;面向专业开发者的集成开发环境2.3 Git&#xff1a;高效协作的版本控制工具2.4 Jupyter Notebook 和 VS Code…

【AI学习笔记】初学机器学习西瓜书概要记录(一)机器学习基础知识篇

初学机器学习西瓜书的概要记录&#xff08;一&#xff09;机器学习基础知识篇(已完结) 初学机器学习西瓜书的概要记录&#xff08;二&#xff09;常用的机器学习方法篇(持续更新) 初学机器学习西瓜书的概要记录&#xff08;三&#xff09;进阶知识篇(待更) 文字公式撰写不易&am…

【爱给网-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

virtualbox中的网络模式,网络设置,固定IP

virtualbox关于网络设置的文档&#xff1a;https://www.virtualbox.org/manual/topics/networkingdetails.html#networkingdetails DHCP Dynamic Host Configuration Protocol&#xff1a;动态主机配置协议&#xff0c;是专门用来给网络中的节点分发IP地址&#xff0c;确保每…

用友U8二次开发工具KK-FULL-*****-EFWeb使用方法

1、安装: 下一步&#xff0c;下一步即可。弹出黑框不要关闭&#xff0c;让其自动执行并关闭。 2、服务配置&#xff1a; 输入服务器IP地址&#xff0c;选择U8数据源&#xff0c;输入U8用户名及账号&#xff0c;U8登录日期勾选系统日期。测试参数有效性&#xff0c;提示测试通过…

【Unity-UGUI组件拓展】| Image 组件拓展,支持FIlled和Slice功能并存

🎬【Unity-UGUI组件拓展】| Image 组件拓展,支持FIlled和Slice功能并存一、组件介绍二、组件拓展方法三、完整代码💯总结🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏…

esp32 wifi 联网后,用http 发送hello 用pc 浏览器查看网页

参考chatgpt Esp32可以配置为http服务器&#xff0c;可以socket编程。为了免除编写针对各种操作系统的app。完全可以用浏览器仿问esp32服务器&#xff0c;获取esp32的各种数据&#xff0c;甚至esp的音频&#xff0c;视频。也可以利用浏览器对esp进行各种操作。但esp不能主动仿…

golang学习笔记1-go程序执行流程

声明&#xff1a;本人已有C&#xff0c;C,Python基础&#xff0c;只写本人认为的重点&#xff0c;方便自己回顾。 命令行执行go程序有两种方式&#xff0c;其流程如下图 注意第一种方式会得到可执行文件&#xff0c;第二种不会。 例1 在当前目录下编译hello.go go build hel…

Matplotlib绘图基础

1、散点图 绘制散点图是数据可视化中非常常见的操作&#xff0c;它用于显示两组数据之间的关系。Matplotlib 提供了 plt.scatter() 函数&#xff0c;可以轻松绘制散点图。以下是一个基础的散点图示例代码&#xff0c;并包含了一些优化可视化呈现的技巧。 import matplotlib.p…

istio中如何使用serviceentry引入外部服务

假设需要引入一个外部服务&#xff0c;外部服务ip为10.10.102.90&#xff0c;端口为32033. 引入到istio中后&#xff0c;我想通过域名gindemo.test.ch:9090来访问这个服务。 serviceentry yaml内容如下&#xff1a; apiVersion: networking.istio.io/v1beta1 kind: ServiceEn…

53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录理论部分使用计数来建模N元语法总结 代码读取长序列数据随机采样顺序分区 小结练习 理论部分 在上一部分中&#xff0c;我们了解了如何将文本数据映射为词元&#xff0c;以及将这些词元可以视为一系列离散的观测&#xff0c;例如单词或字符…

构建与优化自定义进程池

1. 什么是进程池&#xff1f; 简单来说&#xff0c;进程池就是预先创建固定数量的工作进程&#xff0c;通过设计任务队列或调度算法来分配任务给空闲的进程 —— 实现“负载均衡”。 2. 进程池框架设计 枚举错误返回值&#xff1a; enum {UsageError 1,ArgError,PipeError };…

基于51单片机的汽车倒车防撞报警器系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 本课题基于微控制器控制器&#xff0c; 设计一款汽车倒车防撞报警器系统。 要求&#xff1a; 要求&#xff1a;1.配有距离&#xff0c; 用于把车和障碍物之间的距离信号送入控制器。 2.配有报警系…

如何安装和注册 GitLab Runner

如何安装和注册 GitLab Runner GitLab Runner 是一个用于运行 GitLab CI/CD (Continuous Integration/Continuous Deployment) 作业。它是一个与 GitLab 配合使用的应用程序&#xff0c;可以在本地或云中运行。Runner 可以执行不同类型的作业&#xff0c;例如编译代码、运行测…

传统软件应用技术的价值转换率越来越低

为什么感觉到卷&#xff1f;可能的一个原因是大家都在进步&#xff0c;用户和竞争对手也在进步&#xff0c;而自己却没有进步&#xff0c;也谈不上思维模式的改变。 我们不谈理论、不谈理想、不谈市场环境不好&#xff0c;就谈与用户接触过程的案例&#xff0c;这是最有说服力的…

传输层协议(TCP和UDP)

目录 一、UDP 1、UDPAPI 2、UDPAPI的使用 二、TCP 1、TCPAPI 2、TCP的相关特性 2.1 确认应答 2.2 超时重传 2.3 连接管理&#xff08;三次握手&#xff0c;四次挥手&#xff09; 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2.8 捎带应答 2.9 面向字节…

1.3 计算机网络的分类

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、按分布范围分类二、按传输技术分类三、按拓扑结构分类四、按使用者分类五、按传输介质分类 前言 计算机网络根据不同的标准可以被分为多种类型&#xff0c;本章从分布…