小众音乐

小众音乐  小众音乐,顾名思义,就是听众比较少的音乐。
  但并不是指那些少有人喜欢的音乐,相反,是指那些少有人发现的好音乐!
  一些低调的独立歌手创作出很棒的音乐,但传播的范围受限,所以只能是一小部分人可以听到,这些应该就是属于小众音乐的一种。
  小众音乐和非主流音乐不一样,小众音乐具有良好的流行性,与大众音乐的比较是在一个有限范围内的相互作用,没有绝对性。可以这样理解,小众音乐就是暂时被埋没了的大众音乐。
  小众音乐网(1234567i.org)成立于2008年9月6日,致力于发掘国内外的原创音乐,以及特立独行的音乐人。
  为方便用户记忆,启用的域名为1234567i.org,与音乐上的do re mi fa so la xi do意相近。
  
小众歌手

  【台湾】代表人物:陈绮贞、张悬、王若琳、苏打绿、黄小桢、雷光夏、陈珊妮
  【香港】代表人物:方大同、林一峰、何韵诗、王菀之、黄耀明
  【内地】代表人物:许巍、许飞、朴树、叶蓓、曹方、朱哲琴
  【国外】代表人物:Lady&Bird(Keren Ann 、Bradi Johannsson)、Chris Garneau、Tames Well

package com.weishitech.qichechangtingyinyue.fragment.home; 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.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; // 是否由主流程控制播放状态 // 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 = new MusicPlayerManager(); 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() { playNextSong(); } @Override public void onError(String errorMsg) { ToastUtil.showShortToast(errorMsg); updatePlayButtonIcon(); } }); } 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(); 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); //私人音乐适配器 // 从第三个元素(索引=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; } 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()); 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; // 如果正在播放私人歌单,则主按钮不响应 if (playerManager.isPlayingFromPrivate()) { iv_bf.setImageResource(themeManager.getPlayIconRes()); return; } // 否则:根据实际播放状态更新图标 boolean isPlaying = playerManager.isPlaying(); iv_bf.setImageResource(isPlaying ? themeManager.getPauseIconRes() : themeManager.getPlayIconRes()); } private void startRotationForCurrentItem(int position) { rotationHelper.stopRotation(); // 先停止任何正在进行的动画 RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(position); if (vh != null) { ImageView coverView = vh.itemView.findViewById(R.id.iv_song_logo); if (coverView != null) { rotationHelper.startRotation(coverView); } } } /** * 播放私人专属歌单的第一首歌曲(独立播放源) */ private void playPrivateFirstSong() { if (privateSongList == null || privateSongList.isEmpty()) { ToastUtil.showShortToast("暂无私人歌曲"); return; } MusicBean.DataBean firstSong = privateSongList.get(0); String url = firstSong.getMusic(); // 情况1:正在播放这首私人歌曲 → 暂停 if (playerManager.isPlaying() && playerManager.isPlayingFromPrivate() && url.equals(playerManager.getCurrentPlayingMusicUrl())) { playerManager.pause(); sr_start.setImageResource(R.mipmap.img_lan_bf); ToastUtil.showShortToast("已暂停"); return; } // 情况2:已暂停,但可以恢复(播放器未释放) if (playerManager.canResumePlaying(url)) { playerManager.resume(); sr_start.setImageResource(R.mipmap.img_lan_zt); ToastUtil.showShortToast("继续播放: " + firstSong.getTitle()); return; } // 情况3:首次播放 or 已释放 → 重新准备 playerManager.playFromExternalList(requireContext(), privateSongList, 0); sr_start.setImageResource(R.mipmap.img_lan_zt); rotationHelper.stopRotation(); ToastUtil.showShortToast("播放私人歌曲: " + firstSong.getTitle()); } /** * 播放轻音乐歌单的第一首歌曲(独立播放源) */ private void playQingyinFirstSong() { if (qySongList == null || qySongList.isEmpty()) { ToastUtil.showShortToast("暂无轻音乐"); return; } MusicBean.DataBean firstSong = qySongList.get(0); String url = firstSong.getMusic(); // 情况1:正在播放这首轻音乐 → 暂停 if (playerManager.isPlaying() && playerManager.isPlayingFromPrivate() // 我们复用 isPlayingFromPrivate 标志 && url.equals(playerManager.getCurrentPlayingMusicUrl())) { playerManager.pause(); qy_start.setImageResource(R.mipmap.img_lan_bf); // 显示“播放”图标 ToastUtil.showShortToast("已暂停"); return; } // 情况2:已暂停,但可以恢复 if (playerManager.canResumePlaying(url)) { playerManager.resume(); qy_start.setImageResource(R.mipmap.img_lan_zt); // 显示“暂停”图标 ToastUtil.showShortToast("继续播放: " + firstSong.getTitle()); return; } // 情况3:首次播放 or 已释放 → 重新准备 playerManager.playFromExternalList(requireContext(), qySongList, 0); qy_start.setImageResource(R.mipmap.img_lan_zt); // 暂停图标 rotationHelper.stopRotation(); // 停止主列表旋转动画 ToastUtil.showShortToast("播放轻音乐: " + firstSong.getTitle()); } private void playXiaoZhongFirstSong(){ if (xzSongList == null || xzSongList.isEmpty()) { ToastUtil.showShortToast("暂无轻音乐"); return; } MusicBean.DataBean firstSong = xzSongList.get(0); String url = firstSong.getMusic(); // 情况1:正在播放这首轻音乐 → 暂停 if (playerManager.isPlaying() && playerManager.isPlayingFromPrivate() // 我们复用 isPlayingFromPrivate 标志 && url.equals(playerManager.getCurrentPlayingMusicUrl())) { playerManager.pause(); xz_start.setImageResource(R.mipmap.img_lan_bf); // 显示“播放”图标 ToastUtil.showShortToast("已暂停"); return; } // 情况2:已暂停,但可以恢复 if (playerManager.canResumePlaying(url)) { playerManager.resume(); xz_start.setImageResource(R.mipmap.img_lan_zt); // 显示“暂停”图标 ToastUtil.showShortToast("继续播放: " + firstSong.getTitle()); return; } // 情况3:首次播放 or 已释放 → 重新准备 playerManager.playFromExternalList(requireContext(), qySongList, 0); xz_start.setImageResource(R.mipmap.img_lan_zt); // 暂停图标 rotationHelper.stopRotation(); // 停止主列表旋转动画 ToastUtil.showShortToast("播放轻音乐: " + firstSong.getTitle()); } @Override public void onDestroy() { playerManager.release(); rotationHelper.stopRotation(); super.onDestroy(); } } package com.weishitech.qichechangtingyinyue.utils; import android.content.Context; import android.media.AudioAttributes; import android.media.MediaPlayer; import com.weishitech.qichechangtingyinyue.bean.MusicBean; import java.util.List; public class MusicPlayerManager { private MediaPlayer mediaPlayer; private OnPlaybackStateChangedListener listener; private int currentPlayingPosition = -1; private boolean isPlayingFromPrivate = false; public boolean isPlayingFromPrivate() { return isPlayingFromPrivate; } public void setPlayingFromPrivate(boolean flag) { this.isPlayingFromPrivate = flag; } public interface OnPlaybackStateChangedListener { void onPlaying(int position); void onPaused(); void onCompletion(); void onError(String errorMsg); } public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) { this.listener = listener; } // 在 MusicPlayerManager.java 中新增: public void play(Context context, String musicUrl) { play(context, -1, musicUrl); // 用 -1 表示“无位置” } public void play(Context context, int position, String musicUrl) { if (position == currentPlayingPosition && mediaPlayer != null && mediaPlayer.isPlaying()) { return; // 正在播放同一首 } try { release(); mediaPlayer = new MediaPlayer(); 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(); currentPlayingPosition = position; if (listener != null) listener.onPlaying(position); }); mediaPlayer.setOnCompletionListener(mp -> { if (listener != null) listener.onCompletion(); }); mediaPlayer.setOnErrorListener((mp, what, extra) -> { if (listener != null) listener.onError("播放错误: " + what); return true; }); } catch (Exception e) { e.printStackTrace(); if (listener != null) listener.onError("无法播放音乐"); } } public void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); if (listener != null) listener.onPaused(); } } public void resume() { if (mediaPlayer != null && !mediaPlayer.isPlaying()) { mediaPlayer.start(); if (listener != null) listener.onPlaying(currentPlayingPosition); } } public boolean isPlaying() { return mediaPlayer != null && mediaPlayer.isPlaying(); } public void release() { if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } currentPlayingPosition = -1; } public int getCurrentPlayingPosition() { return currentPlayingPosition; } // 新增:获取当前播放的 URL public String getCurrentPlayingMusicUrl() { return currentPlayingUrl; } // 新增字段 private String currentPlayingUrl = null; // public void setOnCompletionListener(Runnable listener) { // if (mediaPlayer != null) { // mediaPlayer.setOnCompletionListener(mp -> { // if (listener != null) listener.run(); // }); // } // } public void playFromExternalList(Context context, List<MusicBean.DataBean> list, int startIndex) { if (list == null || list.isEmpty() || startIndex < 0 || startIndex >= list.size()) { return; } MusicBean.DataBean bean = list.get(startIndex); try { release(); // 先释放旧资源 mediaPlayer = new MediaPlayer(); 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(); currentPlayingPosition = -1; // 表示不在主列表中 currentPlayingUrl = bean.getMusic(); isPlayingFromPrivate = true; // 新增标志位 if (listener != null) listener.onPlaying(-1); // -1 表示来自外部列表 }); mediaPlayer.setOnCompletionListener(mp -> { if (listener != null) listener.onCompletion(); }); mediaPlayer.setOnErrorListener((mp, what, extra) -> { if (listener != null) listener.onError("播放错误: " + what); return true; }); } catch (Exception e) { e.printStackTrace(); if (listener != null) listener.onError("无法播放音乐"); } } /** * 判断是否已加载指定 URL 的音乐并处于暂停状态 */ /** * 判断是否可以继续播放某首歌(已加载且暂停) */ public boolean canResumePlaying(String musicUrl) { return mediaPlayer != null // 播放器还活着 && !mediaPlayer.isPlaying() // 当前没播 && currentPlayingUrl != null && currentPlayingUrl.equals(musicUrl); } } 这个playXiaoZhongFirstSong方法我播放小众音乐ToastUtil.showShortToast("播放小众音乐: " + firstSong.getTitle());这个提示是对的但是没有播放对的音乐
12-04
混合动力汽车(HEV)模型的Simscape模型(Matlab代码、Simulink仿真实现)内容概要:本文档介绍了一个混合动力汽车(HEV)的Simscape模型,该模型通过Matlab代码和Simulink仿真工具实现,旨在对混合动力汽车的动力系统进行建模与仿真分析。模型涵盖了发动机、电机、电池、传动系统等关键部件,能够模拟车辆在不同工况下的能量流动与控制策略,适用于动力系统设计、能耗优化及控制算法验证等研究方向。文档还提及该资源属于一个涵盖多个科研领域的MATLAB仿真资源包,涉及电力系统、机器学习、路径规划、信号处理等多个技术方向,配套提供网盘下载链接,便于用户获取完整资源。; 适合人群:具备Matlab/Simulink使用基础的高校研究生、科研人员及从事新能源汽车系统仿真的工程技术人员。; 使用场景及目标:①开展混合动力汽车能量管理策略的研究与仿真验证;②学习基于Simscape的物理系统建模方法;③作为教学案例用于车辆工程或自动化相关课程的实践环节;④与其他优化算法(如智能优化、强化学习)结合,实现控制策略的优化设计。; 阅读建议:建议使用者先熟悉Matlab/Simulink及Simscape基础操作,结合文档中的模型结构逐步理解各模块功能,可在此基础上修改参数或替换控制算法以满足具体研究需求,同时推荐访问提供的网盘链接获取完整代码与示例文件以便深入学习与调试。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值