package com.weishitech.qichechangtingyinyue.fragment.home;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import com.hfd.common.base.BaseFragment;
import com.hfd.common.net.GenericsCallback;
import com.hfd.common.net.HttpBuiler;
import com.hfd.common.net.JsonGenericsSerializator;
import com.hfd.common.net.Url;
import com.hfd.common.util.ToastUtil;
import com.weishitech.qichechangtingyinyue.R;
import com.weishitech.qichechangtingyinyue.bean.MusicBean;
import com.weishitech.qichechangtingyinyue.bean.MusicCache;
import com.weishitech.qichechangtingyinyue.fragment.Adapter.PrivateAdapter;
import com.weishitech.qichechangtingyinyue.fragment.Adapter.QyAdapter;
import com.weishitech.qichechangtingyinyue.fragment.Adapter.SongTopAdapter;
import com.weishitech.qichechangtingyinyue.fragment.Adapter.XzAdapter;
import com.weishitech.qichechangtingyinyue.utils.DisplayUtils;
import com.weishitech.qichechangtingyinyue.utils.LeftGravitySnapHelper;
import com.weishitech.qichechangtingyinyue.utils.MusicPlayerManager;
import com.weishitech.qichechangtingyinyue.utils.OnMultiClickListener;
import com.weishitech.qichechangtingyinyue.utils.RotationAnimatorHelper;
import com.weishitech.qichechangtingyinyue.utils.ThemeManager;
import com.weishitech.qichechangtingyinyue.view.FixedSideSnapHelper;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import okhttp3.Call;
/**
* 高端语音导航首页
*/
public class HomeFragment extends BaseFragment {
// UI Views
private RecyclerView rv, sr_rv, qy_rv, xz_rv;
private ImageView iv_ss, iv_gd, sr_start, qy_start, xz_start, iv_bf;
private TextView tv_time, tv_song_name;
private LinearLayout line;
private RelativeLayout relative,relative1,relative2,relative3;
// Managers & Helpers
private MusicPlayerManager playerManager;
private ThemeManager themeManager;
private RotationAnimatorHelper rotationHelper;
private boolean isUserAuthorizedPlayback = false; // 用户是否已授权播放(点击/滑动)
private boolean isMainPlaybackActive = false; // 是否由主流程控制播放状态
private int lastPlayedPosition = -1;
// Data & State
private SongTopAdapter songTopAdapter;
private List<MusicBean.DataBean> musicDataList = new ArrayList<>();
private List<MusicBean.DataBean> privateSongList;//私人播放列表字段
private List<MusicBean.DataBean> qySongList;//轻音乐播放列表字段
private List<MusicBean.DataBean> xzSongList;//小众音乐播放列表字段
private GestureDetector gestureDetector;
private PrivateAdapter privateAdapter;
@Override
protected int setLayout() {
return R.layout.fragment_home;
}
@Override
protected void initView() {
iv_ss = fvbi(R.id.iv_ss);
iv_gd = fvbi(R.id.iv_gd);
iv_bf = fvbi(R.id.iv_bf);
rv = fvbi(R.id.rv);
sr_rv = fvbi(R.id.sr_rv);
qy_rv = fvbi(R.id.qy_rv);
xz_rv = fvbi(R.id.xz_rv);
sr_start = fvbi(R.id.sr_start);
qy_start = fvbi(R.id.qy_start);
xz_start = fvbi(R.id.xz_start);
tv_time = fvbi(R.id.tv_time);
tv_song_name = fvbi(R.id.tv_song_name);
line = fvbi(R.id.line);
relative = fvbi(R.id.relative);
relative1 = fvbi(R.id.relative1);
relative2 = fvbi(R.id.relative2);
relative3 = fvbi(R.id.relative3);
// 初始化组件
playerManager = MusicPlayerManager.getInstance(); //单例获取
themeManager = new ThemeManager();
rotationHelper = new RotationAnimatorHelper();
// 跑马灯
tv_song_name.setEllipsize(TextUtils.TruncateAt.MARQUEE);
tv_song_name.setSingleLine(true);
tv_song_name.setSelected(true);
tv_song_name.setFocusable(true);
tv_song_name.setFocusableInTouchMode(true);
setupGestureListener(); // 滑动手势换肤
iv_bf.setImageResource(themeManager.getPlayIconRes()); // 默认显示蓝色“播放”图标
}
@Override
protected void initClick() {
// 搜索跳转
iv_ss.setOnClickListener(v -> toClass(SearchActivity.class));
// 播放/暂停按钮
iv_bf.setOnClickListener(v -> {
if (musicDataList.isEmpty()) {
ToastUtil.showShortToast("暂无歌曲可播放");
return;
}
if (playerManager.isPlaying() && isMainPlaybackActive) {
playerManager.pause();
// 如果暂停了主播放,可以视为退出主控模式(可选)
isMainPlaybackActive = false;
} else {
isMainPlaybackActive = true; // 标记为主控模式
int pos = findBestObscuredItem(rv, DisplayUtils.dp2px(requireContext(), 50f));
if (pos == RecyclerView.NO_POSITION) pos = 0;
playMusicAtPosition(pos);
}
updatePlayButtonIcon(); // 只有主控才更新 iv_bf
});
//私人音乐播放按钮点击
sr_start.setOnClickListener(v -> playPrivateFirstSong());
//轻音乐播放按钮点击
qy_start.setOnClickListener(v -> playQingyinFirstSong());
//小众音乐播放按钮点击
xz_start.setOnClickListener(v -> playXiaoZhongFirstSong());
relative1.setOnClickListener(new OnMultiClickListener() {
@Override
public void onMultiClick(View v) {
if (privateSongList == null || privateSongList.isEmpty()) {
ToastUtil.showShortToast("暂无数据");
return;
}
Bundle bundle = new Bundle();
bundle.putString("type", "私人专属好歌");
bundle.putSerializable("song_list", new ArrayList<>(privateSongList)); // 拷贝一份
toClass(MusicListActivity.class, bundle);
}
});
relative2.setOnClickListener(new OnMultiClickListener() {
@Override
public void onMultiClick(View v) {
if (qySongList == null || qySongList.isEmpty()) {
ToastUtil.showShortToast("暂无数据");
return;
}
Bundle bundle = new Bundle();
bundle.putString("type", "轻音乐");
bundle.putSerializable("song_list", new ArrayList<>(qySongList));
toClass(MusicListActivity.class, bundle);
}
});
relative3.setOnClickListener(new OnMultiClickListener() {
@Override
public void onMultiClick(View v) {
if (xzSongList == null || xzSongList.isEmpty()) {
ToastUtil.showShortToast("暂无数据");
return;
}
Bundle bundle = new Bundle();
bundle.putString("type", "小众音乐");
bundle.putSerializable("song_list", new ArrayList<>(xzSongList));
toClass(MusicListActivity.class, bundle);
}
});
}
@Override
protected void initData() {
updateTimeGreeting();
musicAppInfo();
playerManager.setOnPlaybackStateChangedListener(new MusicPlayerManager.OnPlaybackStateChangedListener() {
@Override
public void onPlaying(int position) {
if (!playerManager.isPlayingFromPrivate()) {
updatePlayButtonIcon();
} else {
String tag = playerManager.getCurrentPlaylistTag();
if ("private".equals(tag)) {
int idx = playerManager.getCurrentExternalIndex();
privateAdapter.updatePlayingPosition(idx, true);
updateExternalPlayButtons(); // 同步顶部按钮
}
}
startRotationForCurrentItem(position);
}
@Override
public void onPaused() {
if (!playerManager.isPlayingFromPrivate()) {
updatePlayButtonIcon();
} else {
String tag = playerManager.getCurrentPlaylistTag();
if ("private".equals(tag)) {
int idx = playerManager.getCurrentExternalIndex();
privateAdapter.updatePlayingPosition(idx, false);
}
}
rotationHelper.stopRotation();
}
@Override
public void onCompletion() {
if (!playerManager.isPlayingFromPrivate()) {
playNextSong();
} else {
// 外部列表播放完成 → 尝试播放下一首
boolean hasNext = playerManager.playNextInExternalList();
if (hasNext) {
int nextIdx = playerManager.getCurrentExternalIndex();
privateAdapter.updatePlayingPosition(nextIdx, true);
} else {
// 已到最后 → 从头开始(循环播放)
playerManager.playFromExternalList(requireContext(), privateSongList, 0, "private");
privateAdapter.updatePlayingPosition(0, true);
}
}
rotationHelper.stopRotation();
}
@Override
public void onError(String errorMsg) {
ToastUtil.showShortToast(errorMsg);
if (playerManager.isPlayingFromPrivate()) {
privateAdapter.clearPlayingState();
} else {
updatePlayButtonIcon();
}
rotationHelper.stopRotation();
}
});
}
private void updateTimeGreeting() {
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
String greeting;
if (hour >= 5 && hour < 11) greeting = "HI,早上好";
else if (hour == 11 || hour == 12 || (hour >= 13 && hour < 18)) greeting = "HI,下午好";
else greeting = "HI,晚上好";
tv_time.setText(greeting);
}
private void musicAppInfo() {
HashMap<String, String> map = new HashMap<>();
showDialog();
HttpBuiler.getInfo(Url.music, map).build().execute(
new GenericsCallback<MusicBean>(new JsonGenericsSerializator()) {
@Override
public void onError(Call call, Exception e, int id) {
dissmiss();
ToastUtil.showShortToast(e.getMessage());
}
@Override
public void onResponse(MusicBean response, int id) {
dissmiss();
if ("0".equals(response.getCode()) && response.getData() != null && !response.getData().isEmpty()) {
musicDataList = response.getData();
// 缓存主歌单
MusicCache.setMainMusicList(musicDataList);
setupMusicRecyclerView();
} else {
ToastUtil.showShortToast("暂无歌曲数据");
}
}
});
}
private void setupMusicRecyclerView() {
songTopAdapter = new SongTopAdapter();
rv.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
rv.setAdapter(songTopAdapter);
// 设置播放状态回调(用于封面旋转)
songTopAdapter.setOnPlayStateChangedListener((position, coverView) -> {
rotationHelper.stopRotation();
rotationHelper.startRotation(coverView);
});
// 必须在设置 adapter 后再初始化滚动逻辑
setupSnappingAndPlayback(rv, songTopAdapter);
// 最后设置数据
songTopAdapter.setList(musicDataList);
//点击top条目跳转到歌词页面
songTopAdapter.setOnItemClickListener((position, dataBean) -> {
// 创建 Bundle 传递数据
Bundle bundle = new Bundle();
bundle.putString("title", dataBean.getTitle());
bundle.putString("singer", dataBean.getSinger());
bundle.putString("cover_url", dataBean.getCover());
bundle.putString("music_url", dataBean.getMusic());
bundle.putString("time", dataBean.getSongTime());
bundle.putInt("position", position);
bundle.putInt("total_count", musicDataList.size());
// 跳转到 LyricsActivity
toClass(LyricsActivity.class, bundle);
});
//私人音乐适配器
// 从第三个元素(索引=2)开始,取最多20首歌曲
int start = 2; // 第三个元素的索引
int end = Math.min(start + 20, musicDataList.size()); // 最多取20首,且不能越界
if (start >= musicDataList.size()) {
privateSongList = new ArrayList<>(); // 防止越界
ToastUtil.showShortToast("起始位置超出列表范围");
} else {
privateSongList = new ArrayList<>(musicDataList.subList(start, end));
}
privateAdapter = new PrivateAdapter();
// 设置点击事件:点击 item 播放该歌曲
privateAdapter.setOnItemClickListener((position, dataBean) -> {
playerManager.playFromExternalList(requireContext(), privateSongList, position, "private");
playerManager.setPlayingFromPrivate(true);
ToastUtil.showShortToast("播放: " + dataBean.getTitle());
});
// 设置横向 Grid 布局:每行3个
GridLayoutManager gridLayoutManager = new GridLayoutManager(requireContext(), 3, RecyclerView.HORIZONTAL, false);
sr_rv.setLayoutManager(gridLayoutManager);
sr_rv.setAdapter(privateAdapter);
privateAdapter.setList(privateSongList);
// <<< 添加吸附效果 >>>
new LeftGravitySnapHelper().attachToRecyclerView(sr_rv);
//轻音乐
// 确保 musicDataList 不为 null 且有数据
if (musicDataList == null || musicDataList.isEmpty()) {
return;
}
int qystart = 5;
int count = 6;
int qyend = Math.min(qystart + count, musicDataList.size());
qySongList = new ArrayList<>(musicDataList.subList(qystart, qyend));
QyAdapter qyAdapter = new QyAdapter();
// 设置横向 Grid 布局:每行3个
GridLayoutManager gridLayoutManagers = new GridLayoutManager(requireContext(), 2);
qy_rv.setLayoutManager(gridLayoutManagers);
qy_rv.setAdapter(qyAdapter);
qyAdapter.setList(qySongList);
//小众音乐
// 确保 musicDataList 不为 null 且有数据
if (musicDataList == null || musicDataList.isEmpty()) {
return;
}
int xzstart = 8;
int xzcount = 6;
int xzend = Math.min(xzstart + xzcount, musicDataList.size());
xzSongList = new ArrayList<>(musicDataList.subList(xzstart, xzend));
XzAdapter xzAdapter = new XzAdapter();
// 设置横向 Grid 布局:每行3个
xz_rv.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
xz_rv.setAdapter(xzAdapter);
xzAdapter.setList(xzSongList);
}
private void setupSnappingAndPlayback(RecyclerView rv, SongTopAdapter adapter) {
LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager();
if (layoutManager == null) return;
int fixedTargetX = DisplayUtils.dp2px(requireContext(), 50f);
FixedSideSnapHelper snapHelper = new FixedSideSnapHelper(fixedTargetX);
snapHelper.attachToRecyclerView(rv);
AtomicInteger lastPlayedPosition = new AtomicInteger(RecyclerView.NO_POSITION);
final boolean[] isFirstLayoutDone = {false}; // <<< 添加标志位
// 延迟标记“首次布局已完成”
rv.post(() -> isFirstLayoutDone[0] = true);
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
isUserAuthorizedPlayback = true;
// 关键:只有完成首次布局后才允许自动播放
if (!isFirstLayoutDone[0]) {
return;
}
int pos = findBestObscuredItem(recyclerView, fixedTargetX);
if (pos != RecyclerView.NO_POSITION && pos != lastPlayedPosition.get()) {
playMusicAtPosition(pos);
lastPlayedPosition.set(pos);
}
dispatchVisualFeedback(recyclerView, adapter);
}
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
boolean isPlayingBefore = playerManager.isPlaying();
if (newState == RecyclerView.SCROLL_STATE_DRAGGING ||
newState == RecyclerView.SCROLL_STATE_SETTLING) {
if (isPlayingBefore) {
playerManager.pause();
rotationHelper.stopRotation();
updatePlayButtonIcon();
}
}
if (newState == RecyclerView.SCROLL_STATE_IDLE && isPlayingBefore) {
playerManager.resume();
startRotationForCurrentItem(playerManager.getCurrentPlayingPosition());
updatePlayButtonIcon();
}
}
});
setupOneWayScroll(rv, requireContext());
}
private int findBestObscuredItem(RecyclerView rv, int fuzzyRightEdge) {
LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager();
if (lm == null) return RecyclerView.NO_POSITION;
int bestPos = RecyclerView.NO_POSITION;
float minDiff = Float.MAX_VALUE;
for (int i = 0; i < lm.getChildCount(); i++) {
View child = lm.getChildAt(i);
if (child == null) continue;
int left = lm.getDecoratedLeft(child);
int right = lm.getDecoratedRight(child);
int width = right - left;
if (right <= 0 || left >= fuzzyRightEdge) continue; // 无交集
int overlapStart = Math.max(left, 0);
int overlapEnd = Math.min(right, fuzzyRightEdge);
int overlapWidth = Math.max(0, overlapEnd - overlapStart);
float coveredRatio = (float) overlapWidth / width;
float diff = Math.abs(coveredRatio - 0.5f);
if (coveredRatio >= 0.3f && coveredRatio <= 0.7f && diff < minDiff) {
minDiff = diff;
bestPos = lm.getPosition(child);
}
}
return bestPos;
}
@SuppressLint("SetTextI18n")
private void playMusicAtPosition(int position) {
if (position == RecyclerView.NO_POSITION || position >= musicDataList.size()) return;
MusicBean.DataBean bean = musicDataList.get(position);
nextSkin(); // 每次播放新歌就换主题
playerManager.play(requireContext(), position, bean.getMusic());
//使用 post() 延迟更新 adapter,避免在 layout 过程中修改
rv.post(() -> {
songTopAdapter.updatePlayingPosition(position);
// 同时更新底部显示
tv_song_name.setText(bean.getTitle() + "-" + bean.getSinger());
ToastUtil.showShortToast("播放: " + bean.getTitle());
});
}
private void playNextSong() {
int nextPos = playerManager.getCurrentPlayingPosition() + 1;
if (nextPos < musicDataList.size()) {
playMusicAtPosition(nextPos);
scrollToTriggerPosition(nextPos, rv);
} else {
ToastUtil.showShortToast("已播放完全部歌曲");
}
}
private void scrollToTriggerPosition(int targetPos, RecyclerView rv) {
LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager();
if (lm == null) return;
LinearSmoothScroller scroller = new LinearSmoothScroller(requireContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return lm.computeScrollVectorForPosition(targetPosition);
}
@Override
public int calculateDxToMakeVisible(View view, int snapPreference) {
int center = (lm.getDecoratedLeft(view) + lm.getDecoratedRight(view)) / 2;
int targetX = DisplayUtils.dp2px(requireContext(), 50f);
return targetX - center;
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 150f / displayMetrics.densityDpi;
}
};
scroller.setTargetPosition(targetPos);
lm.startSmoothScroll(scroller);
}
private void dispatchVisualFeedback(RecyclerView rv, SongTopAdapter adapter) {
LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager();
if (lm == null) return;
int targetX = DisplayUtils.dp2px(requireContext(), 100f);
for (int i = 0; i < rv.getChildCount(); i++) {
View child = rv.getChildAt(i);
if (child == null) continue;
int centerX = (lm.getDecoratedLeft(child) + lm.getDecoratedRight(child)) / 2;
float distance = Math.abs(centerX - targetX);
if (distance > 600) continue;
// float alpha = 1.0f - Math.min(1.0f, distance / 300f);
// float scale = 0.8f + 0.2f * (1.0f - Math.min(1.0f, distance / 200f));
// child.setAlpha(alpha);
// child.setScaleX(scale);
// child.setScaleY(scale);
}
}
private void setupOneWayScroll(RecyclerView rv, Context context) {
final boolean[] hasScrolledLeft = {false};
final float[] startX = {0f};
rv.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
startX[0] = e.getX();
break;
case MotionEvent.ACTION_MOVE:
float dx = e.getX() - startX[0];
if (dx < -20 && !hasScrolledLeft[0]) {
hasScrolledLeft[0] = true;
}
if (hasScrolledLeft[0] && dx > 15) {
Toast.makeText(context, "暂只支持左滑操作", Toast.LENGTH_SHORT).show();
return true;
}
break;
}
return false;
}
});
}
private void setupGestureListener() {
gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY())) {
if (Math.abs(diffX) > 100 && Math.abs(velocityX) > 100) {
nextSkin(); // 滑动换肤
return true;
}
}
return false;
}
});
View.OnTouchListener touchListener = (v, event) -> gestureDetector.onTouchEvent(event);
line.setOnTouchListener(touchListener);
relative.setOnTouchListener(touchListener);
}
private void nextSkin() {
themeManager.nextTheme();
applyCurrentTheme();
ToastUtil.showShortToast("切换到主题 " + (themeManager.getCurrentThemeIndex() + 1));
}
private void applyCurrentTheme() {
if (!isAdded()) return; // Fragment 已 detach,不执行
boolean isPlaying = playerManager.isPlaying();
themeManager.applyThemeTo(line, relative, iv_bf, isPlaying);
}
/**
* 更新主播放按钮图标(根据实际播放状态)
*/
private void updatePlayButtonIcon() {
if (!isAdded()) return;
boolean isPlaying = playerManager.isPlaying();
iv_bf.setImageResource(isPlaying
? themeManager.getPauseIconRes()
: themeManager.getPlayIconRes());
}
private void startRotationForCurrentItem(int position) {
// 先停止任何正在进行的动画
rotationHelper.stopRotation();
// 获取 ViewHolder
RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(position);
if (vh == null) {
// item 当前不可见,可以延迟执行或跳过
return;
}
ImageView coverView = vh.itemView.findViewById(R.id.iv_song_logo);
if (coverView != null) {
rotationHelper.startRotation(coverView); // 开始旋转
}
}
/**
* 更新所有外部播放按钮为“播放”状态
*/
private void updateStartButtonIcons() {
sr_start.setImageResource(R.mipmap.img_bf);
qy_start.setImageResource(R.mipmap.img_bf);
xz_start.setImageResource(R.mipmap.img_bf);
}
/**
* 播放私人专属歌单
*/
private void playPrivateFirstSong() {
if (privateSongList == null || privateSongList.isEmpty()) {
ToastUtil.showShortToast("暂无私人歌曲");
return;
}
boolean isPlaying = playerManager.isPlaying();
boolean isFromThisList = "private".equals(playerManager.getCurrentPlaylistTag());
// 如果正在播放本列表 → 暂停
if (isPlaying && isFromThisList) {
playerManager.pause();
privateAdapter.updatePlayingPosition(playerManager.getCurrentExternalIndex(), false);
sr_start.setImageResource(R.mipmap.img_bf);
ToastUtil.showShortToast("已暂停");
return;
}
// 如果已暂停本列表 → 继续播放
if (!isPlaying && isFromThisList) {
int currentIndex = playerManager.getCurrentExternalIndex();
playerManager.resume();
privateAdapter.updatePlayingPosition(currentIndex, true);
sr_start.setImageResource(R.mipmap.img_zt);
ToastUtil.showShortToast("继续播放: " + privateSongList.get(currentIndex).getTitle());
return;
}
// 新播放:从 index=0 开始
playerManager.playFromExternalList(requireContext(), privateSongList, 0, "private");
playerManager.setPlayingFromPrivate(true);
// 更新 UI:第0首为播放中
privateAdapter.updatePlayingPosition(0, true);
sr_start.setImageResource(R.mipmap.img_zt);
ToastUtil.showShortToast("播放私人歌曲: " + privateSongList.get(0).getTitle());
}
private void playQingyinFirstSong() {
if (qySongList == null || qySongList.isEmpty()) {
ToastUtil.showShortToast("暂无轻音乐");
return;
}
boolean isPlaying = playerManager.isPlaying();
boolean isFromThisList = "qingyin".equals(playerManager.getCurrentPlaylistTag());
if (isPlaying && isFromThisList) {
playerManager.pause();
qy_start.setImageResource(R.mipmap.img_bf);
return;
}
if (!isPlaying && isFromThisList) {
playerManager.resume();
qy_start.setImageResource(R.mipmap.img_zt);
return;
}
// 使用正确 tag
playerManager.playFromExternalList(requireContext(), qySongList, 0, "qingyin");
playerManager.setPlayingFromPrivate(true); // 表示来自外部
qy_start.setImageResource(R.mipmap.img_zt);
rotationHelper.stopRotation();
MusicBean.DataBean firstSong = qySongList.get(0);
ToastUtil.showShortToast("播放轻音乐: " + firstSong.getTitle());
}
private void playXiaoZhongFirstSong() {
if (xzSongList == null || xzSongList.isEmpty()) {
ToastUtil.showShortToast("暂无私人歌曲");
return;
}
boolean isPlaying = playerManager.isPlaying();
boolean isFromThisList = "private".equals(playerManager.getCurrentPlaylistTag());
// 如果正在播放该列表中的任意一首 → 暂停
if (isPlaying && isFromThisList) {
playerManager.pause();
xz_start.setImageResource(R.mipmap.img_bf);
ToastUtil.showShortToast("已暂停");
return;
}
// 如果已暂停,且属于该列表 → 恢复(不重新 prepare)
if (!isPlaying && isFromThisList) {
playerManager.resume();
xz_start.setImageResource(R.mipmap.img_zt);
int currentIndex = playerManager.getCurrentExternalIndex();
MusicBean.DataBean currentSong = xzSongList.get(currentIndex);
ToastUtil.showShortToast("继续播放: " + currentSong.getTitle());
return;
}
// 否则:首次播放 or 切换到新列表 → 从 index=0 开始
playerManager.playFromExternalList(requireContext(), xzSongList, 0, "xiaozhong");
playerManager.setPlayingFromPrivate(true);
xz_start.setImageResource(R.mipmap.img_zt);
rotationHelper.stopRotation();
MusicBean.DataBean firstSong = xzSongList.get(0);
ToastUtil.showShortToast("播放小众歌曲: " + firstSong.getTitle());
}
/**
* 根据当前播放状态和来源,同步更新所有外部播放按钮的图标
*/
private void updateExternalPlayButtons() {
boolean isPlaying = playerManager.isPlaying();
String currentTag = playerManager.getCurrentPlaylistTag();
// 私人专属
if (isPlaying && "private".equals(currentTag)) {
sr_start.setImageResource(R.mipmap.img_zt);
} else {
sr_start.setImageResource(R.mipmap.img_bf);
}
// 轻音乐
if (isPlaying && "qingyin".equals(currentTag)) {
qy_start.setImageResource(R.mipmap.img_zt);
} else {
qy_start.setImageResource(R.mipmap.img_bf);
}
// 小众音乐
if (isPlaying && "xiaozhong".equals(currentTag)) {
xz_start.setImageResource(R.mipmap.img_zt);
} else {
xz_start.setImageResource(R.mipmap.img_bf);
}
}
@Override
public void onDestroy() {
// playerManager.release();
rotationHelper.stopRotation();
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
// 1. 更新主按钮图标
updatePlayButtonIcon();
// 2. 应用主题
applyCurrentTheme();
// 3. 如果正在播放,尝试同步到当前播放项
if (playerManager.isPlaying()) {
int currentPos = playerManager.getCurrentPlayingPosition();
// 只有来自主列表的才做滚动和旋转
if (!playerManager.isPlayingFromPrivate()) { // 假设 isPlayingFromPrivate 表示非主列表
syncToCurrentPlayingItem(currentPos);
}
}
}
/**
* 滚动并高亮当前正在播放的歌曲
*/
private void syncToCurrentPlayingItem(int position) {
if (songTopAdapter == null || rv == null || musicDataList == null || position >= musicDataList.size()) {
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager();
if (layoutManager == null) return;
// 目标 x 坐标:距离左边 50dp
int targetX = DisplayUtils.dp2px(requireContext(), 50f);
LinearSmoothScroller scroller = new LinearSmoothScroller(requireContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return layoutManager.computeScrollVectorForPosition(targetPosition);
}
@Override
public int calculateDxToMakeVisible(View view, int snapPreference) {
// 正确调用:通过 layoutManager 调用
int left = layoutManager.getDecoratedLeft(view);
int right = layoutManager.getDecoratedRight(view);
int center = (left + right) / 2;
// 返回需要移动的距离
return targetX - center;
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
// 控制滚动速度:越小越快
return 100f / displayMetrics.densityDpi; // 可调
}
};
scroller.setTargetPosition(position);
layoutManager.startSmoothScroll(scroller);
// 延迟查找 ViewHolder 并启动旋转
rv.postDelayed(() -> {
RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(position);
if (vh != null) {
ImageView coverView = vh.itemView.findViewById(R.id.iv_song_logo);
if (coverView != null) {
rotationHelper.stopRotation();
rotationHelper.startRotation(coverView);
}
}
}, 350); // 等待滚动接近完成
}
}
package com.weishitech.qichechangtingyinyue.fragment.Adapter;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.weishitech.qichechangtingyinyue.R;
import com.weishitech.qichechangtingyinyue.bean.MusicBean;
import com.weishitech.qichechangtingyinyue.utils.DisplayUtils;
public class PrivateAdapter extends BaseQuickAdapter<MusicBean.DataBean, BaseViewHolder> {
private int currentPlayingPosition = -1;
private boolean isPlaying = false;
public PrivateAdapter() {
super(R.layout.private_adapter);
}
@Override
protected void convert(@NonNull BaseViewHolder holder, MusicBean.DataBean dataBean) {
holder.setText(R.id.tv_song_name, dataBean.getTitle());
holder.setText(R.id.tv_sing_name, dataBean.getSinger());
holder.setText(R.id.tv_tj, "之手 推荐-" + dataBean.getTitle());
ImageView coverView = holder.getView(R.id.iv_sing_logo);
ImageView playBtn = holder.getView(R.id.iv_song_start);
// 加载封面
Glide.with(getContext())
.load(dataBean.getCover())
.error(R.mipmap.img_home_sing)
.transform(new RoundedCorners(DisplayUtils.dp2px(getContext(), 8f)))
.into(coverView);
// 获取当前位置
int position = getItemPosition(dataBean);
// 设置按钮状态
if (position == currentPlayingPosition && isPlaying) {
playBtn.setImageResource(R.mipmap.img_zt); // 暂停图标
} else {
playBtn.setImageResource(R.mipmap.img_bf); // 播放图标
}
// 点击事件:可选(点击 item 也播放)
playBtn.setOnClickListener(v -> {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(position, dataBean);
}
});
}
// 外部调用:更新当前播放哪一首
public void updatePlayingPosition(int position, boolean isPlaying) {
int oldPos = this.currentPlayingPosition;
this.currentPlayingPosition = position;
this.isPlaying = isPlaying;
notifyItemChanged(oldPos);
notifyItemChanged(position);
}
// 播放停止或退出时清空状态
public void clearPlayingState() {
int oldPos = this.currentPlayingPosition;
this.currentPlayingPosition = -1;
this.isPlaying = false;
notifyItemChanged(oldPos);
}
// 自定义接口
public interface OnItemClickListener {
void onItemClick(int position, MusicBean.DataBean dataBean);
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
}
package com.weishitech.qichechangtingyinyue.utils;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import androidx.annotation.NonNull;
import com.weishitech.qichechangtingyinyue.bean.MusicBean;
import java.util.List;
public class MusicPlayerManager {
private static volatile MusicPlayerManager instance; // 双检锁
private MediaPlayer mediaPlayer;
private OnPlaybackStateChangedListener listener;
private int currentPlayingPosition = -1; // 主列表位置
private boolean isPlayingFromPrivate = false;
// 外部播放信息
private List<MusicBean.DataBean> currentExternalList;
private String currentPlaylistTag; // "private", "qingyin", "xiaozhong"
private int currentExternalIndex = -1;
private String currentPlayingUrl = null;
// 私有构造
private MusicPlayerManager() {
// 不再传 context,MediaPlayer 不依赖 Activity Context
}
// 获取单例
public static MusicPlayerManager getInstance() {
if (instance == null) {
synchronized (MusicPlayerManager.class) {
if (instance == null) {
instance = new MusicPlayerManager();
}
}
}
return instance;
}
// 提供对外接口...
public boolean isPlayingFromPrivate() { return isPlayingFromPrivate; }
public void setPlayingFromPrivate(boolean flag) { this.isPlayingFromPrivate = flag; }
public String getCurrentPlayingMusicUrl() { return currentPlayingUrl; }
public int getCurrentExternalIndex() { return currentExternalIndex; }
public String getCurrentPlaylistTag() { return currentPlaylistTag; }
public interface OnPlaybackStateChangedListener {
void onPlaying(int position);
void onPaused();
void onCompletion();
void onError(String errorMsg);
}
public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
this.listener = listener;
}
// 播放主列表歌曲
public void play(Context context, int position, String musicUrl) {
if (position == currentPlayingPosition && mediaPlayer != null && mediaPlayer.isPlaying()) {
return;
}
try {
prepareAndPlay(musicUrl, () -> {
currentPlayingPosition = position;
currentPlayingUrl = musicUrl;
isPlayingFromPrivate = false;
currentPlaylistTag = null;
if (listener != null) listener.onPlaying(position);
});
} catch (Exception e) {
e.printStackTrace();
if (listener != null) listener.onError("无法播放音乐");
}
}
/**
* 播放外部列表(仅用于首次启动)
*/
public void playFromExternalList(Context context, @NonNull List<MusicBean.DataBean> list, int startIndex, String tag) {
try {
boolean isSameList = tag.equals(currentPlaylistTag);
int playIndex = isSameList ? currentExternalIndex : startIndex;
if (playIndex < 0 || playIndex >= list.size()) return;
MusicBean.DataBean bean = list.get(playIndex);
prepareAndPlay(bean.getMusic(), () -> {
currentPlayingPosition = -1;
currentPlayingUrl = bean.getMusic();
isPlayingFromPrivate = true;
currentExternalList = list;
currentExternalIndex = playIndex;
currentPlaylistTag = tag;
if (listener != null) listener.onPlaying(-1);
});
} catch (Exception e) {
e.printStackTrace();
if (listener != null) listener.onError("无法播放音乐");
}
}
/**
* 公共方法:准备并播放(复用 MediaPlayer)
*/
private void prepareAndPlay(String musicUrl, Runnable onPreparedAction) throws Exception {
boolean needNewPlayer = mediaPlayer == null;
if (needNewPlayer) {
mediaPlayer = new MediaPlayer();
setupListeners();
} else if (musicUrl.equals(currentPlayingUrl) && !mediaPlayer.isPlaying()) {
// 已加载过此 URL,直接 start
mediaPlayer.start();
onPreparedAction.run();
return;
} else {
// 不同 URL,重置并重新加载
mediaPlayer.reset();
}
mediaPlayer.setDataSource(musicUrl);
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(mp -> {
mp.start();
currentPlayingUrl = musicUrl;
onPreparedAction.run();
});
}
private void setupListeners() {
mediaPlayer.setOnCompletionListener(mp -> {
if (listener != null) listener.onCompletion();
});
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
if (listener != null) listener.onError("播放错误: " + what);
return true;
});
}
public void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
if (listener != null) listener.onPaused();
}
}
public void resume() {
if (mediaPlayer != null && !mediaPlayer.isPlaying() && currentPlayingUrl != null) {
mediaPlayer.start();
if (listener != null) listener.onPlaying(isPlayingFromPrivate ? -1 : currentPlayingPosition);
}
}
public boolean isPlaying() {
return mediaPlayer != null && mediaPlayer.isPlaying();
}
public int getCurrentPlayingPosition() {
return currentPlayingPosition;
}
public boolean playNextInExternalList() {
if (currentExternalList == null || currentExternalIndex < 0) return false;
int nextIndex = currentExternalIndex + 1;
if (nextIndex >= currentExternalList.size()) return false;
MusicBean.DataBean bean = currentExternalList.get(nextIndex);
try {
prepareAndPlay(bean.getMusic(), () -> {
currentPlayingPosition = -1;
currentPlayingUrl = bean.getMusic();
currentExternalIndex = nextIndex;
isPlayingFromPrivate = true;
if (listener != null) listener.onPlaying(-1);
});
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void release() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
currentPlayingPosition = -1;
currentPlayingUrl = null;
currentExternalIndex = -1;
currentPlaylistTag = null;
currentExternalList = null;
}
// 新增:获取当前播放时间
public int getCurrentPosition() {
if (mediaPlayer != null) {
return mediaPlayer.getCurrentPosition();
}
return 0;
}
// 新增:获取总时长
public int getDuration() {
if (mediaPlayer != null) {
return mediaPlayer.getDuration();
}
return 0;
}
// 新增:跳转到指定位置
public void seekTo(int milliseconds) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(milliseconds);
}
}
}
这是我的完整代码逻辑怎么修改
最新发布