Android 向RecyclerView添加适当的键盘支持

Android 向RecyclerView添加适当的键盘支持,android,android-recyclerview,android-keypad,Android,Android Recyclerview,Android Keypad,在我之前的一篇文章中,我问(并在博客文章后回答)如何正确处理RecyclerView上的按键输入 现在我意识到,如果我一直按箭头键,比方说向下键,向下滚动就会停止,而RecyclerView会失去焦点,这可能是因为滚动比生成所有子视图的速度都快 在RecyclerView上正确处理硬件键盘输入是否有任何解决方法或更好的做法 更新: 我发布了一个基本示例,它现在可以完美地工作,不再丢失焦点。我成功实现了一个抽象适配器类,能够跟踪所选项目而不丢失项目焦点,可以找到一个示例项目,适配器类的具体实现如下

在我之前的一篇文章中,我问(并在博客文章后回答)如何正确处理
RecyclerView
上的按键输入

现在我意识到,如果我一直按箭头键,比方说向下键,向下滚动就会停止,而
RecyclerView
会失去焦点,这可能是因为滚动比生成所有子视图的速度都快

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

更新:


我发布了一个基本示例,它现在可以完美地工作,不再丢失焦点。

我成功实现了一个抽象适配器类,能够跟踪所选项目而不丢失项目焦点,可以找到一个示例项目,适配器类的具体实现如下:

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;
        }
    }


}
导入android.content.Context;
导入android.support.v7.widget.RecyclerView;
导入android.view.KeyEvent;
导入android.view.view;
/**
*维克托于2016年5月31日创建。
*/
公共抽象类InputTrackingRecycleServiceAdapter扩展了RecyclerView.Adapter{
私有上下文;
private int mSelectedItem=0;
私人回收视图mRecyclerView;
公共InputTrackingRecycleServiceAdapter(上下文){
mContext=上下文;
}
@凌驾
附加ToRecyclerView(最终回收视图回收视图)上的公共空间{
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView=回收视图;
//处理向上键和向下键,并尝试移动所选内容
recyclerView.setOnKeyListener(新视图.OnKeyListener(){
@凌驾
公共布尔onKey(视图v、int keyCode、KeyEvent事件){
RecyclerView.LayoutManager lm=RecyclerView.getLayoutManager();
//如果滚动到边界并允许焦点移出列表,则返回false
if(event.getAction()==KeyEvent.ACTION\u向下){
如果(IsConfigerButton(事件)){
if((event.getFlags()&KeyEvent.FLAG\u LONG\u PRESS)==KeyEvent.FLAG\u LONG\u PRESS){
mRecyclerView.findViewHolderForAdapterPosition(mSelectedItem.itemView.performLongClick();
}否则{
event.startTracking();
}
返回true;
}
否则{
if(keyCode==KeyEvent.keyCode\u DPAD\u DOWN){
返回选举(lm,1);
}else if(keyCode==KeyEvent.keyCode\u DPAD\u UP){
返回tryMoveSelection(lm,-1);
}
}
}
else if(event.getAction()==KeyEvent.ACTION\u UP&&isConfigerButton(事件)
&&((event.getFlags()&KeyEvent.FLAG_LONG_PRESS)!=KeyEvent.FLAG_LONG_PRESS)){
mRecyclerView.findViewHolderForAdapterPosition(mSelectedItem.itemView.performClick();
返回true;
}
返回false;
}
});
}
私有布尔trymovesection(RecyclerView.LayoutManager lm,int方向){
int-nextSelectItem=mSelectedItem+方向;
//如果仍在有效范围内,请移动所选内容,通知重画,然后滚动
如果(nextSelectItem>=0&&nextSelectItem
您是否有自己的电脑?昨天,我刚刚在一个
RecyclerView
中添加了键盘支持,与您的做法类似。在我的例子中,它是一个全屏
RecyclerView
,所以我没有任何焦点问题。如果我们有一个问题的具体实现,人们将更容易帮助你解决问题。@commonware我将尽快创建我的示例。我认为您可以尝试在RecyclerView下面添加一个可聚焦视图(例如按钮),并在适配器中使用一个大型数据集,比如说,100个项目。你应该看到focus的行为很奇怪。我明天会再讨论这个问题。我看到了其他奇怪的结果(例如,涂抹效应)。我认为我们可能需要等待第二次更新
RecyclerView
,直到第一次完成处理,而不是每个关键事件更新一次。尽管如此,在垂直滚动列表下面有一个可聚焦视图并不是一个好的键盘用户体验。你会在Android TV(或其Google TV前身Fire TV等)编写应用程序的报道中看到关于这类事情的讨论。@commonware我可能已经自己解决了这个问题,我从零开始编写代码,我得到了这个工作示例