Android – 简易音乐播放器
播放器功能:
* 1. 播放模式:单曲、列表循环、列表随机;
* 2. 后台播放(单例模式);
* 3. 多位置同步状态回调;
处理模块:
* 1. 提取文件信息:音频文件(.mp3) -> 对象类(AudioBean);
* 2. 后台播放管理:VMPlayer(实现对音频的播放相关处理);
* 3. UI显示及控制:歌曲列表 + 播放控制器;
效果:
模块一:处理音频文件(后台服务内)
private void flashAudioRes(boolean autoPlay) {
Log.d(TAG, "同步音频中...");
new Thread(() -> {
try {
List<AudioBean> audioItems = synLocalMusic2(FileUtils.getAudioDir());
if (audioItems != null && !audioItems.isEmpty()) {
Collections.sort(audioItems, (o1, o2) -> o1.getDisplayName().compareTo(o2.getDisplayName()));
VMPlayer.getInstance().setPlayList(audioItems);
if(autoPlay){
Thread.sleep(1000);
VMPlayer.getInstance().resetIndex();
VMPlayer.getInstance().play();
VMPlayer.getInstance().notifyListChanged();
}
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private List<AudioBean> synLocalMusic2(String dir) {
File root = new File(dir);
if (root.exists()) {
File[] files = root.listFiles();
if (files == null || files.length < 1) {
return null;
}
List<AudioBean> list = new ArrayList<>();
MediaPlayer mediaPlayer = new MediaPlayer();
int duration = 0;
for (File f : files) {
if (f.isFile() && f.getName().endsWith(".mp3")) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(f.getPath());
mediaPlayer.prepare();
duration = mediaPlayer.getDuration();
} catch (IOException var5) {
var5.printStackTrace();
}
Log.v(TAG, "synLocalMusic: " + f.getName() + " - " + duration);
AudioBean bean = getAudioFileInfo(f.getPath(), f.length(), duration);
list.add(bean);
}
}
if (mediaPlayer != null) {
mediaPlayer.reset();
mediaPlayer.release();
}
return list;
}
return null;
}
private AudioBean getAudioFileInfo(String path, long size, int duration) {
AudioBean songsInfo = new AudioBean();
String displayName = path.substring(path.lastIndexOf("/") + 1);
String album = displayName.substring(0, displayName.lastIndexOf("."));
String name;
String artist;
if (album.contains("-")) {
artist = album.substring(0, album.lastIndexOf("-")).trim();
name = album.substring(album.lastIndexOf("-") + 1).trim();
} else {
artist = name = album;
}
songsInfo.setName(name);
songsInfo.setDisplayName(displayName);
songsInfo.setArtist(artist);
songsInfo.setDuration(duration);
songsInfo.setSize(size);
songsInfo.setPath(path);
return songsInfo;
}
public class AudioBean implements Serializable {
private String name;
private String displayName;
private String artist;
private String path;
private int duration;
private long size;
public AudioBean() {
}
public AudioBean(String path) {
this.path = path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
@Override
public String toString() {
return "AudioBean{" +
"name='" + name + '\'' +
", displayName='" + displayName + '\'' +
", artist='" + artist + '\'' +
", path='" + path + '\'' +
", duration=" + duration +
", size=" + size +
'}';
}
}
模块二:播放管理器
VMPlayer.java (主要类)
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import com.nepalese.harinetest.config.ShareDao;
import com.nepalese.harinetest.utils.MathUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class VMPlayer implements MediaPlayer.OnCompletionListener, VirgoPlayerCallback {
private static final String TAG = "VMPlayer";
private static final long INTERVAL_GET_PROGRESS = 500;
public static final int STATE_ERROR = -1;
public static final int STATE_INITIAL = 0;
public static final int STATE_PREPARED = 1;
public static final int STATE_PLAYING = 2;
public static final int STATE_PAUSE = 3;
public static final int MODE_SINGLE = 0;
public static final int MODE_LOOP = 1;
public static final int MODE_RANDOM = 2;
@SuppressLint("StaticFieldLeak")
private static volatile VMPlayer instance;
private Context context;
private MediaPlayer mediaPlayer;
private List<AudioBean> beanList;
private List<iPlayBack> iPlayBacks;
private AudioBean curBean;
private int curState;
private int curIndex;
private int curMode;
private int errTime;
private int aimSeek;
public static VMPlayer getInstance() {
if (instance == null) {
synchronized (VMPlayer.class) {
if (instance == null) {
instance = new VMPlayer();
}
}
}
return instance;
}
private VMPlayer() {
beanList = new ArrayList<>();
iPlayBacks = new ArrayList<>(5);
mediaPlayer = new MediaPlayer();
mediaPlayer.setLooping(false);
mediaPlayer.setOnCompletionListener(this);
}
public void init(Context context) {
this.context = context;
curState = STATE_INITIAL;
curMode = ShareDao.getAudioMode(context);
curIndex = ShareDao.getAudioIndex(context);
errTime = 0;
aimSeek = 0;
Log.d(TAG, "init: " + curIndex);
}
private boolean isValid() {
return curState >= STATE_PREPARED && !beanList.isEmpty();
}
public List<AudioBean> getBeanList() {
return beanList;
}
public void resetIndex() {
this.curIndex = 0;
}
@Override
public void onCompletion(MediaPlayer mp) {
if (curMode == MODE_SINGLE) {
mediaPlayer.seekTo(0);
mediaPlayer.start();
} else {
notifyComplete();
}
}
public void playOrPause() {
if (curState == STATE_PLAYING) {
pause();
} else {
play();
}
}
@Override
public void play() {
if (isValid()) {
if (curState == STATE_PAUSE) {
curState = STATE_PLAYING;
mediaPlayer.start();
notifyStateChanged(true);
} else if (curState == STATE_PREPARED) {
prepareAndPlay();
}
} else {
Log.d(TAG, "play: " + curState + " - size: " + beanList.size());
notifyError("未设置播放列表!");
}
}
private void prepareAndPlay() {
curState = STATE_PREPARED;
if (curIndex < 0 || curIndex >= beanList.size()) {
curIndex = 0;
}
ShareDao.setAudioIndex(context, curIndex);
Log.d(TAG, "播放: " + curIndex);
playResource(beanList.get(curIndex));
}
private void playResource(AudioBean bean) {
if (bean == null || TextUtils.isEmpty(bean.getPath())) {
++errTime;
notifyStateChanged(false);
if (errTime >= beanList.size()) {
curState = STATE_ERROR;
notifyError("播放列表异常!");
} else {
curState = STATE_PREPARED;
playNext();
}
return;
}
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(bean.getPath());
mediaPlayer.setOnPreparedListener(mp -> {
notifySongChanged(bean);
notifyStateChanged(true);
curState = STATE_PLAYING;
mediaPlayer.seekTo(aimSeek);
mediaPlayer.start();
errTime = 0;
aimSeek = 0;
curBean = bean;
});
mediaPlayer.prepareAsync();
startTask();
} catch (IOException e) {
++errTime;
if (errTime >= beanList.size()) {
curState = STATE_ERROR;
} else {
if (beanList.size() > 0) {
curState = STATE_PREPARED;
} else {
curState = STATE_INITIAL;
}
}
notifyStateChanged(false);
notifyError("播放器出错!" + e.getMessage());
}
}
@Override
public void play(int index) {
if (isValid()) {
curIndex = index;
prepareAndPlay();
} else {
notifyError("未设置播放列表!");
}
}
@Override
public void play(AudioBean bean) {
if (bean == null) {
notifyError("指定歌曲为空!");
return;
}
curState = STATE_PREPARED;
playResource(bean);
}
@Override
public void play(List<AudioBean> list, int index) {
if (list == null || list.isEmpty()) {
notifyError("新列表为空!");
return;
}
curIndex = index;
setPlayList(list);
prepareAndPlay();
}
@Override
public void playLast() {
if (isValid()) {
switch (curMode) {
case MODE_SINGLE:
break;
case MODE_LOOP:
if (curIndex > 0) {
--curIndex;
} else {
curIndex = beanList.size() - 1;
}
prepareAndPlay();
break;
case MODE_RANDOM:
curIndex = MathUtil.getRandom(0, beanList.size(), curIndex);
prepareAndPlay();
break;
}
} else {
notifyError("未设置播放列表!");
}
}
@Override
public void playNext() {
if (isValid()) {
switch (curMode) {
case MODE_SINGLE:
break;
case MODE_LOOP:
++curIndex;
prepareAndPlay();
break;
case MODE_RANDOM:
curIndex = MathUtil.getRandom(0, beanList.size(), curIndex);
prepareAndPlay();
break;
}
} else {
notifyError("未设置播放列表!");
}
}
@Override
public void pause() {
if (isPlaying()) {
curState = STATE_PAUSE;
mediaPlayer.pause();
notifyStateChanged(false);
}
}
@Override
public void seekTo(int progress) {
if (isValid()) {
if (curState > STATE_PREPARED) {
aimSeek = 0;
mediaPlayer.seekTo(progress);
} else {
aimSeek = progress;
}
}
}
@Override
public void setPlayList(List<AudioBean> beans) {
if (beans == null || beans.isEmpty()) {
notifyError("新列表为空!");
return;
}
Log.d(TAG, "setPlayList: " + beans.size());
curState = STATE_PREPARED;
beanList.clear();
beanList.addAll(beans);
curBean = beanList.get(curIndex);
}
@Override
public void setPlayMode(int mode) {
if (mode == this.curMode) {
return;
}
this.curMode = mode;
ShareDao.setAudioMode(context, mode);
Log.d(TAG, "setPlayMode: " + curMode);
}
@Override
public boolean isPlaying() {
return isValid() && mediaPlayer.isPlaying();
}
@Override
public int getCurProgress() {
return mediaPlayer.getCurrentPosition();
}
@Override
public int getCurState() {
return curState;
}
@Override
public int getCurMode() {
return curMode;
}
@Override
public AudioBean getCurMusic() {
if (isValid()) {
return curBean;
}
return null;
}
@Override
public void releasePlayer() {
stopTask();
if (iPlayBacks != null) {
iPlayBacks.clear();
iPlayBacks = null;
}
if (beanList != null) {
beanList.clear();
beanList = null;
}
try {
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
} catch (Exception e) {
} finally {
if (mediaPlayer != null) {
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
}
instance = null;
curState = STATE_INITIAL;
}
@Override
public void registerCallback(iPlayBack callback) {
iPlayBacks.add(callback);
}
@Override
public void unregisterCallback(iPlayBack callback) {
iPlayBacks.remove(callback);
}
@Override
public void removeCallbacks() {
iPlayBacks.clear();
}
public void notifyListChanged() {
if (iPlayBacks != null) {
for (iPlayBack callBack : iPlayBacks) {
callBack.onListChange();
}
}
}
private void notifySongChanged(AudioBean bean) {
if (iPlayBacks != null) {
for (iPlayBack callBack : iPlayBacks) {
callBack.onChangeSong(bean);
}
}
}
private void notifyStateChanged(boolean isPlaying) {
if (iPlayBacks != null) {
for (iPlayBack callback : iPlayBacks) {
callback.onPlayStateChanged(isPlaying);
}
}
}
private void notifyComplete() {
if (iPlayBacks != null) {
for (iPlayBack callback : iPlayBacks) {
callback.onPlayCompleted();
}
}
}
private void notifyProcessChanged(int process) {
if (iPlayBacks != null) {
for (iPlayBack callback : iPlayBacks) {
callback.onProcessChanged(process);
}
}
}
private void notifyError(String msg) {
if (iPlayBacks != null) {
for (iPlayBack callback : iPlayBacks) {
callback.onPlayError(curState, msg);
}
}
}
private final Handler handler = new Handler(msg -> false);
private final Runnable getProcessTask = new Runnable() {
@Override
public void run() {
handler.postDelayed(getProcessTask, INTERVAL_GET_PROGRESS);
try {
if (isPlaying()) {
notifyProcessChanged(getCurProgress());
}
} catch (Throwable ignored) {
}
}
};
private void startTask() {
stopTask();
handler.post(getProcessTask);
}
private void stopTask() {
handler.removeCallbacks(getProcessTask);
}
}
VirgoPlayerCallback.java (功能接口)
public interface VirgoPlayerCallback {
void play();
void play(int index);
void play(AudioBean bean);
void play(List<AudioBean> beanList, int index);
void playLast();
void playNext();
void pause();
void seekTo(int progress);
void setPlayList(List<AudioBean> beans);
void setPlayMode(int mode);
boolean isPlaying();
int getCurProgress();
int getCurState();
int getCurMode();
AudioBean getCurMusic();
void releasePlayer();
void registerCallback(iPlayBack callback);
void unregisterCallback(iPlayBack callback);
void removeCallbacks();
}
iPlayBack.java(播放状态回调接口)
public interface iPlayBack {
void onListChange();
void onChangeSong(@NonNull AudioBean bean);
void onPlayCompleted();
void onPlayStateChanged(boolean isPlaying);
void onProcessChanged(int process);
void onPlayError(int state, String error);
}
模块三:播放控件+歌曲列表
VirgoSimplePlayer.java (简单音乐播放器控件)
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.nepalese.harinetest.R;
import com.nepalese.harinetest.utils.ConvertUtil;
public class VirgoSimplePlayer extends RelativeLayout {
private static final String TAG = "VirgoSimplePlayer";
private SeekBar musicSeekbar;
private TextView musicName, musicCur, musicAll;
private ImageButton musicLast, musicPlay, musicNext, musicMode;
private VMPlayer vmPlayer;
public VirgoSimplePlayer(Context context) {
this(context, null);
}
public VirgoSimplePlayer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VirgoSimplePlayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.layout_simple_virgo_player, this, true);
init();
}
private void init() {
initUI();
initData();
setListener();
}
private void initUI() {
musicSeekbar = findViewById(R.id.music_seekbar);
musicName = findViewById(R.id.music_tv_name);
musicCur = findViewById(R.id.music_cur);
musicAll = findViewById(R.id.music_all);
musicLast = findViewById(R.id.music_btn_last);
musicPlay = findViewById(R.id.music_btn_paly);
musicNext = findViewById(R.id.music_btn_next);
musicMode = findViewById(R.id.music_btn_mode);
musicName.setSelected(true);
}
private void initData() {
vmPlayer = VMPlayer.getInstance();
}
private void setListener() {
musicLast.setOnClickListener(v -> vmPlayer.playLast());
musicNext.setOnClickListener(v -> vmPlayer.playNext());
musicPlay.setOnClickListener(v -> vmPlayer.playOrPause());
musicMode.setOnClickListener(v -> changPlayMode());
musicSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
vmPlayer.seekTo(seekBar.getProgress());
}
});
}
private void changPlayMode() {
int curMode = vmPlayer.getCurMode();
curMode++;
if (curMode > VMPlayer.MODE_RANDOM) {
curMode = VMPlayer.MODE_SINGLE;
}
vmPlayer.setPlayMode(curMode);
updateModeImg(curMode);
}
private void updateModeImg(int curMode) {
switch (curMode) {
case VMPlayer.MODE_SINGLE:
musicMode.setImageResource(R.mipmap.icon_single);
break;
case VMPlayer.MODE_LOOP:
musicMode.setImageResource(R.mipmap.icon_order);
break;
case VMPlayer.MODE_RANDOM:
musicMode.setImageResource(R.mipmap.icon_random);
break;
}
}
public void notifyStateChanged(boolean isPlaying) {
if (isPlaying) {
musicPlay.setImageResource(R.mipmap.icon_playing);
} else {
musicPlay.setImageResource(R.mipmap.icon_pause);
}
}
public void notifyProcessChanged(int process) {
musicSeekbar.setProgress(process);
musicCur.setText(ConvertUtil.formatTime(process));
}
public void notifySongChanged(String name, int duration) {
musicName.setText(name);
musicSeekbar.setMax(duration);
musicAll.setText(ConvertUtil.formatTime(duration));
}
public void synInfo() {
if (vmPlayer.isPlaying()) {
AudioBean bean = vmPlayer.getCurMusic();
notifySongChanged(bean.getName() + " - " + bean.getArtist(), bean.getDuration());
notifyStateChanged(true);
}else{
vmPlayer.play();
}
updateModeImg(vmPlayer.getCurMode());
}
}
layout_simple_virgo_player.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="15dp"
android:orientation="horizontal"
android:background="@drawable/bg_card_red">
<ImageView
android:layout_width="@dimen/player_img_size"
android:layout_height="@dimen/player_img_size"
android:src="@mipmap/img_cover_default"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/music_tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:text="歌名"
android:textSize="@dimen/text_size_14"
android:textColor="@color/black"
android:paddingStart="15dp"/>
<SeekBar
android:id="@+id/music_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progressTint="@color/black"
android:thumbTint="@color/color_QYH"
android:layout_marginTop="3dp"
android:progress="0"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/music_cur"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
android:textColor="@color/white"
android:textSize="@dimen/text_size_12"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/"
android:textColor="@color/white"
android:textSize="@dimen/text_size_12"/>
<TextView
android:id="@+id/music_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
android:textColor="@color/white"
android:textSize="@dimen/text_size_12"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="horizontal">
<ImageButton
android:id="@+id/music_btn_last"
android:layout_width="@dimen/player_icon_size_small"
android:layout_height="@dimen/player_icon_size_small"
android:layout_margin="@dimen/player_icon_margin"
android:background="@drawable/img_button_transprant"
android:src="@mipmap/icon_last"
android:scaleType="centerCrop"/>
<ImageButton
android:id="@+id/music_btn_paly"
android:layout_width="@dimen/player_icon_size_big"
android:layout_height="@dimen/player_icon_size_big"
android:background="@drawable/img_button_transprant"
android:src="@mipmap/icon_pause"
android:scaleType="centerCrop"/>
<ImageButton
android:id="@+id/music_btn_next"
android:layout_width="@dimen/player_icon_size_small"
android:layout_height="@dimen/player_icon_size_small"
android:layout_margin="@dimen/player_icon_margin"
android:background="@drawable/img_button_transprant"
android:src="@mipmap/icon_next"
android:scaleType="centerCrop"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true">
<ImageButton
android:id="@+id/music_btn_mode"
android:layout_width="@dimen/player_icon_size_small"
android:layout_height="@dimen/player_icon_size_small"
android:layout_margin="@dimen/player_icon_margin"
android:background="@drawable/img_button_transprant"
android:src="@mipmap/icon_order"
android:scaleType="centerCrop"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
<dimen name="player_icon_size_big">42dp</dimen>
<dimen name="player_icon_size_small">30dp</dimen>
<dimen name="player_icon_margin">10dp</dimen>
<dimen name="player_layout_padding">10dp</dimen>
<dimen name="player_img_size">85dp</dimen>
<dimen name="text_size_10">10sp</dimen>
<dimen name="text_size_12">12sp</dimen>
<dimen name="text_size_14">14sp</dimen>
<dimen name="text_size_16">16sp</dimen>
<dimen name="text_size_18">18sp</dimen>
<dimen name="text_size_20">20sp</dimen>
<dimen name="text_size_22">22sp</dimen>
<dimen name="text_size_24">24sp</dimen>
<dimen name="text_size_32">32sp</dimen>
<dimen name="text_size_50">50sp</dimen>
<dimen name="padding_1">1dp</dimen>
<dimen name="padding_2">2dp</dimen>
<dimen name="padding_3">3dp</dimen>
<dimen name="padding_5">5dp</dimen>
<dimen name="padding_10">10dp</dimen>
<dimen name="padding_15">15dp</dimen>
<dimen name="margin_1">1dp</dimen>
<dimen name="margin_3">3dp</dimen>
<dimen name="margin_5">5dp</dimen>
<dimen name="margin_10">10dp</dimen>
<dimen name="margin_15">15dp</dimen>
ListView_LocalSong_Adapter.java(自定义列表适配器)
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.nepalese.harinetest.R;
import java.util.List;
public class ListView_LocalSong_Adapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
private List<AudioBean> data;
private interListenerSongList listener;
public ListView_LocalSong_Adapter(Context context, interListenerSongList listener, List<AudioBean> list) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.listener = listener;
this.data = list;
}
@Override
public int getCount() {
return data == null ? 0 : data.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
static class Holder {
private TextView tvOrder, tvName, tvArtist;
private LinearLayout root;
private ImageButton ibList;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
if (convertView == null) {
holder = new Holder();
convertView = inflater.inflate(R.layout.layout_song_list_local, null);
holder.root = convertView.findViewById(R.id.layout_list_root);
holder.tvOrder = convertView.findViewById(R.id.tv_order);
holder.tvName = convertView.findViewById(R.id.tvLocalName);
holder.tvArtist = convertView.findViewById(R.id.tvLocalArtist);
holder.ibList = convertView.findViewById(R.id.ibLocalSongList);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.tvOrder.setText(String.valueOf(position + 1));
holder.tvName.setText(data.get(position).getName());
holder.tvArtist.setText(data.get(position).getArtist());
if (position % 2 == 0) {
holder.root.setBackgroundColor(Color.parseColor("#4D03A9F4"));
}else{
holder.root.setBackgroundColor(Color.TRANSPARENT);
}
if (VMPlayer.getInstance().getCurState() >= VMPlayer.STATE_PREPARED && VMPlayer.getInstance().getCurMusic().getDisplayName().equals(data.get(position).getDisplayName())) {
holder.tvName.setTextColor(Color.RED);
} else {
holder.tvName.setTextColor(Color.BLACK);
}
holder.ibList.setTag(position);
return convertView;
}
public interface interListenerSongList {
void onItemClick(View view);
}
}
layout_song_list_local.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_list_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_5"
android:gravity="center_vertical"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_order"
android:layout_margin="@dimen/margin_5"
android:layout_width="35dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="@dimen/text_size_18"
android:textColor="@color/black" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvLocalName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginBottom="@dimen/margin_3"
android:textColor="@color/black"
android:textSize="@dimen/text_size_18"/>
<TextView
android:id="@+id/tvLocalArtist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_14"/>
</LinearLayout>
<ImageButton
android:id="@+id/ibLocalSongList"
android:layout_width="@dimen/icon_30"
android:layout_height="@dimen/icon_30"
android:src="@mipmap/icon_list_gray"
android:scaleType="fitCenter"
android:padding="@dimen/padding_2"
android:focusable="false"
android:background="@drawable/selector_button_transparent"/>
</LinearLayout>
前端使用
public class AudioPlayActivity extends AppCompatActivity implements ListView_LocalSong_Adapter.interListenerSongList, iPlayBack {
private static final String TAG = "AudioPlayActivity";
private Context context;
private VirgoSimplePlayer simplePlayer;
private ListView listView;
private ListView_LocalSong_Adapter adapter;
private final List<AudioBean> audioList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_play);
context = getApplicationContext();
init();
}
private void init() {
initUI();
initData();
setListener();
}
private void initUI() {
simplePlayer = findViewById(R.id.simplePlayer);
listView = findViewById(R.id.listviewAudio);
}
private void initData() {
VMPlayer.getInstance().registerCallback(this);
simplePlayer.synInfo();
audioList.addAll(VMPlayer.getInstance().getBeanList());
adapter = new ListView_LocalSong_Adapter(context, this, audioList);
listView.setAdapter(adapter);
}
private void setListener() {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
VMPlayer.getInstance().play(position);
}
});
}
@Override
protected void onDestroy() {
release();
super.onDestroy();
}
private void release() {
VMPlayer.getInstance().unregisterCallback(this);
}
@Override
public void onItemClick(View view) {
}
@Override
public void onListChange() {
audioList.clear();
audioList.addAll(VMPlayer.getInstance().getBeanList());
updateListView();
}
@Override
public void onChangeSong(@NonNull AudioBean bean) {
if (simplePlayer != null) {
simplePlayer.notifySongChanged(bean.getName() + " - " + bean.getArtist(), bean.getDuration());
}
updateListView();
}
@Override
public void onPlayCompleted() {
VMPlayer.getInstance().playNext();
}
@Override
public void onPlayStateChanged(boolean isPlaying) {
if (simplePlayer != null) {
simplePlayer.notifyStateChanged(isPlaying);
}
}
@Override
public void onProcessChanged(int process) {
if (simplePlayer != null) {
simplePlayer.notifyProcessChanged(process);
}
}
@Override
public void onPlayError(int state, String error) {
Log.d(TAG, "onPlayError: " + error);
}
private final int MSG_UPDATE_LIST = 1;
private void updateListView(){
handler.sendEmptyMessage(MSG_UPDATE_LIST);
}
private final Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if(msg.what == MSG_UPDATE_LIST){
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
return false;
}
});
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".AudioPlayActivity">
<ListView
android:id="@+id/listviewAudio"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@color/white"
android:dividerHeight="0dp" />
<com.nepalese.harinetest.musicplayer.VirgoSimplePlayer
android:id="@+id/simplePlayer"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>