Skip to content

Commit

Permalink
fix bug: moveMusicItem #37
Browse files Browse the repository at this point in the history
  • Loading branch information
jrfeng committed Feb 12, 2023
1 parent 86dc604 commit 22415c0
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 28 deletions.
40 changes: 33 additions & 7 deletions app/src/main/java/snow/music/dialog/PlaylistDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -94,6 +97,7 @@ private void initRecyclerView(RecyclerView rvPlaylist) {

rvPlaylist.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
initPlaylistAdapter();
initDragHelper();
}

@SuppressLint("SetTextI18n")
Expand Down Expand Up @@ -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();
Expand All @@ -155,7 +176,7 @@ private static class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapte
private static final int TYPE_EMPTY_VIEW = 1;
private static final int TYPE_ITEM_VIEW = 2;
private static final int TYPE_EMPTY_LOADING = 3;
private Playlist mPlaylist;
private MovablePlaylist mPlaylist;

private final ItemClickHelper mItemClickHelper;
private final SelectableHelper mSelectableHelper;
Expand All @@ -165,7 +186,7 @@ private static class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapte

public PlaylistAdapter(@NonNull Playlist playlist, int playPosition) {
Preconditions.checkNotNull(playlist);
mPlaylist = playlist;
mPlaylist = new MovablePlaylist(playlist, playPosition);

mItemClickHelper = new ItemClickHelper();
mSelectableHelper = new SelectableHelper(this);
Expand All @@ -183,14 +204,14 @@ public void setPlaylist(@NonNull Playlist playlist, int playPosition) {
Preconditions.checkNotNull(playlist);

if (playlist.isEmpty()) {
mPlaylist = playlist;
mPlaylist = new MovablePlaylist(playlist, playPosition);
notifyDataSetChanged();
mSelectableHelper.clearSelected();
return;
}

if (mPlaylist.isEmpty()) {
mPlaylist = playlist;
mPlaylist = new MovablePlaylist(playlist, playPosition);
notifyDataSetChanged();
mSelectableHelper.setSelect(playPosition, true);
return;
Expand All @@ -199,7 +220,7 @@ public void setPlaylist(@NonNull Playlist playlist, int playPosition) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MusicItemDiffCallback(mPlaylist, playlist));
diffResult.dispatchUpdatesTo(this);

mPlaylist = playlist;
mPlaylist = new MovablePlaylist(playlist, playPosition);
mSelectableHelper.setSelect(playPosition, true);
}

Expand Down Expand Up @@ -288,6 +309,11 @@ public int getItemViewType(int position) {
return TYPE_ITEM_VIEW;
}

public void move(int from, int target) {
mPlaylist.move(from, target);
notifyItemMoved(from, target);
}

private int getEmptyType() {
if (mLoading) {
return TYPE_EMPTY_LOADING;
Expand Down Expand Up @@ -371,10 +397,10 @@ public void onPositionChanged(int oldPosition, int newPosition) {
}

private static class MusicItemDiffCallback extends DiffUtil.Callback {
private final Playlist mOldPlaylist;
private final MovablePlaylist mOldPlaylist;
private final Playlist mNewPlaylist;

MusicItemDiffCallback(Playlist oldPlaylist, Playlist newPlaylist) {
MusicItemDiffCallback(MovablePlaylist oldPlaylist, Playlist newPlaylist) {
mOldPlaylist = oldPlaylist;
mNewPlaylist = newPlaylist;
}
Expand Down
74 changes: 74 additions & 0 deletions app/src/main/java/snow/music/util/ItemDragCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package snow.music.util;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import java.util.Objects;

/**
* 列表项拖拽回调。
*/
public class ItemDragCallback extends ItemTouchHelper.Callback {
private boolean mDragging = false;
private int mFromPosition = -1;

private final OnDragCallback mCallback;

public ItemDragCallback(@NonNull OnDragCallback callback) {
Objects.requireNonNull(callback);
mCallback = callback;
}

@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
}

@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
if (!mDragging) {
mDragging = true;
mFromPosition = viewHolder.getBindingAdapterPosition();
}

mCallback.onDragging(viewHolder.getBindingAdapterPosition(), target.getBindingAdapterPosition());

return true;
}

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

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

if (mFromPosition == -1) {
return;
}

int fromPosition = mFromPosition;
int toPosition = viewHolder.getBindingAdapterPosition();

clearDraggingState();
if (toPosition == fromPosition) {
return;
}

mCallback.onDragComplete(fromPosition, toPosition);
}

private void clearDraggingState() {
mDragging = false;
mFromPosition = -1;
}

public interface OnDragCallback {
void onDragging(int from, int target);

void onDragComplete(int fromPosition, int toPosition);
}
}
24 changes: 3 additions & 21 deletions player/src/main/java/snow/player/AbstractPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1790,7 +1790,7 @@ public void run() {
});
}

private void onMusicItemMoved(int fromPosition, int toPosition) {
private void updatePlayPosition(int fromPosition, int toPosition) {
int playPosition = mPlayerState.getPlayPosition();
if (notInRegion(playPosition, fromPosition, toPosition)) {
notifyPlaylistChanged(playPosition);
Expand Down Expand Up @@ -1934,27 +1934,9 @@ public void run() {

List<MusicItem> 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() {
Expand Down
84 changes: 84 additions & 0 deletions player/src/main/java/snow/player/util/MovablePlaylist.java
Original file line number Diff line number Diff line change
@@ -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;

/**
* 该类用于帮助实现可拖拽播放列表。
* <p>
* 当拖拽列表时,可以在 {@code ItemTouchHelper.Callback} 的 {@code onMove} 方法中调用
* {@link #move(int, int)} 方法交换列表项位置。该方法会在移动列表项位置时修正当前正在播放的歌曲在播放队列中的位置,
* 移动歌曲后,可以使用 {@link #getPlayPosition()} 获取到正确的当前正在播放歌曲的位置。
*/
public class MovablePlaylist {
private final List<MusicItem> mMusicItems;
private int mPlayPosition;

public MovablePlaylist(@NonNull Playlist playlist, int playPosition) {
Objects.requireNonNull(playlist);

mMusicItems = new ArrayList<>(playlist.getAllMusicItem());
mPlayPosition = playPosition;
}

/**
* 移动歌曲。
* <p>
* 移动歌曲后,当前正在播放的歌曲在播放队列中的位置可能也会改变。
*
* @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();
}
}

0 comments on commit 22415c0

Please sign in to comment.