播放列表功能开发

This commit is contained in:
litingting 2024-09-30 16:45:21 +08:00
parent 90564a274c
commit 72bf148ad5
17 changed files with 801 additions and 235 deletions

View File

@ -7,7 +7,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"

View File

@ -21,11 +21,36 @@ import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.media3.MyMediaControllerManager;
public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayListBinding> {
private String mCurVideId;
private String lastVideId;
private int curMusicPos = 0;
private MyMediaControllerManager instance = MyMediaControllerManager.getInstance();
@Override
protected ItemPlayListBinding getViewBinding(ViewGroup parent) {
return ItemPlayListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
}
public void setCurVideId(String curVideId) {
lastVideId = mCurVideId;
this.mCurVideId = curVideId;
for (int i = 0; i < data.size(); i++) {
ResponsePlayListInfo listInfo = data.get(i);
if (listInfo.getVideoId().equals(curVideId)) {
notifyItemChanged(i);
}
if (listInfo.getVideoId().equals(lastVideId)) {
notifyItemChanged(i);
}
}
}
public void updateCurMusicAnimation(){
notifyItemChanged(curMusicPos);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
@ -40,20 +65,32 @@ public class AdapterPlayList extends BaseAdapter<ResponsePlayListInfo, ItemPlayL
.into(vb.imCovert);
vb.songName.setText(listInfo.getSongTitle());
vb.artistName.setText(listInfo.getSingerName());
MyMediaControllerManager instance = MyMediaControllerManager.getInstance();
MediaItem currentMediaItem = instance.getMediaController().getCurrentMediaItem();
if (currentMediaItem != null && currentMediaItem.mediaId.equals(listInfo.getVideoId())) {
vb.lottieAnimationView.setVisibility(View.VISIBLE);
vb.viewPlaying.setVisibility(View.VISIBLE);
vb.getRoot().setBackgroundColor(CommonUtils.getMyColor(R.color.cur_play_music));
if (instance.getMediaController().isPlaying()) {
vb.lottieAnimationView.playAnimation();
vb.viewPlaying.startAnimating();
lastVideId = listInfo.getVideoId();
CommonUtils.LogMsg("-------playAnimation");
} else {
vb.lottieAnimationView.pauseAnimation();
vb.viewPlaying.pauseAnimating();
CommonUtils.LogMsg("-------pauseAnimation");
}
} else {
vb.lottieAnimationView.setVisibility(View.GONE);
CommonUtils.LogMsg("-------GONE");
}
vb.viewPlaying.setVisibility(View.GONE);
// CommonUtils.LogMsg("-------GONE");
}
vb.itemLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CommonUtils.LogMsg("-------onClick curMusicPos="+itemHolder.getAbsoluteAdapterPosition());
setCurVideId(listInfo.getVideoId());
instance.playPositionMusic(itemHolder.getAbsoluteAdapterPosition());
}
});
}
}

View File

@ -0,0 +1,158 @@
package com.hi.music.player.customerview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.hi.music.player.helper.CommonUtils;
import java.util.Random;
public class AnimaPlayingView extends View {
private Paint paint;
private int barColor = Color.GREEN; // 音量条的颜色
private int numBars = 5; // 音量条的数量
private float[] barHeights; // 每个音量条的高度
private int maxHeight = CommonUtils.dpToPx(20); // 音量条的最大高度
private int minHeight = CommonUtils.dpToPx(4); // 音量条的最小高度
// 每个柱子的宽度
private int barWidth =CommonUtils. dpToPx(4);
// 柱子之间的间距
private int barSpacing = CommonUtils.dpToPx(2);
private int cornerRadius = CommonUtils.dpToPx(16);
private Random random;
private ValueAnimator animator;
public AnimaPlayingView(Context context) {
super(context);
init();
}
public AnimaPlayingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimaPlayingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setColor(barColor);
paint.setStyle(Paint.Style.FILL);
barHeights = new float[numBars];
random = new Random();
// 初始化音量条高度
for (int i = 0; i < numBars; i++) {
barHeights[i] = 0;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
int barCount = barHeights.length;
int totalWidth = barCount * barWidth + (barCount - 1) * barSpacing;
int startX = (width - totalWidth) / 2;
for (int i = 0; i < numBars; i++) {
float left = startX + i * (barWidth + barSpacing);
float top = height - barHeights[i];
float right = left + barWidth;
float bottom = height;
canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, paint);
}
}
// 启动固定动画使音量条的高度变化
public void startAnimating() {
for (int i = 0; i < numBars; i++) {
animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000); // 每0.5秒更新一次
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setInterpolator(new LinearInterpolator());
int finalI = i;
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 更新每个音量条的高度使其不断变化
float animatedValue = (Float) animation.getAnimatedValue();
float v = animatedValue * maxHeight;
barHeights[finalI] = v;
invalidate(); // 重绘视图
}
});
animator.setStartDelay(i* 200L);
animator.start();
}
}
public void pauseAnimating(){
if(animator!= null){
animator.pause();
}
for (int i = 0; i < numBars; i++) {
barHeights[i] = minHeight;
}
invalidate();
}
// 随机生成音量条的高度
private float randomHeight() {
return minHeight + random.nextFloat() * (maxHeight - minHeight);
}
// 设置音量条的颜色
public void setBarColor(int color) {
barColor = color;
paint.setColor(barColor);
invalidate();
}
// 设置音量条的最大高度
public void setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
invalidate();
}
// 设置音量条的最小高度
public void setMinHeight(int minHeight) {
this.minHeight = minHeight;
invalidate();
}
// 设置音量条数量
public void setNumBars(int numBars) {
this.numBars = numBars;
barHeights = new float[numBars];
invalidate();
}
}

View File

@ -6,13 +6,16 @@ import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.ImageView;
import androidx.media3.common.MediaItem;
import androidx.palette.graphics.Palette;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.R;
import com.hi.music.player.api.onImageColorListener;
import org.json.JSONException;
@ -51,6 +54,7 @@ public class CommonUtils {
public static int dpToPx(int dp) {
return Math.round(dp * (MusicApplication.myApplication.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
public static int pxToDp(int px) {
return Math.round(px / (MusicApplication.myApplication.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
@ -75,7 +79,6 @@ public class CommonUtils {
}
//time 3:45
public static long convertToMilliseconds(String time) {
String[] parts = time.split(":");
@ -92,6 +95,10 @@ public class CommonUtils {
return String.format("%d:%02d", minutes, seconds); // 格式化为 mm:ss
}
public static int getMyColor(int resId) {
return MusicApplication.myApplication.getColor(resId);
}
// 使用 Palette 提取深色主色调
public static void getDominantDarkColor1(Drawable imDraw, onImageColorListener listener) {
@ -99,7 +106,8 @@ public class CommonUtils {
BitmapDrawable drawable = (BitmapDrawable) imDraw;
if (drawable != null) {
Bitmap bitmap = drawable.getBitmap();
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
Palette.from(bitmap)
.generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
// 首先尝试获取深色活力色
@ -162,4 +170,34 @@ public class CommonUtils {
drawable.setCornerRadii(radii);
return drawable;
}
/**
* 当前媒体项是否有有效的播放地址
* @param mediaItem
* @return
*/
public static boolean hasValidUri(MediaItem mediaItem) {
MediaItem.LocalConfiguration localConfiguration = mediaItem.localConfiguration;
if(localConfiguration != null){
if(CommonUtils.isUriFormat(localConfiguration.uri)){
return true;
}
}
return false;
}
private static boolean isUriFormat(Uri parsedUri) {
// 3. 检查 URI 是否具有正确的格式
try {
if (parsedUri.getScheme() == null) {
LogMsg( "URI has no valid scheme.");
return false;
}
} catch (Exception e) {
LogMsg( "URI parsing failed: " + e.getMessage());
return false;
}
return true;
}
}

View File

@ -13,5 +13,7 @@ public class MyValue {
//暂停或者停止
public final static int PLAY_STATUS_CODE_PAUSE = -3;
//-----------------------------PlayActivity
}

View File

@ -0,0 +1,169 @@
package com.hi.music.player.media3;
import android.net.Uri;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.ResolvingDataSource;
import androidx.media3.datasource.cache.CacheDataSource;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.MediaSourceFactory;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.network.JsonHelper;
import com.hi.music.player.network.RetrofitManager;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.ResponseBody;
public class DynamicMediaSourceFactory implements MediaSource.Factory {
// private final CacheDataSource.Factory cacheDataSourceFactory;
// private final ExoPlayer player;
private DefaultMediaSourceFactory mediaSourceFactory;
// public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) {
// this.cacheDataSourceFactory = cacheDataSourceFactory;
// this.player = exoPlayer;
// this.mediaSourceFactory = factory;
// }
public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory) {
this.mediaSourceFactory = factory;
}
@UnstableApi
@Override
public MediaSource.Factory setDrmSessionManagerProvider(DrmSessionManagerProvider drmSessionManagerProvider) {
return null;
}
@UnstableApi
@Override
public MediaSource.Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
return null;
}
@UnstableApi
@Override
public int[] getSupportedTypes() {
return new int[0];
}
@UnstableApi
@Override
public MediaSource createMediaSource(MediaItem mediaItem) {
CommonUtils.LogMsg("------createMediaSource----------=" + mediaItem.mediaId + "---------" + mediaItem.mediaMetadata.title);
// MediaSource realMediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(""));
// RetrofitManager.getInstance().getPlayUrl(mediaItem.mediaId, new RequestListener<ResponseBody>() {
//
// @Override
// public void onFail(String errorMsg) {
//
// }
//
// @Override
// public void onSuccess(ResponseBody data) {
// JSONObject jsonObject = CommonUtils.toJsonObject(data);
// if (jsonObject != null) {
// ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
// if (responsePlayUrl == null) {
// // TODO: 2024/9/27
// return;
// }
// String uri = "";
// if (responsePlayUrl.getAudioUrlMedium() != null) {
// uri = responsePlayUrl.getAudioUrlMedium();
// } else {
// uri = responsePlayUrl.getAudioUrlLow();
// }
// CommonUtils.LogMsg("------createMediaSource----------="+mediaItem.mediaId+"---------"+uri);
// ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(uri));
// player.setMediaSource(mediaSource);
// player.prepare();
//
// }
//
//
// }
// });
// return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(responsePlayUrl.getAudioUrlMedium()));
// 可以在这里根据 mediaItem ID 动态生成新的 URI
String updatedUri = "https://rr1---sn-tt1e7nlz.googlevideo.com/videoplayback?expire=1727628605&ei=3TD5ZuWFEe-yzN0P27WuqQ4&ip=146.19.167.8&id=o-ANGM0PjEvsfYH7TmYV_DFuD-65tipJeeLe2URDOk90sL&itag=140&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=xj&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknsk&ms=au%2Crdu&mv=m&mvi=1&pl=24&pcm2=no&gcr=us&initcwndbps=7610000&vprv=1&svpuc=1&mime=audio%2Fmp4&rqh=1&gir=yes&clen=5440168&dur=335.973&lmt=1709326903801285&mt=1727606690&fvip=5&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=2318224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cpcm2%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRQIhALmM_S8Cmagr60muB3wDOby0OdcjF-x6f7TcEenixH0KAiAnR0-hmA03MeVzSg2wi5ncJ4Ve5FFlpZnlSoRNGWgGhQ%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIhAPv5slfEnf8_E7o6yjEkussQ6JIFFaSY6QtP9HXncTTcAiAjlhMa71t76Wu1R1rcmsHoO6pyxjhGYouio4D0deJqEA%3D%3D";
Uri updateUri = null;
MediaItem.Builder builder = mediaItem.buildUpon();
if (mediaItem.localConfiguration != null) {
updateUri = mediaItem.localConfiguration.uri;
builder.setUri(updateUri);
CommonUtils.LogErrorMsg("----updateUri=成功 builder="+mediaItem.mediaId+"--updateUri="+updateUri);
}else {
CommonUtils.LogErrorMsg("----updateUri=null builder="+mediaItem.mediaId);
}
return mediaSourceFactory.createMediaSource(builder.build());
}
// @UnstableApi
// @Override
// public DataSource createDataSource() {
//
//
//
//
//// RetrofitManager.getInstance().getPlayUrl(mediaItem.mediaId, new RequestListener<ResponseBody>() {
////
//// @Override
//// public void onFail(String errorMsg) {
////
//// }
////
//// @Override
//// public void onSuccess(ResponseBody data) {
//// JSONObject jsonObject = CommonUtils.toJsonObject(data);
//// if (jsonObject != null) {
//// ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
//// if (responsePlayUrl == null) {
//// // TODO: 2024/9/27
//// return;
//// }
//// String uri = "";
//// if (responsePlayUrl.getAudioUrlMedium() != null) {
//// uri = responsePlayUrl.getAudioUrlMedium();
//// } else {
//// uri = responsePlayUrl.getAudioUrlLow();
//// }
//// CommonUtils.LogMsg("------createMediaSource----------="+mediaItem.mediaId+"---------"+uri);
//// ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
//// .createMediaSource(MediaItem.fromUri(uri));
//// player.setMediaSource(mediaSource);
//// player.prepare();
////
//// }
////
////
//// }
//// });
// return null;
// }
}

View File

@ -24,6 +24,7 @@ import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.network.RetrofitManager;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -61,7 +62,6 @@ public class MyMediaControllerManager {
// playerView.setPlayer(controllerFuture.get());
try {
mediaController = controllerFuture.get();
statusListener.onMediaControllerComplete(true);
CommonUtils.LogMsg("=-----mediaController+" + mediaController);
} catch (ExecutionException | InterruptedException e) {
@ -98,19 +98,16 @@ public class MyMediaControllerManager {
if (isPlaying) {
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PLAYING);
// TODO: 2024/9/26 自动播放完成切歌到下一首播放没有触发这里请求下一首
// 播放器开始播放
MediaItem currentItem = mediaController.getCurrentMediaItem();
int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
if (currentMediaItemIndex < playList.size() - 1) {
//代表有下一首
if (!mediaController.hasNextMediaItem()) {
//没有更新下一首的uri
if (mediaController.hasNextMediaItem()) {
MediaItem mediaItemAt = mediaController.getMediaItemAt(mediaController.getNextMediaItemIndex());
if (!CommonUtils.hasValidUri(mediaItemAt)) {
CommonUtils.LogMsg("----0000000000-请求下一首的uri" + mediaController.getMediaItemCount());
int nextIndex = currentMediaItemIndex + 1;
mListener.onRequestNextUri(playList.get(nextIndex).getVideoId(), nextIndex,false);
int nextIndex = mediaController.getNextMediaItemIndex();
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextIndex).mediaId, nextIndex, false);
}
}
}
} else {
// 播放器暂停或停止
mListener.onPlayStatus(MyValue.PLAY_STATUS_CODE_PAUSE);
@ -119,7 +116,6 @@ public class MyMediaControllerManager {
@Override
public void onPlaybackStateChanged(int playbackState) {
CommonUtils.LogMsg("----playbackState" + playbackState + "------------" + mediaController.getMediaItemCount());
mListener.onPlayStatus(playbackState);
}
@ -154,32 +150,30 @@ public class MyMediaControllerManager {
// *
// * @param playUrl
// */
// public void UpdateAudioUrl(ResponsePlayUrl playUrl) {
//
// CommonUtils.LogMsg("-------------更新播放列表中的音频 mediaController.getMediaItemCount()=" + mediaController.getMediaItemCount());
// for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
// MediaItem mediaItemAt = mediaController.getMediaItemAt(i);
// if (mediaItemAt.mediaId.equals(playUrl.getVideoId())) {
//
// MediaItem.Builder builder = mediaItemAt.buildUpon();
//// mediaController.removeMediaItem(i);
//// MediaItem.Builder builder = new MediaItem.Builder();
// builder.setMediaId(playUrl.getVideoId());
// if (playUrl.getAudioUrlMedium() != null) {
// builder.setUri(playUrl.getAudioUrlMedium());
// } else {
// builder.setUri(playUrl.getAudioUrlLow());
// }
// mediaController.replaceMediaItem(i, builder.build());
// CharSequence title = mediaController.getMediaItemAt(i).mediaMetadata.title;
// CommonUtils.LogMsg("-------------更新播放列表中的音频url= i=" + i + "---title=" + title + "----mediaController.size=" + mediaController.getMediaItemCount() + "---" + mediaController);
// break;
// }
// }
// }
public void UpdateAudioUrl(ResponsePlayUrl playUrl, int index) {
CommonUtils.LogMsg("-------------更新播放列表中的音频url= index=" + index);
for (int i = 0; i < mediaController.getMediaItemCount(); i++) {
MediaItem mediaItemAt = mediaController.getMediaItemAt(i);
if (mediaItemAt.mediaId.equals(playUrl.getVideoId())) {
MediaItem.Builder builder = mediaItemAt.buildUpon();
builder.setMediaId(playUrl.getVideoId());
if (playUrl.getAudioUrlMedium() != null) {
builder.setUri(playUrl.getAudioUrlMedium());
} else {
builder.setUri(playUrl.getAudioUrlLow());
}
CharSequence title = mediaController.getMediaItemAt(i).mediaMetadata.title;
CommonUtils.LogMsg("-------------更新播放列表中的音频url= i=" + i + "---id=" + playUrl.getVideoId() + "----mediaController.size=" + mediaController.getMediaItemCount());
mediaController.replaceMediaItem(i, builder.build());
break;
}
}
}
public void setPlayList(List<ResponsePlayListInfo> playList) {
this.playList = playList;
addMusicPlayList(playList);
}
public List<ResponsePlayListInfo> getPlayList() {
@ -192,7 +186,7 @@ public class MyMediaControllerManager {
/**
* 添加播放列表不带音频url
* 注意没有更新有效uri的不会被实际添加到mediaController
* 注意没有setUri的不会被实际添加到mediaController,
*
* @param playUrl
*/
@ -214,7 +208,6 @@ public class MyMediaControllerManager {
MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
MediaMetadata_builder.setArtist(listInfo.getSingerName());
/**
* 这里使用setDescription保存歌曲时长的分秒字符串
*/
@ -228,49 +221,53 @@ public class MyMediaControllerManager {
// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
builder.setMediaMetadata(MediaMetadata_builder.build());
mediaController.addMediaItem(builder.build());
}
}
}
// /**
// * 添加播放列表不带音频url
// * 注意没有更新有效uri的不会被实际添加到mediaController
// *
// * @param listInfo
// */
// @OptIn(markerClass = UnstableApi.class)
// public void addMusicPlayList(List<ResponsePlayListInfo> listInfo) {
// playList = listInfo;
// List<MediaItem> mediaItems = new ArrayList<>();
// for (int i = 0; i < listInfo.size(); i++) {
// ResponsePlayListInfo playInfo = listInfo.get(i);
//
// MediaItem.Builder builder = new MediaItem.Builder();
//
// //唯一标识符
// builder.setMediaId(playInfo.getVideoId());
//
// MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
//
// MediaMetadata_builder.setArtist(playInfo.getSingerName());
// MediaMetadata_builder.setDurationMs(playInfo.getDurationMs());
//// MediaMetadata_builder.setArtworkUri(Uri.parse(playInfo.getCovert()));
// MediaMetadata_builder.setArtworkUri(Uri.parse("https://t7.baidu.com/it/u=2604797219,1573897854&fm=193&f=GIF"));
// MediaMetadata_builder.setTitle(playInfo.getSongTitle());
//
// CommonUtils.LogMsg("----------添加播放列表 i=" + i + "---" + playInfo.getSongTitle() + "-------VideoId=" + playInfo.getVideoId());
//// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
// builder.setMediaMetadata(MediaMetadata_builder.build());
// mediaController.addMediaItem(builder.build());
//// mediaItems.add(builder.build());
// }
//// mediaController.setMediaItems(mediaItems);
// }
/**
* 添加播放列表不带音频url
* 注意没有setUri的不会被实际添加到mediaController(mediaController.getMediaItemCount())
* 这里setUri 一个占位
*
* @param listInfo
*/
@OptIn(markerClass = UnstableApi.class)
public void addMusicPlayList(List<ResponsePlayListInfo> listInfo) {
playList = listInfo;
List<MediaItem> mediaItems = new ArrayList<>();
for (int i = 0; i < listInfo.size(); i++) {
ResponsePlayListInfo playInfo = listInfo.get(i);
MediaItem.Builder builder = new MediaItem.Builder();
//唯一标识符
builder.setMediaId(playInfo.getVideoId());
builder.setUri("-------test-----");
MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
MediaMetadata_builder.setArtist(playInfo.getSingerName());
MediaMetadata_builder.setDescription(playInfo.getDuration());
MediaMetadata_builder.setDurationMs(playInfo.getDurationMs());
MediaMetadata_builder.setArtworkUri(Uri.parse(playInfo.getCovert()));
MediaMetadata_builder.setTitle(playInfo.getSongTitle());
CommonUtils.LogMsg("----------添加播放列表 i=" + i + "---" + playInfo.getSongTitle() + "-------VideoId=" + playInfo.getVideoId());
// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
builder.setMediaMetadata(MediaMetadata_builder.build());
mediaController.addMediaItem(builder.build());
}
}
public void play() {
if (!mediaController.isPlaying()) {
CommonUtils.LogMsg("-----------prepare");
mediaController.prepare();
mediaController.play();
}
}
@ -279,21 +276,32 @@ public class MyMediaControllerManager {
if (mediaController.isPlaying())
mediaController.pause();
}
public void stop() {
if (mediaController.isPlaying())
mediaController.stop();
}
public void playNext() {
if (mediaController.hasNextMediaItem()) {
int nextMediaItemIndex = mediaController.getNextMediaItemIndex();
MediaItem mediaItemAt = mediaController.getMediaItemAt(nextMediaItemIndex);
boolean b = CommonUtils.hasValidUri(mediaItemAt);
if (b) {
mediaController.seekToNextMediaItem();
} else {
int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
if (currentMediaItemIndex < playList.size() - 1) {
//应该有下一首可能是歌曲url请求失败需要重新请求
int nextIndex = currentMediaItemIndex + 1;
mListener.onRequestNextUri(playList.get(nextIndex).getVideoId(), nextIndex,true);
CommonUtils.LogMsg("-------------应该有下一首可能是歌曲url请求失败需要重新请求");
return;
}
CommonUtils.LogMsg("-------------有下一首但是歌曲url请求失败需要重新请求");
mListener.onRequestNextUri(mediaController.getMediaItemAt(nextMediaItemIndex).mediaId, nextMediaItemIndex, true);
} else {
// int currentMediaItemIndex = mediaController.getCurrentMediaItemIndex();
// if (currentMediaItemIndex < playList.size() - 1) {
// //应该有下一首可能是歌曲url请求失败需要重新请求
// int nextIndex = currentMediaItemIndex + 1;
// mListener.onRequestNextUri(playList.get(nextIndex).getVideoId(), nextIndex, true);
// CommonUtils.LogMsg("-------------应该有下一首可能是歌曲url请求失败需要重新请求");
// }
}
}
@ -305,4 +313,29 @@ public class MyMediaControllerManager {
mediaController.play();
}
}
/**
* 播放指定播放列表位置的歌曲
*
* @param index
*/
public void playPositionMusic(int index) {
if (index >= mediaController.getMediaItemCount()) {
CommonUtils.LogErrorMsg("-------------数组越界");
return;
}
stop();
mediaController.prepare();
mediaController.seekTo(index);
MediaItem mediaItemAt = mediaController.getMediaItemAt(index);
boolean b = CommonUtils.hasValidUri(mediaItemAt);
if (!b) {
CommonUtils.LogMsg("-------------播放指定播放列表位置的歌曲,需要重新请求");
mListener.onRequestNextUri(mediaItemAt.mediaId, index, true);
}
}
}

View File

@ -5,48 +5,59 @@ import android.content.Intent;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.database.StandaloneDatabaseProvider;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.ResolvingDataSource;
import androidx.media3.datasource.cache.CacheDataSource;
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor;
import androidx.media3.datasource.cache.SimpleCache;
import androidx.media3.exoplayer.DefaultLoadControl;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.extractor.ExtractorsFactory;
import androidx.media3.extractor.mkv.MatroskaExtractor;
import androidx.media3.session.MediaSession;
import androidx.media3.session.MediaSessionService;
import com.google.common.util.concurrent.ListenableFuture;
import com.hi.music.player.R;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
public class PlaybackService extends MediaSessionService {
private MediaSession mediaSession = null;
private ExoPlayer player;
@OptIn(markerClass = UnstableApi.class)
@Override
public void onCreate() {
super.onCreate();
player = new ExoPlayer.Builder(this).build();
DynamicMediaSourceFactory customMediaSourceFactory = new DynamicMediaSourceFactory(new DefaultMediaSourceFactory(new DefaultHttpDataSource.Factory()));
DefaultMediaSourceFactory defaultMediaSourceFactory1 = new DefaultMediaSourceFactory(getUrlFactory());
// 创建 DefaultLoadControl配置缓冲参数
DefaultLoadControl loadControl = new DefaultLoadControl.Builder()
.setBufferDurationsMs(10000, // minBufferMs: 播放前的最小缓冲时间毫秒
60000, // maxBufferMs: 最大缓冲时间毫秒
10000, // bufferForPlaybackMs: 播放时的目标缓冲时间毫秒
5000) // bufferForPlaybackAfterRebufferMs: 重新缓冲后的缓冲时间毫秒)
.build();
player = new ExoPlayer.Builder(this)
// .setMediaSourceFactory(customMediaSourceFactory)
// .setLoadControl(loadControl)
.build();
mediaSession = new MediaSession.Builder(this, player)
.setCallback(new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(MediaSession mediaSession, MediaSession.ControllerInfo controller, List<MediaItem> mediaItems) {
CommonUtils.LogMsg("--------实际添加的媒体项="+mediaItems.size()+"----"+mediaSession.getPlayer().getMediaItemCount());
return MediaSession.Callback.super.onAddMediaItems(mediaSession, controller, mediaItems);
}
})
.build();
// player = new ExoPlayer.Builder(this)
@ -69,12 +80,39 @@ public class PlaybackService extends MediaSessionService {
// .build();
}
@OptIn(markerClass = UnstableApi.class)
private DataSource.Factory getUrlFactory() {
CommonUtils.LogMsg("--------getUrlFactory");
return new ResolvingDataSource.Factory(getCacheDataSource(), new ResolvingDataSource.Resolver() {
@Override
public DataSpec resolveDataSpec(DataSpec dataSpec) {
CommonUtils.LogMsg("--------resolveDataSpec=" + dataSpec.key);
return null;
}
});
}
@OptIn(markerClass = UnstableApi.class)
private CacheDataSource.Factory getCacheDataSource() {
// 1. 创建缓存对象
SimpleCache simpleCache = new SimpleCache(
new File(MusicApplication.myApplication.getCacheDir(), "media3_cache"), // 设置缓存目录
new LeastRecentlyUsedCacheEvictor(100 * 1024 * 1024), // 设置最大缓存大小例如 100MB
new StandaloneDatabaseProvider(MusicApplication.myApplication));
// 2. 创建 DataSource.Factory用于加载媒体资源
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(MusicApplication.myApplication);
// 3. 创建 CacheDataSource.Factory包含缓存功能
return new CacheDataSource.Factory()
.setCache(simpleCache)
.setUpstreamDataSourceFactory(dataSourceFactory) // 用于从网络加载的工厂
.setCacheWriteDataSinkFactory(null); // 默认写入缓存
}
@Nullable
@Override

View File

@ -0,0 +1,73 @@
package com.hi.music.player.media3;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import com.hi.music.player.helper.CommonUtils;
public class testSourceFactory implements DataSource.Factory {
// private final CacheDataSource.Factory cacheDataSourceFactory;
// private final ExoPlayer player;
private DefaultMediaSourceFactory mediaSourceFactory;
// public DynamicMediaSourceFactory(DefaultMediaSourceFactory factory,CacheDataSource.Factory cacheDataSourceFactory, ExoPlayer exoPlayer) {
// this.cacheDataSourceFactory = cacheDataSourceFactory;
// this.player = exoPlayer;
// this.mediaSourceFactory = factory;
// }
public testSourceFactory(DefaultMediaSourceFactory factory) {
this.mediaSourceFactory = factory;
}
@UnstableApi
@Override
public DataSource createDataSource() {
// RetrofitManager.getInstance().getPlayUrl(mediaItem.mediaId, new RequestListener<ResponseBody>() {
//
// @Override
// public void onFail(String errorMsg) {
//
// }
//
// @Override
// public void onSuccess(ResponseBody data) {
// JSONObject jsonObject = CommonUtils.toJsonObject(data);
// if (jsonObject != null) {
// ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
// if (responsePlayUrl == null) {
// // TODO: 2024/9/27
// return;
// }
// String uri = "";
// if (responsePlayUrl.getAudioUrlMedium() != null) {
// uri = responsePlayUrl.getAudioUrlMedium();
// } else {
// uri = responsePlayUrl.getAudioUrlLow();
// }
// CommonUtils.LogMsg("------createMediaSource----------="+mediaItem.mediaId+"---------"+uri);
// ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
// .createMediaSource(MediaItem.fromUri(uri));
// player.setMediaSource(mediaSource);
// player.prepare();
//
// }
//
//
// }
// });
return null;
}
}

View File

@ -244,7 +244,6 @@ public class JsonHelper {
if (mimeType.contains("audio/mp4")) {
String url = jsonIndex.getString("url");
boolean audioQuality1 = jsonIndex.has("audioQuality");
CommonUtils.LogMsg("------------itag="+itag+"---------audioQuality1="+audioQuality1);
if (jsonIndex.has("audioQuality")) {
String audioQuality = jsonIndex.getString("audioQuality");
if (audioQuality.equals("AUDIO_QUALITY_MEDIUM")) {

View File

@ -15,12 +15,12 @@ public class ObserverWrapper<T> implements Observer<T> {
@Override
public void onSubscribe(Disposable d) {
CommonUtils.LogMsg("----------onSubscribe");
}
@Override
public void onNext(T t) {
CommonUtils.LogMsg("----------onNext");
requestListener.onSuccess(t);
}
@ -32,7 +32,7 @@ public class ObserverWrapper<T> implements Observer<T> {
@Override
public void onComplete() {
CommonUtils.LogMsg("----------onComplete");
}
}

View File

@ -57,8 +57,6 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
//接口返回的播放列表(没有音频数据)
private List<ResponsePlayListInfo> mPlayList;
//播放列对应的音频数据
private List<ResponsePlayUrl> mPlayUrls;
private ResponsePlayUrl mCurPlayInfo;
private ResponsePlayListInfo musicInfo;
@ -66,6 +64,8 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
private Runnable mRunnable;
private MyMediaControllerManager mediaControllerManager;
private AdapterPlayList adapterPlayList;
//播放列表ui初始化
private boolean initPlayList = false;
@ -170,6 +170,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
break;
case Player.STATE_ENDED:
//播放完成
vb.btnPlay.setSelected(false);
CommonUtils.LogMsg("-------------playStatus=STATE_ENDED 播放完成");
mHandler.removeCallbacks(mRunnable); // 停止更新
@ -199,7 +200,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
@Override
public void onChangeMusic(MediaItem mediaItem) {
CommonUtils.LogMsg("111111111-------22222---"+mediaItem.mediaMetadata.title +"---id="+mediaItem.mediaId);
CommonUtils.LogMsg("歌曲切换-" + mediaItem.mediaMetadata.title + "---id=" + mediaItem.mediaId);
loadInfo(mediaItem);
}
@ -250,7 +251,7 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
private void loadInfo(MediaItem mediaItem) {
MediaMetadata mediaMetadata = mediaItem.mediaMetadata;
CommonUtils.LogMsg("--------------加载当前播放歌曲信息 " + mediaMetadata.description);
CommonUtils.LogMsg("--------------加载当前播放歌曲信息 ");
if (mediaMetadata.artworkUri != null) {
loadCovert(mediaMetadata.artworkUri.toString());
}
@ -263,7 +264,9 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
vb.playProgress.setMax((int) durationMs);
vb.progressBarBuffer.setMax((int) durationMs);
}
if (vb.layoutPlayList.linearLayout.getVisibility() == View.VISIBLE) {
updatePlayListUi();
}
}
@ -297,6 +300,11 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
);
vb.rootLayout.setBackground(gradientDrawable);
if (vb.layoutPlayList.linearLayout.getVisibility() == View.VISIBLE) {
updatePlayListColor();
}
}
});
@ -355,6 +363,9 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
} else {
mediaControllerManager.pause();
}
if(adapterPlayList!= null){
adapterPlayList.updateCurMusicAnimation();
}
}
}
@ -374,52 +385,14 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
if (!initPlayList) {
List<ResponsePlayListInfo> playList = mediaControllerManager.getPlayList();
AdapterPlayList adapterPlayList = new AdapterPlayList();
adapterPlayList = new AdapterPlayList();
vb.layoutPlayList.recyclerList.setLayoutManager(new LinearLayoutManager(MusicApplication.myApplication));
adapterPlayList.addData(playList);
vb.layoutPlayList.recyclerList.setAdapter(adapterPlayList);
vb.layoutPlayList.imPlay.setOnClickListener(this);
initPlayList = true;
}
MediaItem currentMediaItem = mediaControllerManager.getMediaController().getCurrentMediaItem();
if (currentMediaItem != null) {
Uri artworkUri = currentMediaItem.mediaMetadata.artworkUri;
vb.layoutPlayList.topSongName.setText(currentMediaItem.mediaMetadata.title);
vb.layoutPlayList.topSingerName.setText(currentMediaItem.mediaMetadata.artist);
vb.layoutPlayList.topLayout.setBackgroundColor(darkerColor);
GradientDrawable gradientDrawable = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{darkerColor, darkerColor}
);
vb.rootLayout.setBackground(gradientDrawable);
Drawable newDrawable = CommonUtils.getNewDrawable(lighterColor, 24f, 24f, 0, 0);
vb.layoutPlayList.listLayout.setBackground(newDrawable);
Glide.with(MusicApplication.myApplication)
.asDrawable()
.apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10))))
.load(artworkUri)
.placeholder(R.mipmap.ic_launcher)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(vb.layoutPlayList.topIm);
}
vb.layoutPlayList.imPlay.setSelected(mediaControllerManager.getMediaController().isPlaying());
updatePlayListUi();
} else {
@ -434,6 +407,56 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> implements S
}
/**
* 更新播放列表的显示
*/
private void updatePlayListUi() {
CommonUtils.LogMsg("----------更新播放列表的显示");
MediaItem currentMediaItem = mediaControllerManager.getMediaController().getCurrentMediaItem();
if (currentMediaItem != null) {
if (adapterPlayList != null) {
adapterPlayList.setCurVideId(currentMediaItem.mediaId);
}
Uri artworkUri = currentMediaItem.mediaMetadata.artworkUri;
vb.layoutPlayList.topSongName.setText(currentMediaItem.mediaMetadata.title);
vb.layoutPlayList.topSingerName.setText(currentMediaItem.mediaMetadata.artist);
updatePlayListColor();
Glide.with(MusicApplication.myApplication)
.asDrawable()
.apply(RequestOptions.bitmapTransform(new RoundedCorners(CommonUtils.dpToPx(10))))
.load(artworkUri)
.placeholder(R.mipmap.ic_launcher)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
CommonUtils.LogMsg(e.getMessage());
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(vb.layoutPlayList.topIm);
}
vb.layoutPlayList.imPlay.setSelected(mediaControllerManager.getMediaController().isPlaying());
}
private void updatePlayListColor() {
vb.layoutPlayList.topLayout.setBackgroundColor(darkerColor);
GradientDrawable gradientDrawable = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{darkerColor, darkerColor}
);
vb.rootLayout.setBackground(gradientDrawable);
Drawable newDrawable = CommonUtils.getNewDrawable(lighterColor, 24f, 24f, 0, 0);
vb.layoutPlayList.listLayout.setBackground(newDrawable);
}
@Override
protected void onDestroy() {
super.onDestroy();

View File

@ -37,9 +37,6 @@ public class VMPlay extends ViewModel {
String videoId = responseSingle.getVideoId();
String params = responseSingle.getParams();
String musicVideoType = responseSingle.getMusicVideoType();
CommonUtils.LogMsg("111111111-------0000---"+responseSingle.getSongTitle() +"---id="+videoId);
RetrofitManager.getInstance().getPlayList(params, playlistId, videoId, musicVideoType, new RequestListener<ResponseBody>() {
@Override
@ -54,7 +51,6 @@ public class VMPlay extends ViewModel {
List<ResponsePlayListInfo> responsePlayListInfos = JsonHelper.ResolvePlayListJson(jsonObject);
MyMediaControllerManager.getInstance().setPlayList(responsePlayListInfos);
_playList.setValue(responsePlayListInfos);
CommonUtils.LogMsg("111111111-------1111----"+responsePlayListInfos.get(0).getSongTitle() +"---id="+responsePlayListInfos.get(0).getVideoId());
getPlayUrl(responseSingle.getVideoId(),0,true);
} else {
_playList.setValue(null);
@ -83,7 +79,8 @@ public class VMPlay extends ViewModel {
// TODO: 2024/9/27
return;
}
MyMediaControllerManager.getInstance().addMusicToPlayList(responsePlayUrl,playListIndex);
MyMediaControllerManager.getInstance().UpdateAudioUrl(responsePlayUrl,playListIndex);
// MyMediaControllerManager.getInstance().addMusicToPlayList(responsePlayUrl,playListIndex);
customerUrlInfo.setPlayUrl(responsePlayUrl);
customerUrlInfo.setPlayMusicIndex(playListIndex);

View File

@ -2,15 +2,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/root_layout"
android:background="@color/black"
tools:context=".ui.activity.PlayActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
@ -51,16 +51,6 @@
app:show_shuffle_button="true" />
<ProgressBar
android:id="@+id/progressBarLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTint="@color/white"
app:layout_constraintBottom_toBottomOf="@id/im_covert"
app:layout_constraintLeft_toLeftOf="@id/im_covert"
app:layout_constraintRight_toRightOf="@id/im_covert"
app:layout_constraintTop_toTopOf="@id/im_covert" />
<TextView
android:id="@+id/tv_song_name"
android:layout_width="wrap_content"
@ -186,4 +176,13 @@
<include
android:id="@+id/layout_playList"
layout="@layout/dialog_play_list" />
<ProgressBar
android:id="@+id/progressBarLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
android:indeterminateTint="@color/white" />
</FrameLayout>

View File

@ -25,6 +25,7 @@
android:id="@+id/top_im"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="fitXY"
android:layout_marginStart="10dp"
android:src="@mipmap/ic_launcher" />
@ -64,11 +65,11 @@
<ImageView
android:id="@+id/im_play"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="42dp"
android:layout_marginEnd="22dp"
android:padding="20dp"
android:src="@drawable/selector_small_play" />
</RelativeLayout>

View File

@ -1,39 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="70dp"
android:id="@+id/item_layout"
android:paddingStart="20dp">
<ImageView
android:layout_width="60dp"
android:id="@+id/im_covert"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:layout_height="60dp" />
android:src="@mipmap/ic_launcher" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimationView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:padding="5dp"
android:layout_alignTop="@id/im_covert"
android:layout_alignBottom="@id/im_covert"
<com.hi.music.player.customerview.AnimaPlayingView
android:id="@+id/view_playing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/im_covert"
android:layout_alignTop="@id/im_covert"
android:layout_alignEnd="@id/im_covert"
app:lottie_fileName="music_bounce.json"
app:lottie_autoPlay="false"
app:lottie_loop="true"/>
android:layout_alignBottom="@id/im_covert"
android:layout_marginBottom="10dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/im_covert"
android:layout_marginStart="12dp"
android:layout_centerVertical="true"
android:layout_marginStart="12dp"
android:layout_toEndOf="@id/im_covert"
android:orientation="vertical">
<TextView
android:id="@+id/song_name"
android:layout_width="wrap_content"

View File

@ -10,6 +10,7 @@
<color name="white_60_color">#99FFFFFF</color>
<color name="progress_buffer_color">#59FFFFFF</color>
<color name="default_play_list_color">#1A1A1A</color>
<color name="cur_play_music">#4DFFFFFF</color>
<color name="test">#2D9C31</color>
</resources>