ToB企服应用市场:ToB评测及商务社交产业平台

标题: Android TV RecyclerView列表获得焦点左右换行 [打印本页]

作者: 冬雨财经    时间: 2024-9-23 02:42
标题: Android TV RecyclerView列表获得焦点左右换行
        在TV上,用RecyclerView显示一个列表,飞鼠遥控左右遥控获得Item焦点,到最后一个举行右移动换行,是不能做到的,因此必要监听key事件处置惩罚换行。
结果图如下

代码实现

Item.xml布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="wrap_content"
  4.     android:layout_height="wrap_content"
  5.     android:padding="10dp"
  6.     android:clickable="true"
  7.     android:focusable="true"
  8.     android:gravity="center"
  9.     android:focusableInTouchMode="true"
  10.     android:background="@drawable/focusable_view_bg"
  11.     android:orientation="vertical">
  12.     <ImageView
  13.         android:id="@+id/img"
  14.         android:layout_width="100dp"
  15.         android:layout_height="100dp"
  16.         android:src="@drawable/girl1"
  17.         android:scaleType="fitXY" />
  18.     <TextView
  19.         android:id="@+id/title"
  20.         android:layout_width="wrap_content"
  21.         android:layout_height="wrap_content"
  22.         android:text="Test1" />
  23. </LinearLayout>
复制代码
focusable_view_bg.xml 获得焦点和悬浮

在drawable创建focusable_view_bg.xml
  1. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  2.     <!-- 悬浮 -->
  3.     <item android:state_hovered="true">
  4.         <shape>
  5.             <corners android:radius="15dp" />
  6.             <solid android:color="#66000000" />
  7.             <stroke android:width="2dp" android:color="#fff000" />
  8.         </shape>
  9.     </item>
  10.     <!-- 获得焦点 -->
  11.     <item android:state_focused="true">
  12.         <shape>
  13.             <corners android:radius="15dp" />
  14.             <stroke android:width="2dp" android:color="#fff000" />
  15.             <solid android:color="#66000000" />
  16.         </shape>
  17.     </item>
  18. </selector>
复制代码
activity_main.xml布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"
  4.     xmlns:tools="http://schemas.android.com/tools"
  5.     android:layout_width="match_parent"
  6.     android:layout_height="match_parent"
  7.     tools:context=".MainActivity">
  8.     <Button
  9.         android:id="@+id/btn_move_left"
  10.         android:layout_width="wrap_content"
  11.         android:layout_height="wrap_content"
  12.         android:text="左移动"
  13.         app:layout_constraintStart_toStartOf="parent"
  14.         app:layout_constraintTop_toTopOf="parent" />
  15.     <Button
  16.         android:id="@+id/btn_move_right"
  17.         android:layout_width="wrap_content"
  18.         android:layout_height="wrap_content"
  19.         android:text="右移动"
  20.         app:layout_constraintEnd_toEndOf="parent"
  21.         app:layout_constraintStart_toStartOf="parent"
  22.         app:layout_constraintTop_toTopOf="parent" />
  23.     <Button
  24.         android:id="@+id/btn_enter"
  25.         android:layout_width="wrap_content"
  26.         android:layout_height="wrap_content"
  27.         android:text="点击"
  28.         app:layout_constraintEnd_toEndOf="parent"
  29.         app:layout_constraintTop_toTopOf="parent" />
  30.     <androidx.recyclerview.widget.RecyclerView
  31.         android:id="@+id/recycler_list"
  32.         android:layout_width="0dp"
  33.         android:layout_height="0dp"
  34.         android:focusable="true"
  35.         android:clickable="true"
  36.         app:layout_constraintBottom_toBottomOf="parent"
  37.         app:layout_constraintEnd_toEndOf="parent"
  38.         app:layout_constraintStart_toStartOf="parent"
  39.         app:layout_constraintTop_toBottomOf="@+id/btn_move_right" />
  40. </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
Adapter类

  1. package com.dfg.recyclerviewfocus;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.view.KeyEvent;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.ImageView;
  9. import android.widget.TextView;
  10. import androidx.annotation.NonNull;
  11. import androidx.recyclerview.widget.RecyclerView;
  12. import java.util.ArrayList;
  13. import java.util.Map;
  14. public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
  15.     private Context context;
  16.     private ArrayList<Map<String, Object>> list;
  17.     private OnItemClickListener itemClickListener;
  18.     private OnIconKeyListener iconKeyListener;
  19.     public void setOnItemClickListener(OnItemClickListener itemClickListener) {
  20.         this.itemClickListener = itemClickListener;
  21.     }
  22.     public void setOnIconKeyListener(OnIconKeyListener iconKeyListener) {
  23.         this.iconKeyListener = iconKeyListener;
  24.     }
  25.     public MyAdapter(Context context, ArrayList<Map<String, Object>> list) {
  26.         this.context = context;
  27.         this.list = list;
  28.     }
  29.     static class ViewHolderItem extends RecyclerView.ViewHolder {
  30.         ImageView imgAppPic;//app图片
  31.         TextView tvAppName;// app 名字
  32.         public ViewHolderItem(View view) {
  33.             super(view);
  34.             imgAppPic = view.findViewById(R.id.img);
  35.             tvAppName = view.findViewById(R.id.title);
  36.         }
  37.     }
  38.     @NonNull
  39.     @Override
  40.     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  41.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
  42.         ViewHolderItem viewHolderItem = new ViewHolderItem(view);
  43.         return viewHolderItem;
  44.     }
  45.     @Override
  46.     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
  47.         ViewHolderItem holder = (ViewHolderItem) viewHolder;
  48.         Map<String, Object> item = list.get(position);
  49.         holder.imgAppPic.setImageBitmap((Bitmap) item.get("icon"));
  50.         holder.tvAppName.setText(item.get("title").toString());
  51.         holder.itemView.setOnClickListener(v -> {
  52.             if (itemClickListener != null) {
  53.                 itemClickListener.itemClick(v, holder.getAdapterPosition());
  54.             }
  55.         });
  56.         holder.itemView.setOnKeyListener(new View.OnKeyListener() {
  57.             @Override
  58.             public boolean onKey(View v, int keyCode, KeyEvent event) {
  59.                 if(iconKeyListener!=null) {
  60.                     return iconKeyListener.onKey(v,keyCode,event,holder.getAdapterPosition());
  61.                 }
  62.                 return false;
  63.             }
  64.         });
  65.     }
  66.     @Override
  67.     public int getItemCount() {
  68.         return list.size();
  69.     }
  70.     // 点击 Item 回调
  71.     interface OnItemClickListener {
  72.         void itemClick(View view, int position);
  73.     }
  74.     // 回调Key
  75.     interface OnIconKeyListener{
  76.         boolean onKey(View v, int keyCode, KeyEvent event,int position);
  77.     }
  78. }
复制代码
MainActivity

  1. public class MainActivity extends AppCompatActivity {
  2.     private String TAG = "MainActivity";
  3.     private Button btnMoveRight;
  4.     private Button btnMoveLeft;
  5.     private Button btnEnter;
  6.     private RecyclerView recyclerView;
  7.     private MyAdapter myAdapter;
  8.     private ArrayList<Map<String, Object>> list = new ArrayList<>();
  9.     // 列数,网格布局中每行4个Item
  10.     private int numColumns = 4;
  11.     @Override
  12.     protected void onCreate(Bundle savedInstanceState) {
  13.         super.onCreate(savedInstanceState);
  14.         setContentView(R.layout.activity_main);
  15.         init();
  16.         setData();
  17.         click();
  18.     }
  19.     public void init() {
  20.         btnMoveRight = findViewById(R.id.btn_move_right);
  21.         btnMoveLeft = findViewById(R.id.btn_move_left);
  22.         btnEnter = findViewById(R.id.btn_enter);
  23.         recyclerView = findViewById(R.id.recycler_list);
  24.     }
  25.     /**
  26.      * 设置数据源并初始化RecyclerView
  27.      */
  28.     public void setData() {
  29.         for (int i = 0; i < 30; i++) {
  30.             Map map = new HashMap();
  31.             map.put("icon", BitmapFactory.decodeResource(getResources(), R.drawable.girl1));
  32.             map.put("title", "test" + i);
  33.             list.add(map);
  34.         }
  35.         myAdapter = new MyAdapter(getApplicationContext(), list);
  36.         GridLayoutManager gridLayoutManager = new GridLayoutManager(this, numColumns);
  37.         recyclerView.setLayoutManager(gridLayoutManager);
  38.         recyclerView.setAdapter(myAdapter);
  39.     }
  40.     public void click() {
  41.         // 右移按钮点击事件
  42.         btnMoveRight.setOnClickListener(v -> {
  43.             try {
  44.                 // 查找当前获得焦点的视图
  45.                 View focusedView = recyclerView.findFocus();
  46.                 // 如果RecyclerView没有获得焦点
  47.                 if (focusedView != null) {
  48.                     // 获取RecyclerView的子类第0个item
  49.                     int position = recyclerView.getChildAdapterPosition(focusedView);
  50.                     Log.d(TAG, "当前获得焦点的Item位置: " + position);
  51.                     Runtime.getRuntime().exec("input keyevent 22");
  52.                 } else {
  53.                     Log.d(TAG, "没有任何Item获得焦点");
  54.                     if (recyclerView.getLayoutManager() != null) {
  55.                         // 如果没有获得焦点的视图,默认让第一个可见项获得焦点
  56.                         int firstPosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
  57.                         View positionChild = recyclerView.getLayoutManager().findViewByPosition(firstPosition);
  58.                         if (positionChild != null) {
  59.                             positionChild.requestFocus();// 让第一个Item获得焦点
  60.                         }
  61.                     }
  62.                 }
  63.             } catch (Exception e) {
  64.                 throw new RuntimeException(e);
  65.             }
  66.         });
  67.         // 左移按钮点击事件
  68.         btnMoveLeft.setOnClickListener(v -> {
  69.             try {
  70.                 // 查找当前获得焦点的视图
  71.                 View focusedView = recyclerView.findFocus();
  72.                 // 如果RecyclerView没有获得焦点
  73.                 if (focusedView != null) {
  74.                     // 获取RecyclerView的子类第0个item
  75.                     int position = recyclerView.getChildAdapterPosition(focusedView);
  76.                     Log.d(TAG, "当前获得焦点的Item位置: " + position);
  77.                 } else {
  78.                     Log.d(TAG, "没有任何Item获得焦点");
  79.                     if (recyclerView.getLayoutManager() != null) {
  80.                         // 如果没有获得焦点的视图,默认让第一个可见项获得焦点
  81.                         int firstPosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
  82.                         View positionChild = recyclerView.getLayoutManager().findViewByPosition(firstPosition);
  83.                         if (positionChild != null) {
  84.                             positionChild.requestFocus();// 让第一个Item获得焦点
  85.                         }
  86.                     }
  87.                 }
  88.                 Runtime.getRuntime().exec("input keyevent 21");
  89.             } catch (IOException e) {
  90.                 throw new RuntimeException(e);
  91.             }
  92.         });
  93.         // 确认按钮点击事件
  94.         btnEnter.setOnClickListener(v -> {
  95.             try {
  96.                 Runtime.getRuntime().exec("input keyevent 66");
  97.             } catch (IOException e) {
  98.                 throw new RuntimeException(e);
  99.             }
  100.         });
  101.         myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
  102.             @Override
  103.             public void itemClick(View view, int position) {
  104.                 Toast.makeText(getApplicationContext(), list.get(position).get("title").toString(), Toast.LENGTH_SHORT).show();
  105.             }
  106.         });
  107.         // 设置RecyclerView Item键盘事件监听
  108.         myAdapter.setOnIconKeyListener(new MyAdapter.OnIconKeyListener() {
  109.             @Override
  110.             public boolean onKey(View v, int keyCode, KeyEvent event, int position) {
  111.                 // 获取按键动作类型
  112.                 final int action = event.getAction();
  113.                 // 检查按键是否为按下动作
  114.                 final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
  115.                 // 标记按键是否被处理
  116.                 boolean wasHandled = false;
  117.                 switch (keyCode) {
  118.                     // 左键按下事件
  119.                     case KeyEvent.KEYCODE_DPAD_LEFT:
  120.                         // 不是手指抬起操作
  121.                         if (handleKeyEvent) {
  122.                             // 如果当前的 Item 是 LinearLayout
  123.                             if (v instanceof LinearLayout) {
  124.                                 // 当前当前的 Item父类 是 RecyclerView
  125.                                 if (v.getParent() instanceof RecyclerView) {
  126.                                     // 如果当前项在一列最后一项 或 第0项
  127.                                     if (position % numColumns == 0) {
  128.                                         // position的位置一定要 >= 0,因为这里要进行换行了
  129.                                         if (position - 1 >= 0) {
  130.                                             if (recyclerView.getLayoutManager() != null) {
  131.                                                 // 这里进行位置 -1,如果是屏幕看不到上一行,就会为Null
  132.                                                 View positionChild = recyclerView.getLayoutManager().findViewByPosition(position - 1);
  133.                                                 if (positionChild != null) {
  134.                                                     // 将焦点移动到前一个Item
  135.                                                     positionChild.requestFocus();
  136.                                                 } else {
  137.                                                     // 如果当前屏幕看不到上一个Item时,这里就会为 null,然后上滑到前一项。
  138.                                                     // 平滑滚动到前一项
  139.                                                     recyclerView.smoothScrollToPosition(position - 1);
  140.                                                     try {
  141.                                                         // 再次执行左键按下
  142.                                                         Runtime.getRuntime().exec("input keyevent 21");
  143.                                                     } catch (IOException e) {
  144.                                                         throw new RuntimeException(e);
  145.                                                     }
  146.                                                 }
  147.                                                 wasHandled = true;
  148.                                             }
  149.                                         }
  150.                                     }
  151.                                 }
  152.                             }
  153.                         }
  154.                         break;
  155.                     case KeyEvent.KEYCODE_DPAD_RIGHT:
  156.                         // 不是手指抬起操作
  157.                         if (handleKeyEvent) {
  158.                             // 如果当前的 Item 是 LinearLayout
  159.                             if (v instanceof LinearLayout) {
  160.                                 // 当前当前的 Item父类 是 RecyclerView
  161.                                 if (v.getParent() instanceof RecyclerView) {
  162.                                     // 当前的位置+1 < adapter的item总数
  163.                                     if (position + 1 < myAdapter.getItemCount()) {
  164.                                         // 如果 当前位置+1 % 列数 =0,表示下一个是下一行了
  165.                                         if ((position + 1) % numColumns == 0) {
  166.                                             if (recyclerView.getLayoutManager() != null) {
  167.                                                 // 获取下一个item,如果是屏幕看不到下一行,就会为Null
  168.                                                 View positionChild = recyclerView.getLayoutManager().findViewByPosition(position + 1);
  169.                                                 if (positionChild != null) {
  170.                                                     // 将焦点移动到下一个Item
  171.                                                     positionChild.requestFocus();
  172.                                                 } else {
  173.                                                     // 如果当前屏幕看不到下一个Item时,这里就会为 null,然后下滑到前一项。
  174.                                                     // 平滑滚动到下一项
  175.                                                     recyclerView.smoothScrollToPosition(position + 1);
  176.                                                     try {
  177.                                                         Runtime.getRuntime().exec("input keyevent 22");
  178.                                                     } catch (IOException e) {
  179.                                                         throw new RuntimeException(e);
  180.                                                     }
  181.                                                 }
  182.                                             }
  183.                                             // 返回true,事件自己消费处理了。
  184.                                             wasHandled = true;
  185.                                         }
  186.                                     } else if (position + 1 == myAdapter.getItemCount()) {
  187.                                         wasHandled = true;
  188.                                     }
  189.                                 }
  190.                             }
  191.                         }
  192.                         break;
  193.                 }
  194.                 return wasHandled;
  195.             }
  196.         });
  197.     }
  198. }
复制代码
RecyclerView相关方法




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4