Android媒体框架: https://developer.android.com/guide/topics/media-apps/audio-app/building-an-audio-app
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "com.android.support:support-media-compat:28.0.0"
媒体播放资源提供端
- onGetRoot
- onLoadChildren
- MediaSessionCompat.Token
- MediaSessionCompat.Callback
- MediaControllerCompat.Callback
- PlaybackStateCompat.Builder
实际播放器,基于接口可被替换
展示媒体进度和控制播放状态
mSession.sendSessionEvent(“error_index”, bundle);
mediaControllerCompat.getTransportControls().sendCustomAction(“play_index”,bundle);
1.单曲循环 PlaybackStateCompat.REPEAT_MODE_ONE
2.整体循环 PlaybackStateCompat.REPEAT_MODE_ALL
3.不做循环 PlaybackStateCompat.REPEAT_MODE_NONE
随机播放
是否允许再次播放 顺序播放
已播放过的内容顺序播放
是否允许再次播放 随机播放
已播放过的内容随机播放
和 顺序播放
都是播放队列不循环播放模式一般 顺序播放/随机播放/单曲循环/列表循环
是在一个按钮上做来回切换
但是getRepeatMode 和 getShuffleMode是两个模式, 简单做下同步
单曲循环 = controllerCompat.getRepeatMode() == PlaybackStateCompat.REPEAT_MODE_ONE
&& controllerCompat.getShuffleMode() == PlaybackStateCompat.SHUFFLE_MODE_NONE
if (单曲循环) {
controllerCompat.getTransportControls().setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
controllerCompat.getTransportControls().setRepeatMode(PlaybackStateCompat.REPEAT_MODE_ONE);
}
点击按钮循环模式切换和应用
shuffleIndex = (++shuffleIndex % shuffleConfigMapSize);
点击按钮切换的配置列表
private void defaultShuffleConfig(MediaControllerCompat controllerCompat) {
playerShuffleResource.add(new ShuffleConfig(1, R.drawable.icon_play_mode_shuffle));
playerShuffleResource.add(new ShuffleConfig(2, R.drawable.icon_play_mode_order));
playerShuffleResource.add(new ShuffleConfig(3, R.drawable.icon_play_cycle_one));
playerShuffleResource.add(new ShuffleConfig(4, R.drawable.icon_play_cycle_all));
}
- 如果指定的歌曲在队列的某个位置,从此位置后开始继续顺序或者随机播放
- 如果指定的歌曲不在列表中,先插入队列,在改变播放指针
- 客户端调用sendCustomAction通知到服务端 播放指定的 index
private void showPlayIndex(MediaControllerCompat mediaControllerCompat) {
Bundle bundle = getIntent().getExtras();
if (null != bundle) {
mediaControllerCompat.getTransportControls().sendCustomAction(AudioPlayerHelper.ACTION_INDEX, bundle);
}
}
- 如果播放时随机的话会导致重复播放问题,所以要整体随机
- 随机播放和顺序播放可以是
同一个完整的播放队列(好做同步控制,添加,删除等)
+两个播放指针 + 两个播放指针记录
+两个已播队列
3.待整理上传源码后分享
if (mShuffleList.size() >= 3) {
int[] shuffleListIndexes = randomList(0, mShuffleList.size() - 1, mShuffleList.size());
}
public static int[] randomList(int min, int max, int n) {
int len = max - min + 1;
if (max < min || n > len) {
return null;
}
int[] source = new int[len];
for (int i = min; i < min + len; i++) {
source[i - min] = i;
}
int[] result = new int[n];
Random rd = new Random();
int index = 0;
for (int i = 0; i < result.length; i++) {
index = Math.abs(rd.nextInt() % len--);
result[i] = source[index];
source[index] = source[len];
}
return result;
}
滑动时切换歌曲
PageTransformer
// viewPager的item data 为按播放列表顺序放入的 mediaId
// 每次切换时从媒体库查询最新信息进行加载
// 也可以放入封装的一个MediaItemData,包含更多信息,不再查询
int index = musicPagerAdapter.getItemPosition(mediaId);
override fun onTouch(v: View, event: MotionEvent): Boolean {
...
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
executeAction = false
lastTouchEventX = event.x
lastTouchEventY = event.y
}
MotionEvent.ACTION_MOVE -> {
if (executeAction) return true
val distanceX = event.x - lastTouchEventX
val distanceY = event.y - lastTouchEventY
if (abs(distanceX) < touchLimitDistance && distanceY > touchYDistance) {
executeAction = true
[email protected]()
return true
}
}
}
return false
}
})
public static void startAudioActivity(Context context) {
Intent intent = new Intent(context, AudioActivity.class);
context.startActivity(intent);
if (context instanceof Activity) {
((Activity) context).overridePendingTransition(R.anim.lib_audio_player_music_in, 0);
}
}
添加媒体库时放入的url: MediaMetadataCompat#METADATA_KEY_ALBUM_ART_URI
private void loadImage1(final String mediaId, final ImageView albumArt) {
String imageUrl = MusicLibrary.getAlbumImageUrl(mediaId);
Glide.with(context)
.load(imageUrl)
.transform(getTransform())
.placeholder(errorPlaceHolder)
.error(errorPlaceHolder)
.circleCrop()
.into(albumArt);
}
private void loadImage2(final String mediaId, final ImageView albumArt) {
Uri albumUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(albumUri, Long.parseLong(mediaId));
Glide.with(context)
.load(uri)
.transform(getTransform())
.placeholder(errorPlaceHolder)
.error(errorPlaceHolder)
.circleCrop()
.into(albumArt);
}
private void loadImage3(final String mediaId, final ImageView albumArt) {
//albumArt.setTag(mediaId);
String pathUrl = MusicLibrary.getAudioPathUrl(mediaId);
if (!TextUtils.isEmpty(pathUrl)) {
MusicImageTask.ImageTaskDone imageTaskDone = new MusicImageTask.ImageTaskDone() {
@Override
public void done(String pathUrl, byte[] bytes) {
Glide.with(context)
.load(bytes)
.transform(getTransform())
.placeholder(errorPlaceHolder)
.error(errorPlaceHolder)
.circleCrop()
.into(albumArt);
}
};
MusicImageTask musicImageTask = new MusicImageTask(pathUrl, imageTaskDone);
musicImageTask.execute(pathUrl);
taskDoneArrayList.add(musicImageTask);
}
}
MusicImageTask
...
private byte[] parserImage(String mediaUri) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(mediaUri);
return mediaMetadataRetriever.getEmbeddedPicture();
}
利用 gilde error时使用 RequestBuilder
@NonNull
public RequestBuilder error(@Nullable RequestBuilder errorBuilder) {
this.errorBuilder = errorBuilder;
return this;
}
Glide.with(context)
.load(imageUrl)
.transform(getTransform())
.placeholder(errorPlaceHolder)
.error(errorPlaceHolder)
.error(Glide.with(context)
.load(uri)
.transform(getTransform())
.placeholder(errorPlaceHolder)
.error(errorPlaceHolder)
.listener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
loadImage3(mediaId, albumArt);
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
return false;
}
}))
.circleCrop()
.into(albumArt);
待整理后上传到github
文章来自:http://blog.csdn.net/intbird 转载请说明出处
SVID
待整理后上传…
4. 媒体服务之 musicService
5. 播放展示之 musicActivity
6. 播放展示之 musicView
7. 播放展示之 musicProgressBar
8. 播放展示之 musicNotification (MediaStyle)