RecyclerView的NO_POSITION 不是异常值!

RecyclerView的getAdapterPosition()返回-1解析
本文探讨了在使用RecyclerView时遇到的getAdapterPosition()返回-1导致的程序崩溃问题。官方解释称在布局未完成时,该方法会返回NO_POSITION(-1)。为避免崩溃,建议在调用后立即检查返回值。
部署运行你感兴趣的模型镜像
  1. RecyclerView 是当前代码中使用最多的控件之一。

        代码编写过程中针对列表的点击事件 很多同学都会在自己定义的RecyclerView.Adapter 里面 onBindViewHolder()中添加监听事件。

        这里就会发生一个很神奇的事情, 当你通过getAdapterPosition() 去拿当前选中位置的时候有时候会返回-1;导致程序崩溃

holder.view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            		/*在这里拿到选中的条目数据*/
                list.get(holder.getAdapterPosition());
            }
        });

测试会偶先这样的崩溃bug。

09-11 17:19:06.690 30454 30454 E AndroidRuntime: java.lang.IndexOutOfBoundsException: Invalid index -1, size is 8
09-11 17:19:06.690 30454 30454 E AndroidRuntime: 	at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)

2. Cause:

进入RecyclerView.java 的 getAdapterPosition()方法:

/**
         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
         *
         * @return The adapter position of the item if it still exists in the adapter.
         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
         * layout pass or the ViewHolder has already been recycled.
         */
public final int getAdapterPosition() {
            if (mOwnerRecyclerView == null) {
                return NO_POSITION;
            }
            return mOwnerRecyclerView.getAdapterPositionFor(this);
}

官方明确表示,在layout布局没有完成的时候 会返回 NO_POSITION, 因此 返回-1 是一个很正常的情况。

最好的解决方案,是在使用 getAdapterPosition() 方法的时候 马上进行返回值的检查。

比如:

holder.view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            		/*在这里拿到选中的条目数据*/
                int pos = holder.getAdapterPosition();
                if(pos != RecyclerView.NO_POSITION)list.get(pos);
            }
        });

 

 

 

 

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

private void setupSnappingAndPlayback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int fixedTargetX = dp2px(50f); // 模糊图右边界 // 使用 SnapHelper(保留吸附动画) FixedSideSnapHelper snapHelper = new FixedSideSnapHelper(fixedTargetX); snapHelper.attachToRecyclerView(rv); // 新增:记录已触发播放的位置,防止重复 final AtomicInteger lastPlayedPosition = new AtomicInteger(RecyclerView.NO_POSITION); rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // ✅ 关键:如果正处于“completion 自动播放”流程中,跳过滚动触发 if (isPlayingNextDueToCompletion) { Log.d("ScrollSkip", "跳过滚动检测:正由 completion 处理下一首"); return; } int pos = findBestObscuredItem(recyclerView, fixedTargetX); if (pos != RecyclerView.NO_POSITION && pos != lastPlayedPosition.get()) { Log.d("ScrollPlay", "滚动触发播放: " + pos); playMusicAtPosition(pos, adapter.getData(), false); // false = 来自滚动 lastPlayedPosition.set(pos); } dispatchVisualFeedback(recyclerView, adapter); } @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) { // 用户开始滑动或惯性滑动 → 暂停音乐和动画 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); wasPlayingBeforeScroll = true; // 记录原来是播放状态 stopRotationAnimation(); iv_bf.setImageResource(R.mipmap.img_lv_bf); // 更新按钮图标 Log.d("ScrollPause", "滑动中暂停音乐"); } else { wasPlayingBeforeScroll = false; } isScrollInProgress = true; } if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滑动结束 → 如果原来是播放状态,则恢复 isScrollInProgress = false; if (wasPlayingBeforeScroll && mediaPlayer != null) { mediaPlayer.start(); iv_bf.setImageResource(R.mipmap.img_lan_zt); startRotationForCurrentItem(currentPlayingPosition); Log.d("ScrollResume", "滑动结束,恢复播放"); } } } }); setupOneWayScroll(rv, rv.getContext()); } private void dispatchVisualFeedback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int targetX = dp2px(100f); // 模糊图右侧位置 for (int i = 0; i < rv.getChildCount(); i++) { View child = rv.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int centerX = (left + right) / 2; float distance = Math.abs(centerX - targetX); // 只处理附近的 item 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); } } /** * 查找当前是否有 item 被模糊图遮挡其左半部分约 50% * 假设模糊图固定显示在 [0, fuzzyRightEdge] 区域内(例如宽度 100dp) */ private int findBestObscuredItem(RecyclerView rv, int fuzzyRightEdge) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return RecyclerView.NO_POSITION; int childCount = layoutManager.getChildCount(); int bestPos = RecyclerView.NO_POSITION; float minDiffToHalf = Float.MAX_VALUE; for (int i = 0; i < childCount; i++) { View child = layoutManager.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int width = right - left; // 必须有交集:item 必须与模糊图区域 [0, fuzzyRightEdge] 相交 if (right <= 0 || left >= fuzzyRightEdge) continue; // 无交集 // 计算重叠区域:模糊图 [0, fuzzyRightEdge] 与 item [left, right] 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 < minDiffToHalf) { minDiffToHalf = diff; bestPos = layoutManager.getPosition(child); } } return bestPos; } 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 currentX = e.getX(); float dx = currentX - startX[0]; // 手指移动距离 if (dx < -20 && !hasScrolledLeft[0]) { // 用户向左滑动(内容向右移出视野),视为“已前进” hasScrolledLeft[0] = true; } // 如果已经左滑过,则禁止任何右滑(手指向右拖) if (hasScrolledLeft[0] && dx > 15) { // 检测到右滑意图 → 拦截事件,不交给 RecyclerView Toast.makeText(context, "暂只支持左滑操作", Toast.LENGTH_SHORT).show(); return true; // 拦截触摸事件,防止 RecyclerView 滚动 } break; } return false; // 正常处理其他情况 } }); } @SuppressLint("SetTextI18n") private void playMusicAtPosition(int position, List<MusicBean.DataBean> dataList, boolean fromCompletion) { synchronized (playbackLock) { if (isPlayingRequestInProgress) { Log.d("Playback", "播放请求被忽略(并发): pos=" + position + ", 来源=" + (fromCompletion ? "completion" : "scroll")); return; } isPlayingRequestInProgress = true; } // ✅ 如果是从 completion 进来的,在进入播放前保留标志 if (!fromCompletion) { // 手动滚动触发 → 清除自动播放标志(表示中断了自动流程) isPlayingNextDueToCompletion = false; } // 注意:不要在这里清除 isPlayingNextDueToCompletion=true 的情况 try { MusicBean.DataBean bean = dataList.get(position); currentPlayingPosition = position; if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } else { mediaPlayer.setOnPreparedListener(null); mediaPlayer.setOnCompletionListener(null); mediaPlayer.setOnErrorListener(null); if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.reset(); } mediaPlayer.setDataSource(bean.getMusic()); mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build()); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp -> { mp.start(); isPlayingState = true; iv_bf.setImageResource(R.mipmap.img_lan_zt); // ✅ 新歌已成功开始播放 → 自动播放流程结束 if (fromCompletion) { isPlayingNextDueToCompletion = false; } // 释放锁 synchronized (playbackLock) { isPlayingRequestInProgress = false; } }); final int finalPosition = position; mediaPlayer.setOnCompletionListener(mp -> { Log.d("AutoPlay", "歌曲完成: " + bean.getTitle() + ", pos=" + finalPosition); isPlayingNextDueToCompletion = true; // 再次标记 playNextSong(dataList, finalPosition); }); songTopAdapter.updatePlayingPosition(position); tv_song_name.setText(bean.getTitle() + "-" + bean.getSinger()); ToastUtil.showShortToast("播放: " + bean.getTitle()); } catch (Exception e) { e.printStackTrace(); ToastUtil.showShortToast("无法播放"); playNextSong(dataList, position); // 错误也尝试下一首 } finally { // ✅ 只有在非 completion 调用时才释放锁(避免 completion 流程中提前释放) if (!fromCompletion) { synchronized (playbackLock) { isPlayingRequestInProgress = false; } } // 如果是 completion 触发,锁会在新播放开始或失败后释放 } } private void playNextSong(List<MusicBean.DataBean> dataList, int completedPosition) { int nextPos = completedPosition + 1; if (nextPos >= 0 && nextPos < dataList.size()) { playMusicAtPosition(nextPos, dataList, true); // 第三个参数表示“因 completion 触发” scrollToTriggerPosition(nextPos, rv); } else { ToastUtil.showShortToast("已播放完全部歌曲"); } } private void scrollToTriggerPosition(int targetPos, RecyclerView recyclerView) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); if (layoutManager == null) return; LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { private static final float TARGET_OFFSET = 80f; // 调整此值 @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; // 假设模糊图右边界为 50dp,我们希望 item 左半部分进入该区域 int targetX = (int) (dp2px(50f) - dp2px(80f)); // 调整偏移量使位置合适(可微调) return targetX - center; } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 150f / displayMetrics.densityDpi; } }; scroller.setTargetPosition(targetPos); layoutManager.startSmoothScroll(scroller); } private int dp2px(float dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, rv.getResources().getDisplayMetrics() ); } /** * 启动封面旋转动画 */ private void startRotationAnimation(ImageView imageView) { if (imageView == null || imageView.getDrawable() == null) return; // 先清除旧动画 if (rotationAnimator != null && rotationAnimator.isRunning()) { rotationAnimator.cancel(); } // 方法一:使用 ObjectAnimator(保留,但需设置 LayerType) rotationAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f); rotationAnimator.setDuration(10000); // 10秒一圈 rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE); rotationAnimator.setInterpolator(new LinearInterpolator()); // 关键一步:启用硬件层加速(否则某些设备上动画卡顿或不显示) imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null); rotationAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { imageView.setLayerType(View.LAYER_TYPE_NONE, null); } }); rotationAnimator.start(); } /** * 停止旋转动画 */ private void stopRotationAnimation() { if (rotationAnimator != null) { rotationAnimator.cancel(); rotationAnimator = null; } if (currentRotationView != null) { currentRotationView.setLayerType(View.LAYER_TYPE_NONE, null); currentRotationView = null; } } /** * 根据 position 手动启动该 itemView 的旋转动画(用于恢复播放) */ private void startRotationForCurrentItem(int position) { if (position == RecyclerView.NO_POSITION || songTopAdapter == null) return; RecyclerView.ViewHolder viewHolder = rv.findViewHolderForAdapterPosition(position); if (viewHolder != null) { ImageView coverView = viewHolder.itemView.findViewById(R.id.iv_song_logo); // 替换为你的封面ID if (coverView != null) { stopRotationAnimation(); // 安全起见先停 startRotationAnimation(coverView); } } else { // 如果 item 不可见,通过 adapter 回调间接启动(convert 会触发) Log.d("Rotation", "目标 itemView 不可见,等待 convert 触发"); } }这个isPlayingRequestInProgress变量怎么添加
12-03
private void setupSnappingAndPlayback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; // 1. 添加 SnapHelper 实现居中吸附 mSnapHelper = new LinearSnapHelper() { @Override public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { OrientationHelper helper = OrientationHelper.createHorizontalHelper(layoutManager); int[] out = new int[2]; out[0] = distanceToCenter(targetView, helper); out[1] = 0; return out; } @Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { OrientationHelper helper = OrientationHelper.createHorizontalHelper(layoutManager); return findCenterView(layoutManager, helper); } private int distanceToCenter(View targetView, OrientationHelper helper) { int childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2; int containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; return childCenter - containerCenter; } private View findCenterView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { int childCount = layoutManager.getChildCount(); if (childCount == 0) return null; View closestChild = null; int minDistance = Integer.MAX_VALUE; int containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; for (int i = 0; i < childCount; i++) { View child = layoutManager.getChildAt(i); if (child == null) continue; int childCenter = helper.getDecoratedStart(child) + helper.getDecoratedMeasurement(child) / 2; int distance = Math.abs(childCenter - containerCenter); if (distance < minDistance) { minDistance = distance; closestChild = child; } } return closestChild; } }; mSnapHelper.attachToRecyclerView(rv); // 2. 禁用 Fling:将快速滑动转换为“左/右一步” rv.setOnFlingListener(new RecyclerView.OnFlingListener() { @Override public boolean onFling(int velocityX, int velocityY) { // 拦截所有 fling 事件,不交给 RecyclerView 处理 handleFlingStep(velocityX, layoutManager, rv); return true; // 我已处理,不再传递 } }); // 3. 监听滚动状态,在停止时播放音乐 rv.addOnScrollListener(new RecyclerView.OnScrollListener() { private Handler handler = new Handler(); private Runnable pendingPlayTask; @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滚动结束 → 解锁 isScrollInProgress = false; // 播放音乐 View centerView = mSnapHelper.findSnapView(layoutManager); int pos = layoutManager.getPosition(centerView); if (pos != RecyclerView.NO_POSITION && pos < adapter.getData().size()) { if (pendingPlayTask != null) handler.removeCallbacks(pendingPlayTask); pendingPlayTask = () -> playMusicAtPosition(pos, adapter.getData()); handler.post(pendingPlayTask); } } else { // 滚动中 → 清除待执行任务 if (pendingPlayTask != null) handler.removeCallbacks(pendingPlayTask); } } }); } private void handleFlingStep(int velocityX, LinearLayoutManager layoutManager, RecyclerView rv) { if (isScrollInProgress) { return; // 正在滚动,直接忽略新操作 } View currentSnapView = mSnapHelper.findSnapView(layoutManager); int currentPosition = currentSnapView != null ? layoutManager.getPosition(currentSnapView) : 0; int itemCount = layoutManager.getItemCount(); int targetPosition; if (velocityX > 0) { // 向左滑 → 下一首 targetPosition = Math.min(currentPosition + 1, itemCount - 1); } else if (velocityX < 0) { // 向右滑 → 上一首 targetPosition = Math.max(currentPosition - 1, 0); } else { return; } if (targetPosition == currentPosition) return; isScrollInProgress = true; // 锁定状态 smoothScrollToPositionWithSnap(rv, layoutManager, targetPosition); } // 辅助方法:获取绑定的 SnapHelper(需要保存引用) private LinearSnapHelper getSnapHelper() { // 因为 LinearSnapHelper 没有直接获取方式,建议成员变量保存 // 示例中假设你是通过字段保存的 // 如果没有,请改为传参或封装 if (mSnapHelper == null) { throw new IllegalStateException("mSnapHelper 未初始化,请先调用 setupSnappingAndPlayback"); } return mSnapHelper; } private void smoothScrollToPositionWithSnap(RecyclerView rv, LinearLayoutManager layoutManager, int position) { // ✅ 1. 取消任何正在进行的 SmoothScroller if (currentScroller != null) { currentScroller.stop(); } // ✅ 2. 停止 RecyclerView 当前滚动 rv.stopScroll(); // ✅ 3. 创建新的 Scroller currentScroller = new LinearSmoothScroller(rv.getContext()) { @Override public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) { return (boxStart + boxEnd) / 2 - (viewStart + viewEnd) / 2; } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 150f / displayMetrics.densityDpi; // 控制速度不要太快 } @Override protected void onStop() { super.onStop(); // ✅ 滚动真正结束时才解锁 isScrollInProgress = false; currentScroller = null; // 清理引用 } }; currentScroller.setTargetPosition(position); layoutManager.startSmoothScroll(currentScroller); } private void playMusicAtPosition(int position, List<MusicBean.DataBean> dataList) { if (position == RecyclerView.NO_POSITION || position >= dataList.size()) return; MusicBean.DataBean bean = dataList.get(position); if (currentPlayingPosition == position && mediaPlayer != null && mediaPlayer.isPlaying()) { return; // 已在播放 } currentPlayingPosition = position; try { if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } else { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } mediaPlayer.reset(); } mediaPlayer.setDataSource(bean.getMusic()); mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build()); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp -> mp.start()); Log.d("AutoPlay", "正在播放: " + bean.getTitle()); ToastUtil.showShortToast("播放: " + bean.getTitle()); } catch (Exception e) { e.printStackTrace(); ToastUtil.showShortToast("无法播放"); } } 播放方法里currentPlayingPosition
12-03
package com.weishitech.qichechangtingyinyue.fragment.home; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.CircleCrop; import com.hfd.common.base.BaseActivity; 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.utils.MusicPlayerManager; import com.weishitech.qichechangtingyinyue.utils.OnMultiClickListener; public class LyricsActivity extends BaseActivity { // UI Views TextView tv_back, tv_song_name, tv_sing_name, tv_start_time, tv_end_time; ImageView iv_sing_logo, iv_tj, iv_gd, iv_xh, iv_sys, iv_bf, iv_xys, iv_zjbf; SeekBar seekbar; // 播放管理器 private MusicPlayerManager playerManager; private boolean isBoundToCurrentSong = false; // 是否已绑定当前歌曲 // 数据接收字段 private String title, singer, coverUrl, musicUrl; private int songPosition,totalSongCount; // 进度更新 private final Handler handler = new Handler(); private final Runnable updateProgressRunnable = new Runnable() { @Override public void run() { if (playerManager != null && playerManager.isPlaying()) { int currentPosition = playerManager.getCurrentPosition(); int duration = playerManager.getDuration(); if (duration > 0) { seekbar.setMax(duration); seekbar.setProgress(currentPosition); // 更新时间文本(格式 mm:ss) tv_start_time.setText(formatTime(currentPosition)); tv_end_time.setText(formatTime(duration)); } } handler.postDelayed(this, 1000); // 每秒更新一次 } }; @Override protected int setLayout() { return R.layout.activity_lyrics; } @Override protected void setView() { tv_back = fvbi(R.id.tv_back); iv_sing_logo = fvbi(R.id.iv_sing_logo); tv_song_name = fvbi(R.id.tv_song_name); tv_sing_name = fvbi(R.id.tv_sing_name); iv_tj = fvbi(R.id.iv_tj); iv_gd = fvbi(R.id.iv_gd); seekbar = fvbi(R.id.seekbar); tv_start_time = fvbi(R.id.tv_start_time); tv_end_time = fvbi(R.id.tv_end_time); iv_xh = fvbi(R.id.iv_xh); iv_sys = fvbi(R.id.iv_sys); iv_bf = fvbi(R.id.iv_bf); iv_xys = fvbi(R.id.iv_xys); iv_zjbf = fvbi(R.id.iv_zjbf); } @Override protected void setData() { Bundle bundle = getIntent().getExtras(); if (bundle != null) { title = bundle.getString("title"); singer = bundle.getString("singer"); coverUrl = bundle.getString("cover_url"); musicUrl = bundle.getString("music_url"); songPosition = bundle.getInt("position", -1); totalSongCount = bundle.getInt("total_count", 0); tv_song_name.setText(title); tv_sing_name.setText(singer); Glide.with(this) .load(coverUrl) .transform(new CircleCrop()) .into(iv_sing_logo); } // 初始化播放器 //使用单例 playerManager = MusicPlayerManager.getInstance(); // 注册播放状态监听 setupPlaybackListener(); // 绑定当前歌曲(检查是否已在播放) bindToCurrentSong(); } @Override protected void setClick() { tv_back.setOnClickListener(v -> finish()); iv_sys.setOnClickListener(v -> { if (songPosition <= 0) { ToastUtil.showShortToast("当前已是第一首"); return; } playMusicAtPosition(songPosition - 1); }); iv_xys.setOnClickListener(v -> { if (songPosition >= totalSongCount - 1) { ToastUtil.showShortToast("当前已是最后一首"); return; } playMusicAtPosition(songPosition + 1); }); // 播放/暂停按钮 iv_bf.setOnClickListener(v -> { if (!isBoundToCurrentSong) { ToastUtil.showShortToast("暂无歌曲信息"); return; } if (playerManager.isPlaying()) { playerManager.pause(); } else { playerManager.resume(); } updatePlayButtonIcon(); }); // SeekBar 拖动控制 seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { tv_start_time.setText(formatTime(progress)); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // 用户开始拖动 } @Override public void onStopTrackingTouch(SeekBar seekBar) { // 用户停止拖动,跳转到指定位置 if (playerManager != null && isBoundToCurrentSong) { playerManager.seekTo(seekbar.getProgress()); } } }); } private void bindToCurrentSong() { if (musicUrl == null || title == null) return; boolean isPlaying = playerManager.isPlaying(); int currentPos = playerManager.getCurrentPlayingPosition(); // 判断是否就是当前正在播放的歌曲 if (isPlaying && currentPos == songPosition) { // 是同一首歌 → 直接同步状态 isBoundToCurrentSong = true; updatePlayButtonIcon(); // 显示“暂停” startUpdatingProgress(); // 开始更新进度条 } else { // 不是当前播放的 → 准备播放这首 playerManager.play(this, songPosition, musicUrl); isBoundToCurrentSong = true; updatePlayButtonIcon(); // 默认播放中 startUpdatingProgress(); } } private void setupPlaybackListener() { playerManager.setOnPlaybackStateChangedListener(new MusicPlayerManager.OnPlaybackStateChangedListener() { @Override public void onPlaying(int position) { if (position == songPosition) { runOnUiThread(() -> { updatePlayButtonIcon(); startUpdatingProgress(); }); } } @Override public void onPaused() { runOnUiThread(() -> { updatePlayButtonIcon(); stopUpdatingProgress(); }); } @Override public void onCompletion() { runOnUiThread(() -> { updatePlayButtonIcon(); stopUpdatingProgress(); seekbar.setProgress(0); tv_start_time.setText("00:00"); }); } @Override public void onError(String errorMsg) { runOnUiThread(() -> { ToastUtil.showShortToast("播放出错:" + errorMsg); updatePlayButtonIcon(); stopUpdatingProgress(); }); } }); } private void updatePlayButtonIcon() { if (playerManager.isPlaying()) { iv_bf.setImageResource(R.mipmap.img_lan_zt); // 暂停图标 } else { iv_bf.setImageResource(R.mipmap.img_lan_bf); // 播放图标 } } private void startUpdatingProgress() { stopUpdatingProgress(); // 防止重复 handler.postDelayed(updateProgressRunnable, 0); } private void stopUpdatingProgress() { handler.removeCallbacks(updateProgressRunnable); } @Override public void onResume() { super.onResume(); // 页面可见时尝试恢复状态 if (isBoundToCurrentSong) { updatePlayButtonIcon(); } } @Override protected void onPause() { super.onPause(); // 可选:不移除监听,让后台也能播放 } @Override protected void onDestroy() { stopUpdatingProgress(); // 注意:不要 release() playerManager,否则其他页面会中断 // playerManager.release(); ❌ 不要在这里释放 super.onDestroy(); } // 时间格式化工具 private String formatTime(int milliseconds) { int seconds = milliseconds / 1000; int minutes = seconds / 60; seconds = seconds % 60; return String.format("%02d:%02d", minutes, seconds); } private void playMusicAtPosition(int pos) { // 使用 MusicCache 判断边界(替代 musicDataList) if (pos < 0 || pos >= totalSongCount) { return; // 越界防护 } MusicBean.DataBean song = MusicCache.getMusicAt(pos); if (song == null) { ToastUtil.showShortToast("歌曲数据异常"); return; } // 播放指定位置的音乐 playerManager.play(this, pos, song.getMusic()); // 更新界面 updateUIForNewSong(song, pos); } /** * 播放新歌时更新界面和内部状态 */ private void updateUIForNewSong(MusicBean.DataBean song, int position) { // 更新成员变量 this.title = song.getTitle(); this.singer = song.getSinger(); this.coverUrl = song.getCover(); this.musicUrl = song.getMusic(); this.songPosition = position; // 更新 UI 显示 tv_song_name.setText(title); tv_sing_name.setText(singer); Glide.with(this) .load(coverUrl) .transform(new CircleCrop()) .into(iv_sing_logo); // 标记已绑定当前歌曲 isBoundToCurrentSong = true; // 更新播放按钮为“正在播放” updatePlayButtonIcon(); // 开始进度条更新 startUpdatingProgress(); // 可选:Toast 提示 ToastUtil.showShortToast("播放: " + title); } } 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; @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) { updatePlayButtonIcon(); startRotationForCurrentItem(position); } @Override public void onPaused() { updatePlayButtonIcon(); rotationHelper.stopRotation(); } @Override public void onCompletion() { // 来自主列表 → 播放下一首主歌 if (!playerManager.isPlayingFromPrivate()) { playNextSong(); return; } // 来自外部歌单 → 尝试播放下一首 boolean hasNext = playerManager.playNextInExternalList(); if (!hasNext) { ToastUtil.showShortToast("当前歌单已播放完毕"); updateStartButtonIcons(); // 所有按钮恢复“播放”图标 } rotationHelper.stopRotation(); } @Override public void onError(String errorMsg) { ToastUtil.showShortToast(errorMsg); 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 privateAdapter = new PrivateAdapter(); // 设置横向 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_lan_bf); qy_start.setImageResource(R.mipmap.img_lan_bf); xz_start.setImageResource(R.mipmap.img_lan_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(); sr_start.setImageResource(R.mipmap.img_lan_bf); ToastUtil.showShortToast("已暂停"); return; } // 如果已暂停,且属于该列表 → 恢复(不重新 prepare) if (!isPlaying && isFromThisList) { playerManager.resume(); sr_start.setImageResource(R.mipmap.img_lan_zt); int currentIndex = playerManager.getCurrentExternalIndex(); MusicBean.DataBean currentSong = privateSongList.get(currentIndex); ToastUtil.showShortToast("继续播放: " + currentSong.getTitle()); return; } // 否则:首次播放 or 切换到新列表 → 从 index=0 开始 playerManager.playFromExternalList(requireContext(), privateSongList, 0, "private"); playerManager.setPlayingFromPrivate(true); sr_start.setImageResource(R.mipmap.img_lan_zt); rotationHelper.stopRotation(); MusicBean.DataBean firstSong = privateSongList.get(0); ToastUtil.showShortToast("播放私人歌曲: " + firstSong.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_lan_bf); return; } if (!isPlaying && isFromThisList) { playerManager.resume(); qy_start.setImageResource(R.mipmap.img_lan_zt); return; } // 使用正确 tag playerManager.playFromExternalList(requireContext(), qySongList, 0, "qingyin"); playerManager.setPlayingFromPrivate(true); // 表示来自外部 qy_start.setImageResource(R.mipmap.img_lan_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_lan_bf); ToastUtil.showShortToast("已暂停"); return; } // 如果已暂停,且属于该列表 → 恢复(不重新 prepare) if (!isPlaying && isFromThisList) { playerManager.resume(); xz_start.setImageResource(R.mipmap.img_lan_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_lan_zt); rotationHelper.stopRotation(); MusicBean.DataBean firstSong = xzSongList.get(0); ToastUtil.showShortToast("播放小众歌曲: " + firstSong.getTitle()); } @Override public void onDestroy() { // playerManager.release(); rotationHelper.stopRotation(); super.onDestroy(); } @Override public void onResume() { super.onResume(); updatePlayButtonIcon(); // 主动刷新一次 applyCurrentTheme(); // 主题也刷新 // 恢复动画状态 if (playerManager.isPlaying()) { int pos = playerManager.getCurrentPlayingPosition(); startRotationForCurrentItem(pos); } } } 在activity页面播放歌曲点击下一首播放歌曲或者上一首歌曲播放,返回fragment页面activity播放的歌曲fragment要同步activity播放歌曲的旋转动画以及activity播放歌曲的歌曲封面要滑动到被模糊图片遮挡的一半
最新发布
12-04
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值