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>