48

私が達成したいこと:ドラッグアンドドロップをサポートし、ドラッグ中にアイテムを再配置するGridLayoutManagerを備えたRecyclerViewを用意します。

補足: ドラッグ アンド ドロップで何かを開発するのは初めてです。

たとえば、ListView を使用してこの機能を実現する方法については、多くのトピックがあります

ただし、例は通常、ドラッグされたビューのビットマップを作成する多くのコードであり、アニメーションを再配置するため、 RecyclerViewとView.startDrag(...)RecyclerView を使用して同じ結果を達成できるはずです。notifyItemAdded()notifyItemMoved()notifyItemRemoved()

だから私はいくつか遊んで、これを思いついた:

final CardAdapter adapter = new CardAdapter(list);
adapter.setHasStableIds(true);
adapter.setListener(new CardAdapter.OnLongClickListener() {
    @Override
    public void onLongClick(View view) {
        ClipData data = ClipData.newPlainText("","");
        View.DragShadowBuilder builder = new View.DragShadowBuilder(view);
        final int pos = mRecyclerView.getChildAdapterPosition(view);
        final Goal item = list.remove(pos);

        mRecyclerView.setOnDragListener(new View.OnDragListener() {
            int prevPos = pos;

            @Override
            public boolean onDrag(View view, DragEvent dragEvent) {
                final int action = dragEvent.getAction();
                switch(action) {
                    case DragEvent.ACTION_DRAG_LOCATION:
                        View onTopOf = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY());
                        int i = mRecyclerView.getChildAdapterPosition(onTopOf);

                        list.add(i, list.remove(prevPos));
                        adapter.notifyItemMoved(prevPos, i);
                        prevPos = i;
                        break;

                    case DragEvent.ACTION_DROP:
                        View underView = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY());
                        int underPos = mRecyclerView.getChildAdapterPosition(underView);

                        list.add(underPos, item);
                        adapter.notifyItemInserted(underPos);
                        adapter.notifyDataSetChanged();
                        break;
                }

                return true;
            }
        });

        view.startDrag(data, builder, view, 0);
    }
});
mRecyclerView.setAdapter(adapter);

このコードのような作業は、スワッピングを取得しますが、非常に不安定/不安定であり、グリッド全体を更新すると、元の順序またはランダムなものに再配置されることがあります。とにかく、上記のコードは私の最初の簡単な試みにすぎません。私が本当に知りたいのは、ReyclerView でドラッグ アンド ドロップを行う標準/ベスト プラクティスの方法があるかどうか、またはそれを解決する正しい方法がまだ同じかどうかです。 ListViews に何年も使用されていますか?

4

4 に答える 4

119

実際には、これを達成するためのより良い方法があります。RecyclerViewの「コンパニオン」クラスのいくつかを使用できます。

ItemTouchHelper、つまり

RecyclerView へのスワイプとドラッグ アンド ドロップのサポートを追加するユーティリティ クラス。

とそのItemTouchHelper.Callback、つまり

ItemTouchHelper とアプリケーションの間の契約

// Create an `ItemTouchHelper` and attach it to the `RecyclerView`
ItemTouchHelper ith = new ItemTouchHelper(_ithCallback);
ith.attachToRecyclerView(rv);

// Extend the Callback class
ItemTouchHelper.Callback _ithCallback = new ItemTouchHelper.Callback() {
    //and in your imlpementaion of
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        // get the viewHolder's and target's positions in your adapter data, swap them
        Collections.swap(/*RecyclerView.Adapter's data collection*/, viewHolder.getAdapterPosition(), target.getAdapterPosition());
        // and notify the adapter that its dataset has changed
        _adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //TODO    
    }

    //defines the enabled move directions in each state (idle, swiping, dragging). 
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
                ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.START | ItemTouchHelper.END);
    }
};

詳細については、ドキュメントを確認してください。

于 2015-06-04T12:44:47.990 に答える
19

これは、データベースの並べ替えを使用した私のソリューションです。

    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            final int fromPosition = viewHolder.getAdapterPosition();
            final int toPosition = target.getAdapterPosition();
            if (fromPosition < toPosition) {
                for (int i = fromPosition; i < toPosition; i++) {
                    Collections.swap(mAdapter.getCapitolos(), i, i + 1);
                }
            } else {
                for (int i = fromPosition; i > toPosition; i--) {
                    Collections.swap(mAdapter.getCapitolos(), i, i - 1);
                }
            }
            mAdapter.notifyItemMoved(fromPosition, toPosition);
            return true;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
            MyViewHolder svH = (MyViewHolder ) viewHolder;
            int index = mAdapter.getCapitolos().indexOf(svH.currentItem);
            mAdapter.getCapitolos().remove(svH.currentItem);
            mAdapter.notifyItemRemoved(index);
            if (emptyView != null) {
                if (mAdapter.getCapitolos().size() > 0) {
                emptyView.setVisibility(TextView.GONE);
                } else {
                emptyView.setVisibility(TextView.VISIBLE);
                }
            }
        }

        @Override
        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            super.clearView(recyclerView, viewHolder);
            reorderData();
        }
    };

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
    itemTouchHelper.attachToRecyclerView(recList);

AsyncTask を利用するサポート関数があります。

private void reorderData() {
    AsyncTask<String, Void, Spanned> task = new AsyncTask<String, Void, Spanned>() {
        @Override
        protected Spanned doInBackground(String... strings) {
            dbService.deleteAllData();
            for (int i = mAdapter.getCapitolos().size() - 1; i >= 0; i--) {
                Segnalibro s = mAdapter.getCapitolos().get(i);
                dbService.saveData(s.getIdCapitolo(), s.getVersetto());
            }
            return null;
        }

        @Override
        protected void onPostExecute(Spanned spanned) {
        }
    };
    task.execute();
}
于 2016-05-25T07:14:51.437 に答える
-4

Advancedrecyclerviewは非常に便利であることがわかりました。見てください!!!

于 2016-11-01T10:51:11.877 に答える