From 22415c0bfde9b1ccdf769283a2c580f4d574b2b1 Mon Sep 17 00:00:00 2001 From: jrfeng Date: Sun, 12 Feb 2023 11:29:08 +0800 Subject: [PATCH] fix bug: moveMusicItem #37 --- .../snow/music/dialog/PlaylistDialog.java | 40 +++++++-- .../snow/music/util/ItemDragCallback.java | 74 ++++++++++++++++ .../main/java/snow/player/AbstractPlayer.java | 24 +----- .../snow/player/util/MovablePlaylist.java | 84 +++++++++++++++++++ 4 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/snow/music/util/ItemDragCallback.java create mode 100644 player/src/main/java/snow/player/util/MovablePlaylist.java diff --git a/app/src/main/java/snow/music/dialog/PlaylistDialog.java b/app/src/main/java/snow/music/dialog/PlaylistDialog.java index f3565b99..c9fc34b1 100644 --- a/app/src/main/java/snow/music/dialog/PlaylistDialog.java +++ b/app/src/main/java/snow/music/dialog/PlaylistDialog.java @@ -14,6 +14,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -25,12 +26,14 @@ import recyclerview.helper.SelectableHelper; import snow.music.R; import snow.music.service.AppPlayerService; +import snow.music.util.ItemDragCallback; import snow.music.util.PlayerUtil; import snow.player.PlayerClient; import snow.player.audio.MusicItem; import snow.player.lifecycle.PlayerViewModel; import snow.player.lifecycle.PlaylistLiveData; import snow.player.playlist.Playlist; +import snow.player.util.MovablePlaylist; public class PlaylistDialog extends BottomDialog { private PlayerViewModel mPlayerViewModel; @@ -94,6 +97,7 @@ private void initRecyclerView(RecyclerView rvPlaylist) { rvPlaylist.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); initPlaylistAdapter(); + initDragHelper(); } @SuppressLint("SetTextI18n") @@ -132,6 +136,23 @@ private void initPlaylistAdapter() { }); } + private void initDragHelper() { + ItemDragCallback.OnDragCallback callback = new ItemDragCallback.OnDragCallback() { + @Override + public void onDragging(int from, int target) { + mPlaylistAdapter.move(from, target); + } + + @Override + public void onDragComplete(int fromPosition, int toPosition) { + mPlayerViewModel.getPlayerClient().moveMusicItem(fromPosition, toPosition); + } + }; + + ItemTouchHelper helper = new ItemTouchHelper(new ItemDragCallback(callback)); + helper.attachToRecyclerView(rvPlaylist); + } + private void removeMusicItem(int position) { Context context = getContext(); Playlist playlist = mPlaylistLiveData.getValue(); @@ -155,7 +176,7 @@ private static class PlaylistAdapter extends RecyclerView.Adapter musicItems = mPlaylist.getAllMusicItem(); - // 分两种情况: - // 1. toPosition 是列表的末尾索引 - // 2. toPosition 不是列表的末尾索引 - if (toPosition == getPlaylistSize() - 1) { - // 情况 1. toPosition 是列表的末尾索引: - // 步骤 1:先移除 fromPosition 处的元素 - // 步骤 2:然后再把这个元素添加到列表末尾 - musicItems.add(musicItems.remove(fromPosition)); - } else { - // 情况 2. 当 toPosition 不是列表的末尾索引时 - // 步骤 1:先将 fromPosition 处的元素插入到 toPosition - MusicItem musicItem = musicItems.get(fromPosition); - musicItems.add(toPosition, musicItem); - - // 步骤 2:移除 fromPosition 处的旧元素 - // 当 fromPosition 小于 toPosition 时,插入新元素会导致 fromPosition 处 - // 元素向后移,因此,需要将 fromPosition 加 1 才是正确的要移除的元素的位置 - musicItems.remove(fromPosition < toPosition ? fromPosition : fromPosition + 1); - } + musicItems.add(toPosition, musicItems.remove(fromPosition)); + updatePlayPosition(fromPosition, toPosition); - onMusicItemMoved(fromPosition, toPosition); updatePlaylist(mPlaylist, musicItems, new Runnable() { @Override public void run() { diff --git a/player/src/main/java/snow/player/util/MovablePlaylist.java b/player/src/main/java/snow/player/util/MovablePlaylist.java new file mode 100644 index 00000000..29310c2d --- /dev/null +++ b/player/src/main/java/snow/player/util/MovablePlaylist.java @@ -0,0 +1,84 @@ +package snow.player.util; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import snow.player.audio.MusicItem; +import snow.player.playlist.Playlist; + +/** + * 该类用于帮助实现可拖拽播放列表。 + *

+ * 当拖拽列表时,可以在 {@code ItemTouchHelper.Callback} 的 {@code onMove} 方法中调用 + * {@link #move(int, int)} 方法交换列表项位置。该方法会在移动列表项位置时修正当前正在播放的歌曲在播放队列中的位置, + * 移动歌曲后,可以使用 {@link #getPlayPosition()} 获取到正确的当前正在播放歌曲的位置。 + */ +public class MovablePlaylist { + private final List mMusicItems; + private int mPlayPosition; + + public MovablePlaylist(@NonNull Playlist playlist, int playPosition) { + Objects.requireNonNull(playlist); + + mMusicItems = new ArrayList<>(playlist.getAllMusicItem()); + mPlayPosition = playPosition; + } + + /** + * 移动歌曲。 + *

+ * 移动歌曲后,当前正在播放的歌曲在播放队列中的位置可能也会改变。 + * + * @param fromPosition 要移动的歌曲的位置。 + * @param toPosition 歌曲要移动到的位置。 + */ + public void move(int fromPosition, int toPosition) { + if (fromPosition == toPosition) { + return; + } + + mMusicItems.add(toPosition, mMusicItems.remove(fromPosition)); + updatePlayPosition(fromPosition, toPosition); + } + + private void updatePlayPosition(int fromPosition, int toPosition) { + int playPosition = mPlayPosition; + + if (notInRegion(playPosition, fromPosition, toPosition)) { + return; + } + + if (fromPosition < playPosition) { + playPosition -= 1; + } else if (fromPosition == playPosition) { + playPosition = toPosition; + } else { + playPosition += 1; + } + + mPlayPosition = playPosition; + } + + private boolean notInRegion(int position, int fromPosition, int toPosition) { + return position > Math.max(fromPosition, toPosition) || position < Math.min(fromPosition, toPosition); + } + + public int getPlayPosition() { + return mPlayPosition; + } + + public MusicItem get(int index) { + return mMusicItems.get(index); + } + + public int size() { + return mMusicItems.size(); + } + + public boolean isEmpty() { + return mMusicItems.isEmpty(); + } +}