Android Fragment获取RecyclerView选中项位置实现方案
问题背景
- 当前RecyclerView相关代码基础运行正常:
RecycleAdapter类内可通过Toast正常提示选中的RecyclerView条目位置,getAbsoluteAdapterPosition()方法在Adapter类中可正常调用 - 需求:在
HomeFragment类中获取RecyclerView选中项位置,实现选中条目后跳转至其他Fragment的逻辑 - 已尝试方案及报错:
- 直接在HomeFragment中给条目内的ImageView、TextView设置点击监听,触发空指针异常
- 直接给
binding.recyclerview设置setOnClickListener无报错,但点击RecyclerView条目时监听不会触发 - 空指针原因已明确:条目子控件是在Adapter类中加载的,无法在HomeFragment中直接引用
现有代码
HomeFragment.java
public class HomeFragment extends Fragment { HomeFragmentBinding binding; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.id.home_fragment, container, false); return v; } public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { %% some code here %% recycleAdapter = new RecycleAdapter(list); binding.recyclerview.setAdapter(recycleAdapter); TextView txt = (TextView) binding.getRoot().findViewById(R.id.recycler_text); txt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getContext(), "Clicked on Image", Toast.LENGTH_SHORT).show(); } }); binding.recyclerview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getContext(), "RecyclerView clicked", Toast.LENGTH_SHORT).show(); Log.d("RecyclerViewclicked", " --- "); } }); } }
RecycleAdapter.java
public class RecycleAdapter extends RecyclerView.Adapter<RecycleAdapter.MyViewHolder>{ RecyclerRowItemsBinding binding; List<IconsModel> iconsList; public RecycleAdapter(List<IconsModel> iconslist) { this.iconsList = iconslist; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflator = LayoutInflater.from(parent.getContext()); View v = inflator.inflate(R.layout.recycler_row_items, parent, false); MyViewHolder myviewholder = new MyViewHolder(v); return myviewholder; } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { IconsModel iconModel = iconsList.get(position); holder.imageView.setImageResource(iconModel.getImageid()); holder.recyclertext.setText(iconModel.getIconName()); } @Override public int getItemCount() { //return 20; return iconsList.size(); } class MyViewHolder extends RecyclerView.ViewHolder{ ImageView imageView; TextView recyclertext; public MyViewHolder(@NonNull View itemView) { super(itemView); imageView = itemView.findViewById(R.id.recycler_image); recyclertext = itemView.findViewById(R.id.recycler_text); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { IconsModel iconModel = iconsList.get(getAbsoluteAdapterPosition()); Toast.makeText(itemView.getContext(), "Clicked on "+iconModel.getIconName(), Toast.LENGTH_SHORT).show(); } }); } } }
home_fragment.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" tools:layout_editor_absoluteX="1dp" tools:layout_editor_absoluteY="1dp" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"/> </androidx.constraintlayout.widget.ConstraintLayout>
recycler_row.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools" android:layout_gravity="center" android:layout_margin="5dp" android:orientation="horizontal" app:cardCornerRadius="5dp" app:cardElevation="5dp" app:cardBackgroundColor="@color/home_bg_cardview"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" android:backgroundTint="@color/home_bg_cardview" android:weightSum="2"> <ImageView android:id="@+id/recycler_image" android:layout_width="0dp" android:layout_height="120dp" android:layout_weight="0.5" app:srcCompat="?android:attr/actionModeWebSearchDrawable" tools:ignore="ContentDescription" /> <TextView android:id="@+id/recycler_text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginBottom="29dp" android:layout_weight="1.5" android:gravity="center" android:text="Browse Items" android:textAlignment="center" android:textSize="24sp" /> </LinearLayout> </androidx.cardview.widget.CardView>
实现方案
核心逻辑为自定义回调接口,将Adapter内部捕获的点击事件、选中位置、对应数据透传给HomeFragment,无需在Fragment中直接操作Adapter内部的条目控件。
- 在
RecycleAdapter类中定义条目点击回调接口,约定回调传参public interface OnItemClickListener { // 回调传入选中位置、对应的数据实体,可根据自身需求调整参数 void onItemClick(int position, IconsModel selectedItem); } - 给
RecycleAdapter添加回调成员变量,修改构造方法接收外部传入的监听器实例public class RecycleAdapter extends RecyclerView.Adapter<RecycleAdapter.MyViewHolder>{ List<IconsModel> iconsList; // 新增点击回调成员变量 private OnItemClickListener itemClickListener; // 修改构造方法,接收外部传入的监听器 public RecycleAdapter(List<IconsModel> iconslist, OnItemClickListener listener) { this.iconsList = iconslist; this.itemClickListener = listener; } // 其余原有代码保持不变 } - 修改ViewHolder内的条目点击逻辑,点击触发时调用回调方法,将数据传递给外部
itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int selectedPos = getAbsoluteAdapterPosition(); // 过滤位置无效的边界场景(如条目动画移除时返回NO_POSITION的情况) if (selectedPos == RecyclerView.NO_POSITION || itemClickListener == null) { return; } IconsModel selectedModel = iconsList.get(selectedPos); // 保留原有Adapter内的提示逻辑 Toast.makeText(itemView.getContext(), "Clicked on "+selectedModel.getIconName(), Toast.LENGTH_SHORT).show(); // 触发回调,将数据传给外部实现类 itemClickListener.onItemClick(selectedPos, selectedModel); } }); - 修复HomeFragment原有代码错误,初始化Adapter时实现回调接口,在回调中编写Fragment跳转逻辑
- 修复
onCreateView中ViewBinding初始化错误:原代码inflate参数误用id、未给binding赋值,会触发空指针 - 删除原有在Fragment中查找条目子控件、给RecyclerView本身设置点击监听的无效代码
- 初始化Adapter时传入回调实例,在回调方法中获取选中数据执行跳转
修正后的HomeFragment核心代码:
public class HomeFragment extends Fragment { HomeFragmentBinding binding; private RecycleAdapter recycleAdapter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 正确初始化ViewBinding,inflate传入布局文件而非id binding = HomeFragmentBinding.inflate(inflater, container, false); return binding.getRoot(); } public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // 原有列表数据初始化逻辑 // List<IconsModel> list = xxx; // 初始化Adapter时传入点击回调 recycleAdapter = new RecycleAdapter(list, new RecycleAdapter.OnItemClickListener() { @Override public void onItemClick(int position, IconsModel selectedItem) { // 此处可直接获取选中位置、对应数据,编写跳转逻辑即可 Toast.makeText(getContext(), "选中位置:" + position + ",选中项:" + selectedItem.getIconName(), Toast.LENGTH_SHORT).show(); // 跳转示例,根据自身项目的导航框架编写即可 // Bundle args = new Bundle(); // args.putInt("selected_position", position); // Navigation.findNavController(view).navigate(R.id.action_home_to_target_fragment, args); } }); binding.recyclerview.setAdapter(recycleAdapter); } } - 修复
额外注意事项
- 现有代码存在布局名不匹配问题:Adapter中加载的条目布局为
R.layout.recycler_row_items,但提供的条目布局文件名为recycler_row.xml,需保持二者命名一致,否则会触发布局加载异常 - RecyclerView条目为滚动过程中动态创建、复用的,永远不要在Fragment/Activity中直接通过
findViewById查找条目内的子控件,该操作大概率返回null触发空指针 - RecyclerView作为容器控件,子条目会优先消费点击事件,直接给RecyclerView本身设置点击监听永远不会被触发
内容的提问来源于stack exchange,提问作者appsy
相关产品推荐
相关产品推荐

