(ZT)Java中equals和==的区别

本文详细解析了Java中的数据类型分类,重点探讨了基本数据类型和复合数据类型的比较方式,尤其是String对象的内部实现机制。通过具体代码示例展示了基本数据类型之间的比较方法,以及String对象在内存中的存储机制,包括字符串缓冲池的作用,以及如何通过intern()方法优化String对象的内存使用。

转自http://www.cnblogs.com/zhxhdean/archive/2011/03/25/1995431.html
java中的数据类型,可分为两类:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
2.复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
{
System.out.println("s1 == s2");}
else{
System.out.println("s1 != s2");}
}
}

编译并运行程序,输出:s1 == s2说明:s1 与 s2 引用同一个 String 对象 “Monday”!


2.再稍微改动一下程序,会有更奇怪的发现:public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
if (s1 == s2)
{System.out.println("s1 == s2");}
else
{System.out.println("s1 != s2");}
if (s1.equals(s2)) {System.out.println("s1 equals s2");}
else{
System.out.println("s1 not equals s2");}
}
}

我们将s2用new操作符创建
程序输出:
s1 != s2
s1 equals s2
说明:s1 s2分别引用了两个”Monday”String对象

  1. 字符串缓冲池
    原来,程序在运行的时候会创建一个字符串缓冲池当使用 s2 = “Monday” 这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1
    将s2引用s1所引用的对象”Monday”
    第二段程序中,使用了 new 操作符,他明白的告诉程序:”我要一个新的!不要旧的!”于是一个新的”Monday”Sting对象被创建在内存中。他们的值相同,但是位置不同,一个在池中游泳一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?

4.再次更改程序:public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
if (s1 == s2)
{System.out.println("s1 == s2");}
else
{System.out.println("s1 != s2");}
if (s1.equals(s2)) {System.out.println("s1 equals s2");}
else{
System.out.println("s1 not equals s2");}
}
}

这次加入:s2 = s2.intern();
程序输出:
s1 == s2
s1 equals s2
原 来,(java.lang.String的intern()方法”abc”.intern()方法的返回值还是字符串”abc”,表面上看起来好像这个方 法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在”abc”这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会 把”abc”添加到字符串池中,然后再返回它的引用。

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); } } } 这是我的完整代码逻辑怎么修改
最新发布
12-05
你已经提供了完整的代码逻辑,包括: - `HomeFragment` - `PrivateAdapter` - `MusicPlayerManager` 现在我们来 **逐层分析并修复核心问题**: > ✅ **点击 `iv_song_start` 播放第三首,但实际播放的是第二首** --- ## 🔍 问题定位总结 ### ❌ 根本原因: 在 `MusicPlayerManager.playFromExternalList()` 中,虽然传入了 `startIndex`,但当列表是“相同标签”时(`isSameList = true`),会使用旧的 `currentExternalIndex` 而不是新的 `startIndex`! ```java boolean isSameList = tag.equals(currentPlaylistTag); int playIndex = isSameList ? currentExternalIndex : startIndex; ``` 👉 这意味着:**即使你想从第3首开始播放,只要 tag 相同,就继续播上一次的位置!** 这就是为什么你点第3首,结果还是播第2首。 --- ## ✅ 解决方案:强制使用 `startIndex` 来重新播放指定歌曲 我们要改写 `playFromExternalList(...)` 方法,让每次点击 item 都以用户选择的 `position` 为准。 --- ### ✅ 修改 `MusicPlayerManager.java` 的 `playFromExternalList(...)` #### ✅ 原始错误代码(有问题): ```java boolean isSameList = tag.equals(currentPlaylistTag); int playIndex = isSameList ? currentExternalIndex : startIndex; if (playIndex < 0 || playIndex >= list.size()) return; ``` #### ✅ 正确修改为: ```java public void playFromExternalList(Context context, @NonNull List<MusicBean.DataBean> list, int startIndex, String tag) { try { // 强制使用 startIndex,不管是否是同一个列表 if (startIndex < 0 || startIndex >= list.size()) { throw new IndexOutOfBoundsException("Invalid start index: " + startIndex); } MusicBean.DataBean bean = list.get(startIndex); prepareAndPlay(bean.getMusic(), () -> { currentPlayingPosition = -1; // 不属于主列表 currentPlayingUrl = bean.getMusic(); isPlayingFromPrivate = true; currentExternalList = list; currentExternalIndex = startIndex; // ✅ 关键:更新索引 currentPlaylistTag = tag; // ✅ 更新标签 if (listener != null) listener.onPlaying(-1); }); } catch (Exception e) { e.printStackTrace(); if (listener != null) listener.onError("无法播放音乐: " + e.getMessage()); } } ``` > ⚠️ 注意:我们不再判断 `isSameList`,而是每次都用新位置。 --- ## ✅ 第二步:修复 `PrivateAdapter` 中错误的方法调用 你现在用了: ```java int position = getItemPosition(dataBean); // ❌ 错误!BaseQuickAdapter 没有这个方法 ``` 这是编译都过不了的错误(除非你自己定义了)。 ✅ 改成: ```java int position = holder.getAdapterPosition(); ``` --- ### ✅ 修改后的 `PrivateAdapter.java` ```java 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); // ✅ 正确获取 position int position = holder.getAdapterPosition(); // 设置按钮状态 if (position == currentPlayingPosition && isPlaying) { playBtn.setImageResource(R.mipmap.img_zt); // 暂停图标 } else { playBtn.setImageResource(R.mipmap.img_bf); // 播放图标 } 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; } } ``` --- ## ✅ 第三步:确保 `onCompletion()` 中下一首正确递增 你现在的 `playNextInExternalList()` 是正确的,但它只支持“下一首”,不循环。 ### ✅ 建议增强为支持循环播放: ```java public boolean playNextInExternalList() { if (currentExternalList == null || currentExternalIndex < 0) return false; int nextIndex = (currentExternalIndex + 1) % currentExternalList.size(); // 循环 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; } } ``` 这样最后一首播完后自动回到第一首。 --- ## ✅ 第四步:验证 Fragment 中的点击回调是否正确传递 position 你在 `setupMusicRecyclerView()` 中设置点击事件: ```java privateAdapter.setOnItemClickListener((position, dataBean) -> { playerManager.playFromExternalList(requireContext(), privateSongList, position, "private"); playerManager.setPlayingFromPrivate(true); ToastUtil.showShortToast("播放: " + dataBean.getTitle()); }); ``` ✅ 完全正确!它确实把 `position` 传进去了。 --- ## ✅ 最终流程验证(点击播放第3首) | 步骤 | 执行内容 | |------|--------| | 1 | 用户点击第3个 `iv_song_start` → 回调 `onItemClick(2, dataBean)` | | 2 | `playerManager.playFromExternalList(..., 2, "private")` | | 3 | `playIndex = 2`,加载第3首歌曲 URL | | 4 | 成功播放第3首 | | 5 | `onPlaying(-1)` 触发 → `privateAdapter.updatePlayingPosition(2, true)` | | 6 | UI 上第3首显示“暂停”,其他恢复“播放” | ✅ 完全符合预期。 --- ## ✅ 补充建议 ### ✅ 添加日志调试(临时) 在 `playFromExternalList` 开头加: ```java Log.d("MusicPlayer", "准备播放外部列表 [" + tag + "],位置: " + startIndex + ", 歌曲: " + list.get(startIndex).getTitle()); ``` 方便排查问题。 --- ### ✅ 添加防抖机制(防止快速双击) ```java private long lastClickTime = 0; playBtn.setOnClickListener(v -> { long now = System.currentTimeMillis(); if (now - lastClickTime < 500) return; // 防抖 lastClickTime = now; if (onItemClickListener != null) { onItemClickListener.onItemClick(position, dataBean); } }); ``` --- ## ✅ 总结:你需要做的修改清单 | 文件 | 修改点 | 说明 | |------|-------|------| | ✅ `MusicPlayerManager.java` | `playFromExternalList()` 方法中移除 `isSameList` 判断,强制使用 `startIndex` | 否则会沿用旧位置 | | ✅ `PrivateAdapter.java` | 将 `getItemPosition(dataBean)` 改为 `holder.getAdapterPosition()` | 否则编译报错或返回 -1 | | ✅ `MusicPlayerManager.java` | `playNextInExternalList()` 使用 `% size()` 实现循环播放 | 更好体验 | | ✅ (可选)添加防抖、日志 | 提升稳定性与调试效率 | 推荐上线前加入 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值