Android recyclerview-selection实施?

时间:2018-03-28 23:27:28

标签: android android-recyclerview multipleselection android-actionmode

我目前正在尝试从Android支持库28.0.0-alpha1实施新的recyclerview-selection API,并且遇到了一些问题。我的目标是拥有RecyclerView,能够选择多行,显示上下文操作栏,并对其执行操作,例如"删除"或"分享"

我会尝试提供足够的代码来充分了解正在发生的事情,但如果有必要,我可以随时回复。

在我关注的Fragment我的RecyclerView中,我正在发起SelectionTracker,并将其设置在RecyclerView.Adapter上,如下所示:

private void buildRecyclerView() {
    sheetsAdapter = new SheetsAdapter(getContext(), this, sheets);
    gridManager = new GridLayoutManager(getContext(), getResources().getInteger(R.integer.grid_span_count));

    ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(getContext(), R.dimen.item_offset);
    sheetsRecycler.addItemDecoration(itemDecoration);
    sheetsRecycler.setLayoutManager(gridManager);
    sheetsRecycler.setAdapter(sheetsAdapter);
    sheetsRecycler.setHasFixedSize(true);

    SelectionTracker selectionTracker = new SelectionTracker.Builder<>("sheet_selection",
                                                        sheetsRecycler,
                                                        new StableIdKeyProvider(sheetsRecycler),
                                                        new SheetDetailsLookup(sheetsRecycler),
                                                        StorageStrategy.createLongStorage())
                                                        .withOnContextClickListener(this)
                                                        .build();

    sheetsAdapter.setSelectionTracker(selectionTracker);
}

Fragmentimplements OnContextClickListener,以便倾听我RecyclerView中项目的长按:

@Override
public boolean onContextClick(@NonNull MotionEvent e) {
    if (actionMode != null) {
        return false;
    }

    // Start the CAB using the ActionMode.Callback defined below
    if (getActivity() != null) {
        actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
    }

    return true;
}

它应该 显示我的CAB,如下所示:

private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.sheets_cab_menu, 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.delete:
                Toast.makeText(getContext(), R.string.sheets_delete, Toast.LENGTH_SHORT).show();
                mode.finish();
                return true;
            default:
                return false;
        }
    }

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

我的SheetDetailsLookup看起来像这样:

public class SheetDetailsLookup extends ItemDetailsLookup<Long> {

private RecyclerView recyclerView;

SheetDetailsLookup(RecyclerView recyclerView) {
    super();

    this.recyclerView = recyclerView;
}

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

在我的SheetViewHolder中,我更新视图以显示它已被选中:

if (selectionTracker.isSelected(sheet.uid)) {
            layout.setBackgroundResource(R.color.md_grey_700);
        } else {
            layout.setBackgroundResource(android.R.color.transparent);
        }

以及:

public SheetItemDetails getItemDetails() {
        return new SheetItemDetails(getAdapterPosition(), mSheets.get(getAdapterPosition()).uid);
    }

SheetItemDetails只是:

public class SheetItemDetails extends ItemDetailsLookup.ItemDetails<Long> {

private int position;
private Long key;

SheetItemDetails(int position, Long key) {
    this.position = position;
    this.key = key;
}

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

@Nullable
@Override
public Long getSelectionKey() {
    return key;
}
}

我已经实施了API specification中提到的所有内容,但现在遇到了麻烦。当我选择一个项目时,我的CAB不显示...而且应用程序通常会崩溃。每当我尝试&#34;退出&#34;时就会发生崩溃。选择,然后长按以开始另一个选择,使用此堆栈跟踪:

java.lang.IllegalStateException
    at android.support.v4.util.Preconditions.checkState(Preconditions.java:130)
    at android.support.v4.util.Preconditions.checkState(Preconditions.java:142)
    at androidx.recyclerview.selection.GestureSelectionHelper.start(GestureSelectionHelper.java:76)
    at androidx.recyclerview.selection.SelectionTracker$Builder$4.run(SelectionTracker.java:742)
    at androidx.recyclerview.selection.TouchInputHandler.onLongPress(TouchInputHandler.java:136)
    at androidx.recyclerview.selection.GestureRouter.onLongPress(GestureRouter.java:95)
    at android.view.GestureDetector.dispatchLongPress(GestureDetector.java:779)
    at android.view.GestureDetector.access$200(GestureDetector.java:40)
    at android.view.GestureDetector$GestureHandler.handleMessage(GestureDetector.java:293)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

此外,我现在已经失去了&#34;短按&#34;在我的一个项目上,发布一个详细视图......到目前为止我工作得很好。

我做错了什么?

4 个答案:

答案 0 :(得分:4)

我最近开始查看该库,并遇到了相同的异常。当SelectionTracker试图从您的自定义RecyclerView.Adapter子类中获取ID时,就会发生问题。要解决此问题,请先在其构造函数中调用setHasStableIds(true)。然后覆盖getItemId()以返回给定位置参数的ID。

答案 1 :(得分:2)

新图书馆现在看起来很复杂。在开始实施之前,我会等待新的最终版本。当然,您可以尝试使用它,但我建议您暂时不要在应用中使用它。

只有一个不错的新功能:通过移动手指或鼠标来实现连续多选

但是,我找到了这些例子:

与此同时,我强烈建议使用像我这样的库:FlexibleAdapter,它来自超过3年的“选择经验”,它不会在(de)选择发生时绑定项目!使用ActionModeHelper进行多项选择很简单,可以使用ActionMode简化代码。阅读相关的Wiki page

目前,选择内容保存在Set中,但将来可能会将其委托给适配器项本身。但是,您可以对此扩展程序使用“选择”。

答案 2 :(得分:0)

选择包仍处于alpha状态,文档很差,并且不太清楚如何使用它。我试过自己,但我遇到了类似的问题,最后我使用了SmartRecyclerView

答案 3 :(得分:0)

两件事:

1)要点击项目,您需要实现一个OnItemActivatedListener<K>侦听器,并将对它的引用传递给跟踪器构建器。之后,您可以在项目上收到“触摸”。

2)要显示上下文操作栏菜单,需要使用不同的方法:您需要实现SelectionTracker.SelectionObserver,并将其创建后传递给跟踪器:tracker.addObserver(...。之后,您可以在该观察器中接收选择更改事件(通过onSelectionChanged回调)。例如,当选择开始(!tracker.getSelection().isEmpty() =>显示CAB)并且选择结束(tracker.getSelection()。isEmpty()隐藏CAB)时。 如果要控制单个/多个项目的选择,则必须为跟踪器添加SelectionTracker.SelectionPredicate实例(通过.withSelectionPredicate(构建器方法)。

并且,正如@ Code-Apprentice所建议的那样,您在构造getItemId()提供程序时需要从ItemDetailsLookup.ItemDetails提供正确的ID(这消除了异常)。