recyclerview
。使用dpad滚动是有效的,同时项目也获得了焦点。
但当我按住按钮时,会触发许多keydown事件,导致快速滚动,而项目失去焦点,无法再滚动,因为我recyclerview上方的另一个Textview
正在获得焦点。
这看起来像一个bug。有没有解决方法?recyclerview
。使用dpad滚动是有效的,同时项目也获得了焦点。
但当我按住按钮时,会触发许多keydown事件,导致快速滚动,而项目失去焦点,无法再滚动,因为我recyclerview上方的另一个Textview
正在获得焦点。
这看起来像一个bug。有没有解决方法?我认为@tmm1的回答是目前为止最好的答案。我已经成功地实现了这个解决方法,解决了在用户使用DPAD快速滚动时,RecyclerView中加载的元素失去焦点的问题。
在我的RecyclerView适配器中,我使用LayoutInflater来膨胀我的元素视图,如下所示:
@Override
public ListItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_keyboard_key, viewGroup, false);
return new ListItemViewHolder(itemView);
}
我的 item_keyboard_key.xml 最初是这样定义的
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_keyboard_key_layout"
android:orientation="vertical"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_keyboard_key_text"
android:text="A"/>
</FrameLayout>
然后我创建了一个新的自定义FrameLayout类(FocusFixFrameLayout),扩展了FrameLayout,并将其用作根布局。我的item_keyboard_key.xml文件变成了:
<?xml version="1.0" encoding="utf-8"?>
<no.bluebit.views.FocusFixFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_keyboard_key_layout"
android:orientation="vertical"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_keyboard_key_text"
android:text="A"/>
</no.bluebit.views.FocusFixFrameLayout>
public class FocusFixFrameLayout extends FrameLayout {
public FocusFixFrameLayout(@NonNull Context context) {
super(context);
}
public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void clearFocus() {
if(this.getParent() != null)
super.clearFocus();
}
}
我也面临着同样的问题,在我的Activity中(包含RecyclerView),我采用以下方式解决:
private long mLastKeyDownTime = 0;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
long current = System. currentTimeMillis();
boolean res = false;
if (current - mLastKeyDownTime < 300 ) {
res = true;
} else {
res = super.onKeyDown(keyCode, event);
mLastKeyDownTime = current;
}
return res;
}
当你按下dpad上的按钮时,它可以避免快速滚动,并且在我的RecyclerView中焦点工作正常。
看起来这是一个bug。
请参见https://dev59.com/bVwZ5IYBdhLWcg3wVO5U#33190656和https://issuetracker.google.com/issues/37067220
我通过使我的项目视图上的clearFocus()
无效来解决了此问题,因此分离的视图失去焦点不会将焦点移动到RecyclerView之外:
override fun clearFocus() {
if (parent != null)
super.clearFocus()
}
parent
是从哪里来的? - mcoparent
is defined on FrameLayout
/ViewGroup
/View
- tmm1getParent()
方法但是没有公共/受保护的parent
变量。 - mcoparent
调用了 getParent()
。 - tmm1当我使用notifyItemChanged(position)
时,我遇到了同样的问题。
通过这样发送一个空载荷和位置notifyItemChanged(position, Any())
来解决问题。
2023年更新的答案:
当使用DPAD时,Recyclerview会失去焦点,这是一个已知的bug。我使用的一个小技巧是重写onBackPressed并添加以下内容:
@Override
public void onBackPressed() {
Log.i("MainActivity", "getCurrentFocus(): "+getCurrentFocus());
}
getCurrentFocus(): androidx.recyclerview.widget.RecyclerView{12c0bcc VFED.VC.. .F...... 0,0-1843,864 #7f0a016a app:id/recycler_view}
通过这个方法,你可以知道哪个视图获取了焦点,并且你可以为该视图添加一个onFocusChange监听器,或者完全禁用该视图的焦点功能。重要的是,在RecyclerView失去焦点时,你要找出谁获得了焦点。
尝试使用notifyItemChanged(position),这对我很有效。我在适配器内部调用了适配器,并且我的子适配器有多个EditText,当我调用notifyDataSetChanged时会失去焦点。因此,我用notifyItemChanged(Position)替换了它。
notifyItemChanged(position);
clearFocus
,正如许多人建议的那样,但对于较大的RecyclerView项目,这并不总是有效。
当使用d-pad滚动RecyclerView时,焦点会丢失,因为它试图聚焦列表上的下一项,而该项似乎没有显示在屏幕上,也没有被回收,因此它会聚焦在屏幕上的其他现有视图。
所以这是我的方法:
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
val pos = getPosition(focused)
val count = itemCount
if (direction == View.FOCUS_DOWN && pos + 1 < count) {
scrollToPosition(pos + 1)
}
return super.onInterceptFocusSearch(focused, direction)
}
我在为Android TV和leanback库工作的项目中遇到了同样的问题。
好消息是,流行的应用程序(如YouTube等)没有这个问题。我找到了有用的资源。经过调查,我制定了一个对我来说方便的解决方案。关键是使用Presenter而不是Adapter。以下是最简单的示例:
//Presenter class
public class ItemViewPresenter extends Presenter {
@Override
public final ViewHolder onCreateViewHolder(ViewGroup parent) {
//I use data binding
ItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);
return new MyViewHolder(binding);
}
@Override
public final void onBindViewHolder(ViewHolder viewHolder, Object item) {
String text = (String) item;
((MyViewHolder) viewHolder).bind(text);
}
@Override
public final void onUnbindViewHolder(ViewHolder viewHolder) {
}
private class MyViewHolder extends Presenter.ViewHolder {
private final ItemBinding binding;
private DiscoveryViewHolder(ItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
private void bind(String text) {
binding.textView.setText(text);
}
}
}
//...
private void setupRecyclerView() {
RecyclerView recyclerView = _baseLayout.findViewById(R.id.recyclerView);
ArrayObjectAdapter arrayObjectAdapter = new ArrayObjectAdapter(new ItemViewPresenter());
ItemBridgeAdapter bridgeAdapter = new ItemBridgeAdapter(arrayObjectAdapter);
recyclerView.setAdapter(bridgeAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(_baseLayout.getContext(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
List<Strings> textList = new ArrayList(1);
textList.add("1");
//add objects
arrayObjectAdapter.addAll(0, textList);
//clear objects
//arrayObjectAdapter.clear();
}