【问题标题】:Adding proper keyboard support to RecyclerView为 RecyclerView 添加适当的键盘支持
【发布时间】:2016-05-26 12:56:25
【问题描述】:

在我之前的一个questions 中,我问过(并在this 博客帖子之后由我自己回答)如何正确处理RecyclerView 上的键输入。

现在我意识到,如果我按住箭头键,比如说向下键,向下滚动停止并且RecyclerView 失去焦点,可能是因为滚动速度比所有子代Views 的生成速度都要快.

是否有任何解决方法或更好的做法来正确处理RecyclerView 上的硬件键盘输入?

更新:

我发布了一个基本示例here,它现在可以完美运行,不再有焦点损失。

【问题讨论】:

  • 你有minimal reproducible example吗?昨天我只是在RecyclerView 中添加键盘支持,就像你做的那样。就我而言,它是全屏RecyclerView,所以我没有任何焦点问题。如果我们对问题有具体的实施,人们会更容易帮助您解决问题。
  • @CommonsWare 我将尽快创建我的示例。我认为您可以尝试在 RecyclerView 下方添加一个可聚焦的视图(例如一个按钮),并在适配器中使用一个大型数据集,比如说 100 个项目。您应该会看到焦点的异常行为。
  • 我明天会再戳这个。我看到了其他奇怪的结果(例如,拖尾效应)。我想也许我们需要等待第二次更新RecyclerView,直到第一次完成处理之后,而不是每个键事件一个。话虽如此,在垂直滚动列表下方有一个可聚焦的视图首先并不是一个好的键盘 UX。您将在为 Android TV(或其前身 Google TV、Fire TV 等)编写应用程序的报道中看到有关此类事情的讨论。
  • @CommonsWare 我可能已经自己解决了这个问题,我从头开始编码,我得到了这个工作示例github.com/zevektor/KeyboardRecyclerView

标签: android android-recyclerview android-keypad


【解决方案1】:

我设法实现了一个抽象的Adapter类,能够在不丢失item焦点的情况下跟踪选中的item,可以找到一个示例项目here,adapter类的具体实现如下:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.View;


/**
 * Created by vektor on 31/05/16.
 */
public abstract class InputTrackingRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH>{

    private Context mContext;
    private int mSelectedItem = 0;
    private RecyclerView mRecyclerView;

    public InputTrackingRecyclerViewAdapter(Context context){
        mContext = context;
    }

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        mRecyclerView = recyclerView;
        // Handle key up and key down and attempt to move selection
        recyclerView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

                // Return false if scrolled to the bounds and allow focus to move off the list
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (isConfirmButton(event)) {
                        if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == KeyEvent.FLAG_LONG_PRESS) {
                            mRecyclerView.findViewHolderForAdapterPosition(mSelectedItem).itemView.performLongClick();
                        } else {
                            event.startTracking();
                        }
                        return true;
                    }
                    else {
                        if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                            return tryMoveSelection(lm, 1);
                        } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                            return tryMoveSelection(lm, -1);
                        }
                    }
                }
                else if(event.getAction() == KeyEvent.ACTION_UP && isConfirmButton(event)
                        && ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != KeyEvent.FLAG_LONG_PRESS)){
                    mRecyclerView.findViewHolderForAdapterPosition(mSelectedItem).itemView.performClick();
                    return true;
                }
                return false;
            }
        });
    }

    private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
        int nextSelectItem = mSelectedItem + direction;

        // If still within valid bounds, move the selection, notify to redraw, and scroll
        if (nextSelectItem >= 0 && nextSelectItem < getItemCount()) {
            notifyItemChanged(mSelectedItem);
            mSelectedItem = nextSelectItem;
            notifyItemChanged(mSelectedItem);
            //lm.scrollToPosition(mSelectedItem);
            mRecyclerView.smoothScrollToPosition(mSelectedItem);
            return true;
        }

        return false;
    }

    public Context getContext(){ return mContext; }

    public int getSelectedItem() { return mSelectedItem; }
    public void setSelectedItem(int selectedItem) { mSelectedItem = selectedItem; }

    public RecyclerView getRecyclerView() { return mRecyclerView; }


    @Override
    public void onBindViewHolder(VH holder, int position) {
        onBindViewHolder(holder, position);
    }

    public static boolean isConfirmButton(KeyEvent event){
        switch (event.getKeyCode()){
            case KeyEvent.KEYCODE_ENTER:
            case KeyEvent.KEYCODE_DPAD_CENTER:
            case KeyEvent.KEYCODE_BUTTON_A:
                return true;
            default:
                return false;
        }
    }


}

【讨论】:

    猜你喜欢
    • 2011-12-17
    • 2016-01-11
    • 2019-12-24
    • 2020-04-17
    • 2017-03-17
    • 1970-01-01
    • 2021-08-16
    • 2012-04-18
    • 1970-01-01
    相关资源
    最近更新 更多