web-dev-qa-db-ja.com

Android recyclerview-selection Implementation?

現在、Android Support Library 28.0.0-alpha1からの新しいrecyclerview-selection AP​​Iを実装しようとしており、いくつかの問題に直面しています。私の目標は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);
}

これはFragment内のアイテムのロングクリックをリッスンするためにRecyclerViewimplements OnContextClickListenerです:

@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;
}

そして、それはshouldこのように私の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仕様 に記載されているすべてのものを実装しましたが、現在は問題に直面しています。アイテムを選択してもCABが表示されません...通常、アプリはクラッシュします。このスタックトレースを使用して、選択範囲を「バックアウト」し、ロングクリックして別の選択範囲を開始しようとすると、クラッシュが発生します。

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)

また、アイテムの1つを「ショートクリック」し、詳細ビューを起動する機能を失いました。これまではうまく機能していました。

私は何を間違えましたか?

6
drinfernoo

私は最近このライブラリを調べ始め、同じ例外にとらわれました。この問題は、SelectionTrackerがカスタム_RecyclerView.Adapter_サブクラスからIDを取得しようとしたときに発生します。この問題を解決するには、まずコンストラクターでsetHasStableIds(true)を呼び出します。次に、getItemId()をオーバーライドして、指定された位置パラメーターのIDを返します。

6
Code-Apprentice

実際、新しいライブラリは今のところ複雑に見えます。新しい最終バージョンを待ってから実装を開始します。確かに、あなたはそれを実験することができますが、私は今のところあなたのアプリでそれを使用しないことをお勧めします。

ニースの新しい機能は1つだけです:指またはマウスを動かすことによる連続的な複数選択

しかし、私はこれらの例を見つけました:

それまでは、私のようなライブラリを使用することを強くお勧めします。 FlexibleAdapter これは、3年以上の「選択経験」から来ています。複数選択は、 ActionModeHelper で簡単に使用でき、ActionModeを使用してコードを簡素化します。関連する Wikiページ を読んでください。

現在、選択はセットに保存されていますが、将来的にアダプター項目自体に委任される可能性があります。ただし、この拡張機能では「選択」を使用できます。

3
Davideas

2つのこと:

1)アイテムのクリックに到達するには、OnItemActivatedListener<K>リスナーを実装し、それへの参照をトラッカービルダーに渡す必要があります。その後、アイテムの「タッチ」を受け取ることができます。

2)コンテキストアクションバーメニューを表示するには、異なるアプローチが必要です。SelectionTracker.SelectionObserverを実装し、作成後にトラッカーに渡す必要があります:tracker.addObserver(...。その後、そのオブザーバーで選択変更イベントを受け取ることができます(onSelectionChangedコールバック経由)。たとえば、選択が開始され(!tracker.getSelection().isEmpty() => show CAB)、選択が終了すると(tracker.getSelection()。isEmpty()CABを非表示にします)。単一または複数のアイテムの選択を制御する場合は、トラッカーにSelectionTracker.SelectionPredicateインスタンスを追加する必要があります(.withSelectionPredicate(ビルダーメソッドを使用)。

また、@ Code-Apprenticeが示唆しているように、ItemDetailsLookup.ItemDetailsプロバイダーの構築中にgetItemId()から正しいIDを提供する必要があります(これにより例外が排除されます)。

0
A. Petrov

選択パッケージはまだアルファ版であり、ドキュメントは非常に貧弱であり、それを使用する方法は本当に明確ではありません。自分で試してみましたが、同様の問題があり、最後に SmartRecyclerView を使用しました

0
greywolf82