You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:18:24