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" > < ImageViewandroid: layout_width= " @dimen/player_img_size" android: layout_height= " @dimen/player_img_size" android: src= " @mipmap/img_cover_default" android: scaleType= " centerCrop" /> < LinearLayoutandroid: layout_width= " match_parent" android: layout_height= " wrap_content" android: orientation= " vertical" android: layout_marginStart= " 10dp" > < LinearLayoutandroid: layout_width= " match_parent" android: layout_height= " wrap_content" android: layout_gravity= " center_vertical" android: orientation= " vertical" > < TextViewandroid: 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" /> < SeekBarandroid: 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> < RelativeLayoutandroid: layout_width= " match_parent" android: layout_height= " wrap_content" > < LinearLayoutandroid: layout_width= " wrap_content" android: layout_height= " wrap_content" android: layout_centerVertical= " true" android: layout_marginLeft= " 5dp" android: orientation= " horizontal" > < TextViewandroid: 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" /> < TextViewandroid: layout_width= " wrap_content" android: layout_height= " wrap_content" android: text= " /" android: textColor= " @color/white" android: textSize= " @dimen/text_size_12" /> < TextViewandroid: 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> < LinearLayoutandroid: layout_width= " wrap_content" android: layout_height= " wrap_content" android: layout_centerInParent= " true" android: gravity= " center" android: orientation= " horizontal" > < ImageButtonandroid: 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" /> < ImageButtonandroid: 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" /> < ImageButtonandroid: 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> < LinearLayoutandroid: layout_width= " wrap_content" android: layout_height= " wrap_content" android: layout_centerVertical= " true" android: layout_alignParentEnd= " true" > < ImageButtonandroid: 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" > < TextViewandroid: 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" /> < LinearLayoutandroid: layout_width= " 0dp" android: layout_height= " wrap_content" android: layout_weight= " 1" android: orientation= " vertical" > < TextViewandroid: 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" /> < TextViewandroid: 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> < ImageButtonandroid: 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" > < ListViewandroid: 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.VirgoSimplePlayerandroid: id= " @+id/simplePlayer" android: layout_width= " match_parent" android: layout_height= " wrap_content" /> </ LinearLayout>