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

news2024/11/13 9:09:25

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

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

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

相关文章

24最全网最全面的Comfyui工作流原理拆解分析教程!

前言 前言 前面几篇有讲到Comfyui的安装和入门基础的文生图&#xff0c;图生图加上CN和局部重绘的工作流教程&#xff0c;这工作流是基于sd webui的工作流原理跟大家简单讲了Comfyui工作流的基本原理。 今天我们通过拆解组合的方式再稍微深入拓展给大家讲一下Comfyui的工作流…

基于springboot+vue图书管理系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;图书信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

昂科烧录器支持Senasic琻捷电子的蓝牙低功耗芯片SNP746

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Senasic琻捷电子的蓝牙低功耗芯片SNP746已经被昂科的通用烧录平台AP8000所支持。 SNP746是一款蓝牙低功耗芯片&#xff0c;集成了压力传感器和加速度传感器的测量电路。它是为…

以电子书号出版的论著有哪些优缺点?

以电子书号出版的论著具有以下优点和缺点&#xff1a; 优点&#xff1a; 1. 费用较低&#xff1a; - 电子书号的管理费用相对较低&#xff0c;因为其不需要分配 CIP 数据&#xff08;图书在版编目数据&#xff09;&#xff0c;这一项就节省了不少成本。对于一些预算有限的作者…

不小心把U盘格式化了怎么恢复?教你轻松找回数据

U盘作为我们日常工作和生活中的重要数据存储工具&#xff0c;其便携性和大容量深受用户喜爱。然而&#xff0c;不小心将U盘格式化&#xff0c;导致重要数据丢失&#xff0c;是许多人都可能遇到的问题。 当这种突发情况发生时&#xff0c;我们应该如何迅速有效地恢复被格式化的…

最佳实践 · 如何高效索引MySQL JSON字段

概述 从MySQL 5.7.8版本开始&#xff0c;MySQL引入了对JSON字段的支持&#xff0c;这为处理半结构化数据提供了极大的灵活性。然而&#xff0c;MySQL原生并不支持直接对JSON对象中的字段进行索引。本文将介绍如何利用MySQL 5.7中的虚拟字段功能&#xff0c;对JSON字段中的数据…

数据结构-排序(冒泡,选择,插入,希尔,快排,归并,堆排)

文章目录 排序冒泡排序代码实现 选择排序动图演示代码实现 插入排序动图演示代码实现 希尔排序动图演示代码实现 快速排序动图演示代码实现(递归) 归并排序动图演示代码实现 堆排序动图演示代码实现 排序 概念&#xff1a;排序就是将一组杂乱无章的数据按照一定的规律&#xff…

web基础—dvwa靶场(九)Weak Session IDs

Weak Session IDs&#xff08;弱会话&#xff09; Weak Session IDs&#xff08;弱会话&#xff09;&#xff0c;用户访问服务器的时候&#xff0c;一般服务器都会分配一个身份证 session id 给用户&#xff0c;用于标识。用户拿到 session id 后就会保存到 cookies 上&#x…

数据可视化pyecharts——数据分析(柱状图、折线图、饼图)

安装 首先确保已经安装了pyecharts库&#xff0c;如果没有&#xff0c;可以通过pip install pyecharts进行安装。 柱状图 从pyecharts.charts导入Bar&#xff0c;从pyecharts导入options。准备数据&#xff08;如类别数据x_data和对应的数值数据y_data&#xff09;。创建Bar对…

C# 动态编译

一、简介 CSharpCodeProvider 是 .NET 提供的一个强大工具&#xff0c;它允许开发人员在应用程序运行时动态地生成和执行 C# 代码。这一特性为后端开发带来了前所未有的灵活性和动态性&#xff0c;特别是在处理那些需要高度定制化或难以在编译时确定逻辑的场景时&#xff0c;尤…

【项目实训1】手把手教你使用 Dehazeformer 模型去雾:服务器租用、环境配置、自定义数据集、模型的训练与测试(全网最全的操作指导)

前言 文章性质:实操笔记 📖 代码来源:GitHub - IDKiro/DehazeFormer: [IEEE TIP] Vision Transformers for Single Image Dehazing 主要内容:本文详细记录了如何借助 Tabby 图形界面工具在 AutoDL 远程服务器上配置 Dehazeformer 所需的项目环境,并且成功运行 Dehazeform…

HeterGCL-Graph Contrastive Learning Framework on Heterophilic Graph

推荐指数: #paper/⭐⭐ 发表于:IJCAI24 类型&#xff1a;个人觉得算是图结构学习&#xff0c;部分思想不错 问题背景&#xff1a; 传统的随机增强不适合异配图。随机增强主要保留的是同配信息。这就导致在异配图用随机增强会抑制高频信息&#xff0c;直接使用时不合理的(这个…

JDBC 编程

目录 JDBC 是什么 JDBC 的工作原理 JDBC 的使用 引入驱动 使用 常用接口和类 Connection Statement ResultSet 使用总结 JDBC 是什么 JDBC&#xff08;Java Database Connectivity&#xff09;&#xff1a;Java数据库连接&#xff0c;是一种用于执行 SQL 语句的Java…

【附激活码】2024最新PyCharm下载安装激活汉化教程!

一、PyCharm激活 激活码&#xff1a; KQ8KMJ77TY-eyJsaWNlbnNlSWQiOiJLUThLTUo3N1RZIiwibGljZW5zZWVOYW1lIjoiVW5pdmVyc2l0YXMgTmVnZXJpIE1hbGFuZyIsImxpY2Vuc2VlVHlwZSI6IkNMQVNTUk9PTSIsImFzc2lnbmVlTmFtZSI6IkpldOWFqOWutuahtiDorqTlh4blupflkI0iLCJhc3NpZ25lZUVtYWlsIjoi…

如何使用 Python Matplotlib 绘制 3D 曲面图

在数据可视化中&#xff0c;3D 图表是一个非常有用的工具&#xff0c;特别是当想要展示复杂的三维数据时&#xff0c;如期权的波动率曲面。Python 的 matplotlib 库提供了生成各种类型图表&#xff0c;包括 3D 图表。 本文将介绍如何使用 Python 中的 matplotlib 绘制 3D 曲面…

分公司=一部门——组合模式

文章目录 分公司一部门——组合模式分公司不就是一部门吗&#xff1f;组合模式透明方式与安全方式何时使用组合模式公司管理系统组合模式好处 分公司一部门——组合模式 分公司不就是一部门吗&#xff1f; 时间&#xff1a;5月10日19点  地点&#xff1a;小菜、大鸟住所的客…

开放的数据时代:Web3和个人隐私的未来

在数字化和信息化的时代&#xff0c;数据隐私成为了公众关注的焦点。随着Web3技术的兴起&#xff0c;个人隐私保护进入了一个新的阶段。Web3作为去中心化的互联网架构&#xff0c;提出了对数据控制和隐私保护的新方案。本文将探讨Web3如何影响个人隐私的未来&#xff0c;并分析…

[附源码]SpringBoot+VUE+Java实现人脸识别系统

今天带来一款优秀的项目&#xff1a;java人脸识别系统源码 。 系统采用的流行的前后端分离结构&#xff0c;内含功能包括 “人脸数数据录入”&#xff0c;“人脸管理”&#xff0c;“摄像头识别” 如果您有任何问题&#xff0c;也请联系小编&#xff0c;小编是经验丰富的程序员…

编写程序,在一行上显示1-5数字,每个相邻的数字要求用空格进行分开

目录 前言 一、一行输出&#xff08;使用一个System语句输出&#xff09; 二、多行输出&#xff08;使用多&#xff08;N&#xff09;个System语句输出&#xff09; 三、循环输出&#xff08;使用for语句循环在通过System语句输出&#xff09; 四、完整代码 前言 1.本文所…

只会Python编程,做量化交易策略用QMT怎么样?听说QMT是支持Python的!

QMT是专门为机构、活跃投资者、高净值客户等专业投资者研发的智能量化交易终端&#xff0c;拥有高速行情、极速交易、策略交易、多维度风控等专业功能&#xff0c;满足专业投资者的特殊交易需求。覆盖业务范围广:沪深A股、港股通、两融、期权、期货。 适合用QMT的投资者&#x…