RecyclerView选中项时onBindViewHolder未调用的问题求助
问题描述
滚动列表加载正常,初始加载和滚动加载新项时onBindViewHolder都会被调用。但点击列表项时,ItemDetailsLookup和getItemDetails函数会触发,却未调用onBindViewHolder,导致setActivated无法执行。根据androidx.recyclerview.selection文档,选中项时SelectionTracker应通知RecyclerView刷新并触发onBindViewHolder,但可能遗漏了关键配置。
原代码
Fragment代码
package com.example.smartflashcards; import android.content.Context; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.selection.ItemDetailsLookup; import androidx.recyclerview.selection.SelectionPredicates; import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.selection.StableIdKeyProvider; import androidx.recyclerview.selection.StorageStrategy; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class StackSelectionFragment extends Fragment { private static final String ARG_COLUMN_COUNT = "column-count"; private int mColumnCount = 1; static SelectionTracker<Long> tracker; public StackSelectionFragment() { } @SuppressWarnings("unused") public static StackSelectionFragment newInstance(int columnCount) { StackSelectionFragment fragment = new StackSelectionFragment(); Bundle args = new Bundle(); args.putInt(ARG_COLUMN_COUNT, columnCount); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_stack_selection_list, container, false); File directory = getActivity().getFilesDir(); List<String> filesList = new ArrayList(Arrays.asList(directory.list())); if (view instanceof RecyclerView) { Context context = view.getContext(); RecyclerView recyclerView = (RecyclerView) view; if (mColumnCount <= 1) { recyclerView.setLayoutManager(new LinearLayoutManager(context)); } else { recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); } recyclerView.setAdapter(new MyItemRecyclerViewAdapter(filesList)); recyclerView.getAdapter().hasStableIds(); this.tracker = new SelectionTracker.Builder<Long>( "stack_selector", recyclerView, new StableIdKeyProvider(recyclerView), new MyDetailsLookup(recyclerView), StorageStrategy.createLongStorage() ).withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything()).build(); } return view; } final class MyDetailsLookup extends ItemDetailsLookup { private final RecyclerView mRecyclerView; MyDetailsLookup(RecyclerView recyclerView) { mRecyclerView = recyclerView; } public @Nullable ItemDetails getItemDetails(@NonNull MotionEvent e) { View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY()); if (view != null) { RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view); if (holder instanceof MyItemRecyclerViewAdapter.ViewHolder) { return ((MyItemRecyclerViewAdapter.ViewHolder)holder).getItemDetails(); } } return null; } } }
Adapter代码
package com.example.smartflashcards; import androidx.annotation.Nullable; import androidx.recyclerview.selection.ItemDetailsLookup; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.TextView; import com.example.smartflashcards.databinding.FragmentStackSelectionBinding; import java.util.List; public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> { private final List<String> mValues; public MyItemRecyclerViewAdapter(List<String> items) { setHasStableIds(true); mValues = items; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(FragmentStackSelectionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } @Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.mItem = getItemId(position); holder.mContentView.setText(mValues.get(position)); holder.mContentView.setActivated(StackSelectionFragment.tracker.isSelected((long) position)); } @Override public int getItemCount() { return mValues.size(); } @Override public long getItemId(int position) { return (long)position; } public class ViewHolder extends RecyclerView.ViewHolder { public final TextView mIdView; public final TextView mContentView; public Long mItem; public ViewHolder(FragmentStackSelectionBinding binding) { super(binding.getRoot()); mIdView = binding.itemNumber; mContentView = binding.content; } @Override public String toString() { return super.toString() + " '" + mContentView.getText() + "'"; } public ItemDetailsLookup.ItemDetails<Long> getItemDetails() { return new ItemDetailsLookup.ItemDetails<Long>() { @Override public int getPosition() { return getAbsoluteAdapterPosition(); } @Nullable @Override public Long getSelectionKey() { return mItem; } }; } } }
修复方案
核心问题是未将SelectionTracker与Adapter关联,也未监听选择状态变化来触发RecyclerView刷新,以下是具体修复步骤:
1. 修改Adapter代码
替换静态tracker引用为实例变量,添加选择状态监听并触发项刷新:
public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> { private final List<String> mValues; private SelectionTracker<Long> tracker; public MyItemRecyclerViewAdapter(List<String> items) { setHasStableIds(true); mValues = items; } // 添加设置tracker的方法,同时注册选择观察者 public void setTracker(SelectionTracker<Long> tracker) { this.tracker = tracker; tracker.addObserver(new SelectionTracker.SelectionObserver<Long>() { @Override public void onItemStateChanged(@NonNull Long key, boolean selected) { super.onItemStateChanged(key, selected); // 根据选中的key找到对应position,触发单条刷新 int position = (int) key.longValue(); notifyItemChanged(position); } }); } @Override public void onBindViewHolder(final ViewHolder holder, int position) { long itemId = getItemId(position); holder.mContentView.setText(mValues.get(position)); // 使用实例变量判断选中状态,避免空指针 holder.mContentView.setActivated(tracker != null && tracker.isSelected(itemId)); } // ... 保留原有其他代码 public class ViewHolder extends RecyclerView.ViewHolder { // ... 保留原有其他代码 public ItemDetailsLookup.ItemDetails<Long> getItemDetails() { return new ItemDetailsLookup.ItemDetails<Long>() { @Override public int getPosition() { return getAbsoluteAdapterPosition(); } @Nullable @Override public Long getSelectionKey() { // 直接返回当前项的ID,无需依赖mItem变量 return getItemId(getAbsoluteAdapterPosition()); } }; } } }
2. 修改Fragment代码
移除静态tracker,创建后传递给Adapter,并删除无效代码:
public class StackSelectionFragment extends Fragment { private static final String ARG_COLUMN_COUNT = "column-count"; private int mColumnCount = 1; private SelectionTracker<Long> tracker; // 改为实例变量 // ... 保留原有其他代码 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_stack_selection_list, container, false); File directory = getActivity().getFilesDir(); List<String> filesList = new ArrayList(Arrays.asList(directory.list())); if (view instanceof RecyclerView) { Context context = view.getContext(); RecyclerView recyclerView = (RecyclerView) view; if (mColumnCount <= 1) { recyclerView.setLayoutManager(new LinearLayoutManager(context)); } else { recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); } MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(filesList); recyclerView.setAdapter(adapter); // 创建tracker this.tracker = new SelectionTracker.Builder<Long>( "stack_selector", recyclerView, new StableIdKeyProvider(recyclerView), new MyDetailsLookup(recyclerView), StorageStrategy.createLongStorage() ).withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything()).build(); // 将tracker传递给Adapter adapter.setTracker(tracker); } return view; } // ... 保留原有其他代码 }
额外优化说明
- 移除了
recyclerView.getAdapter().hasStableIds();无效代码(Adapter构造中已设置setHasStableIds(true)) - 废弃静态
tracker变量,避免内存泄漏和状态混乱 - 在
getItemDetails中直接返回项ID,消除对mItem变量的依赖,避免空指针或状态不一致问题
内容的提问来源于stack exchange,提问作者Keenan
相关产品推荐
相关产品推荐

