冬雨财经 发表于 2024-9-23 02:42:07

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

        在TV上,用RecyclerView显示一个列表,飞鼠遥控左右遥控获得Item焦点,到最后一个举行右移动换行,是不能做到的,因此必要监听key事件处置惩罚换行。
结果图如下
https://i-blog.csdnimg.cn/direct/6d60934da3134ae18880c47f30dcfcb6.gif
代码实现

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


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android TV RecyclerView列表获得焦点左右换行