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

Android Support Library 28.0.0-alpha1 RecyclerView-Selection API实现求助

Hey there! Let's walk through getting the RecyclerView Selection API working with a Contextual Action Bar (CAB) and your desired actions like delete/share. I'll break this down step by step based on the 28.0.0-alpha1 support library.

Step 1: Verify Dependencies

First, make sure your app-level build.gradle includes the required libraries:

dependencies {
    implementation 'com.android.support:recyclerview-v7:28.0.0-alpha1'
    implementation 'com.android.support:recyclerview-selection:28.0.0-alpha1'
}
Step 2: Prepare Your RecyclerView Adapter

Your adapter needs to integrate with the Selection API by implementing ItemDetailsLookup.ItemDetails in its ViewHolder. This lets the tracker access item-specific data for selection logic.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private List<MyItem> items;
    private SelectionTracker<Long> selectionTracker;

    // Constructor, onCreateViewHolder, and basic adapter methods...

    public class MyViewHolder extends RecyclerView.ViewHolder implements ItemDetailsLookup.ItemDetails<Long> {
        TextView itemTitle;

        public MyViewHolder(View itemView) {
            super(itemView);
            itemTitle = itemView.findViewById(R.id.item_title);
        }

        @Override
        public int getPosition() {
            return getAdapterPosition();
        }

        @Override
        public Long getSelectionKey() {
            // Return a unique, stable key for each item (e.g., database ID)
            return items.get(getAdapterPosition()).getId();
        }

        @Override
        public boolean inSelectionHotspot(MotionEvent e) {
            // Allow selection to trigger from anywhere on the item (adjust if needed)
            return true;
        }
    }

    // Setter for the selection tracker
    public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
        this.selectionTracker = selectionTracker;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyItem item = items.get(position);
        holder.itemTitle.setText(item.getTitle());

        // Update item appearance based on selection state
        boolean isSelected = selectionTracker.isSelected(item.getId());
        holder.itemView.setActivated(isSelected);
    }

    // Helper methods to access and update data
    public List<MyItem> getItems() {
        return items;
    }

    public void updateItems(List<MyItem> newItems) {
        items = newItems;
        notifyDataSetChanged();
    }
}

Note: Ensure your item layout's root view uses android:background="?attr/selectableItemBackground" or a state-list drawable to show a visual highlight when selected.

Step 3: Set Up SelectionTracker & CAB in Your Fragment

In your Fragment's view initialization, set up the SelectionTracker, listen for selection changes to trigger the CAB, and handle action callbacks.

public class MyListFragment extends Fragment {
    private RecyclerView recyclerView;
    private MyAdapter adapter;
    private SelectionTracker<Long> selectionTracker;
    private ActionMode actionMode;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_list, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        recyclerView = view.findViewById(R.id.recycler_view);
        adapter = new MyAdapter(getSampleData()); // Replace with your data source
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

        // Initialize SelectionTracker
        selectionTracker = new SelectionTracker.Builder<>(
                "unique-selection-id", // Unique ID for saving/restoring state
                recyclerView,
                new ItemKeyProviderImpl(adapter),
                new ItemDetailsLookupImpl(recyclerView),
                StorageStrategy.createLongStorage()
        )
                .withSelectionPredicate(SelectionPredicates.createSelectAnything()) // Enable multi-selection
                .build();

        // Restore selection state after configuration changes
        if (savedInstanceState != null) {
            selectionTracker.onRestoreInstanceState(savedInstanceState);
        }

        adapter.setSelectionTracker(selectionTracker);

        // Listen for selection changes to show/hide CAB
        selectionTracker.addObserver(new SelectionTracker.SelectionObserver<Long>() {
            @Override
            public void onSelectionChanged() {
                super.onSelectionChanged();
                int selectedCount = selectionTracker.getSelectedItems().size();
                if (selectedCount > 0) {
                    if (actionMode == null) {
                        actionMode = requireActivity().startActionMode(actionModeCallback);
                    }
                    actionMode.setTitle(getString(R.string.selected_items, selectedCount));
                } else {
                    if (actionMode != null) {
                        actionMode.finish();
                    }
                }
            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        selectionTracker.onSaveInstanceState(outState);
    }

    // CAB Action Callback
    private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.menu_contextual, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.action_delete:
                    deleteSelectedItems();
                    mode.finish();
                    return true;
                case R.id.action_share:
                    shareSelectedItems();
                    mode.finish();
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            selectionTracker.clearSelection();
            actionMode = null;
        }
    };

    // Handle delete operation
    private void deleteSelectedItems() {
        Set<Long> selectedKeys = selectionTracker.getSelectedItems();
        List<MyItem> updatedItems = new ArrayList<>();
        for (MyItem item : adapter.getItems()) {
            if (!selectedKeys.contains(item.getId())) {
                updatedItems.add(item);
            }
        }
        adapter.updateItems(updatedItems);
    }

    // Handle share operation
    private void shareSelectedItems() {
        Set<Long> selectedKeys = selectionTracker.getSelectedItems();
        StringBuilder shareContent = new StringBuilder();
        for (MyItem item : adapter.getItems()) {
            if (selectedKeys.contains(item.getId())) {
                shareContent.append(item.getTitle()).append("\n");
            }
        }

        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_TEXT, shareContent.toString());
        startActivity(Intent.createChooser(shareIntent, getString(R.string.share_via)));
    }

    // Custom ItemKeyProvider
    private static class ItemKeyProviderImpl extends ItemKeyProvider<Long> {
        private MyAdapter adapter;

        public ItemKeyProviderImpl(MyAdapter adapter) {
            super(SCOPE_CACHED);
            this.adapter = adapter;
        }

        @Override
        public Long getKey(int position) {
            return adapter.getItems().get(position).getId();
        }

        @Override
        public int getPosition(Long key) {
            for (int i = 0; i < adapter.getItems().size(); i++) {
                if (adapter.getItems().get(i).getId().equals(key)) {
                    return i;
                }
            }
            return RecyclerView.NO_POSITION;
        }
    }

    // Custom ItemDetailsLookup
    private static class ItemDetailsLookupImpl extends ItemDetailsLookup<Long> {
        private RecyclerView recyclerView;

        public ItemDetailsLookupImpl(RecyclerView recyclerView) {
            this.recyclerView = recyclerView;
        }

        @Override
        public ItemDetails<Long> getItemDetails(MotionEvent e) {
            View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (view != null) {
                RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(view);
                if (holder instanceof MyAdapter.MyViewHolder) {
                    return ((MyAdapter.MyViewHolder) holder);
                }
            }
            return null;
        }
    }

    // Replace with your actual data fetching logic
    private List<MyItem> getSampleData() {
        List<MyItem> items = new ArrayList<>();
        // Add sample items here
        return items;
    }
}
Step 4: Create the Contextual Menu

Add a menu resource (res/menu/menu_contextual.xml) for the CAB actions:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_delete"
        android:title="@string/delete"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_share"
        android:title="@string/share"
        app:showAsAction="ifRoom"/>
</menu>
Common Pitfalls to Watch For
  • Stable Selection Keys: Never use item position as the selection key — positions change when items are added/removed. Use a unique, persistent ID instead.
  • Activated State: Ensure your item view uses the activated state to show selection highlights.
  • State Restoration: Always save and restore the SelectionTracker state to preserve selections across screen rotations.
  • Selection Predicate: Use SelectionPredicates.createSelectSingleAnything() if you need single-selection instead of multi-selection.

If you're still running into specific issues (like CAB not appearing, selection not persisting, or action callbacks failing), share the relevant code snippets or error logs and I can help troubleshoot further!

内容的提问来源于stack exchange,提问作者dmyoung9

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:49:45