简易播放器
本次实验要求实现一个简单的音乐播放器。
主要实现的功能有:可以实现多首音乐播放(此处音乐放置在Raw下,并非是获取存储中的文件)、实现列表中上一首下一首播放、进度条可以拖动、播放时显示当前歌曲信息。
功能不难,基本上都是常规代码设计,搞清楚逻辑,写起来就完事儿了。再次特别感谢参考博客:小妖的博客
首先是布局:ListVIew、seekBar等,设置背景、以及顶部项目名,没有啥难度。
首先是先新建一个MusicFile类,将一首音乐的歌曲名、歌手名、专辑封面、总时长、歌曲分类封装起来。
private String musicName; private int imageID; private String singer; private String sorter; private String minutes; private int path;
然后为每个列表项布局music_item.xml
MusicAdapter.java
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class MusicAdapter extends ArrayAdapter<MusicFile> { private int resourceId; public MusicAdapter(Context context, int textViewResourceId, List<MusicFile> objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { //根据position获取待显示的Name实例 MusicFile music = getItem(position); //获取子视图控件实例 View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); ImageView imageID = (ImageView) view.findViewById(R.id.imageView); TextView musicName = (TextView) view.findViewById(R.id.music_name); TextView singer = (TextView) view.findViewById(R.id.singer); TextView sorter = (TextView) view.findViewById(R.id.sorter); TextView time = (TextView) view.findViewById(R.id.minutes); //将Name实例内的名字和头像赋值给子视图控件实例 imageID.setImageResource(R.drawable.music1); musicName.setText(music.getMusicName()); singer.setText(music.getSinger()); sorter.setText(music.getSorter()); time.setText(music.getMinutes()); return view; } }
MainActivity.java中全局变量列表:
private ListView musiclist; private TextView musiclistCurrentTime; private TextView musiclistTotalTime; private TextView infotextView1; private TextView infotextView2; private ImageView imageView; private ImageView musiclistPlay; private SeekBar seekBar; private MediaPlayer mediaPlayer; private Handler handler; private Thread thread = new Thread(); private MusicAdapter adapter; private ListView listView; private List<MusicFile> MusicList = new ArrayList<>(); private static final int IDLE = 0; //空闲:没有播放音乐 private static final int PAUSE = 1; //暂停:播放音乐时暂停 private static final int START = 2; //正在播放音乐 private boolean flag = false;//控制进度条的索引 private static final int CURR_TIME_VALUE = 1; private int currState = IDLE;//当前播放器的状态 private int currPosition;//list的当前选中项的索引值(第一项对应0)
MainActivity中,适配ListView:
private MusicAdapter adapter; private ListView listView; private List<MusicFile> MusicList = new ArrayList<>();
adapter = new MusicAdapter(MainActivity.this, R.layout.music_item, MusicList); listView = (ListView) findViewById(R.id.musiclist); listView.setAdapter(adapter);
初始化数据:
//初始化列表数据 private void initInfo() { MusicFile nw = new MusicFile("新世界", R.drawable.nwimage, "华晨宇", "5:51", "心愿歌单", R.raw.newworld); MusicList.add(nw); MusicFile mh = new MusicFile("疯人院", R.drawable.mhimage, "华晨宇", "6:37", "华语精选", R.raw.madhouse); MusicList.add(mh); MusicFile nc = new MusicFile("Nobody Can Save Me", R.drawable.ncimage, "Linkin Park", "3:45", "摇滚精选", R.raw.ns); MusicList.add(nc); MusicFile ss = new MusicFile("神树", R.drawable.ssimage, "华晨宇", "5:19", "Love Songs", R.raw.shenshu); MusicList.add(ss); MusicFile co = new MusicFile("After the After party", R.drawable.aaimage, "Charli XCX", "3:39", "Love Songs", R.raw.aa); MusicList.add(co); MusicFile aa = new MusicFile("Children of A Miracle", R.drawable.coimage, "Don Diablo", "3:09", "欧美精选", R.raw.co); MusicList.add(aa); MusicFile sp = new MusicFile("杀破狼", R.drawable.spimage, "JS", "4:40", "心愿歌单", R.raw.sp); MusicList.add(sp); MusicFile yl = new MusicFile("月球", R.drawable.yqimage, "银临", "4:39", "华语精选", R.raw.yl); MusicList.add(yl); MusicFile nc1 = new MusicFile("Nobody Can Save Me", R.drawable.ncimage, "Linkin Park", "3:45", "摇滚精选", R.raw.ns); MusicList.add(nc1); MusicFile aa1 = new MusicFile("Children of A Miracle", R.drawable.coimage, "Don Diablo", "3:09", "欧美精选", R.raw.co); MusicList.add(aa); MusicFile nw2 = new MusicFile("新世界", R.drawable.nwimage, "华晨宇", "5:51", "心愿歌单", R.raw.newworld); MusicList.add(nw2); MusicFile co1 = new MusicFile("After the After party", R.drawable.aaimage, "Charli XCX", "3:39", "Love Songs", R.raw.aa); MusicList.add(co1); }
到现在为止,UI界面已经初始化好了,接下来就是处理点击列表项,播放音乐。
//列表点击事件 @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MusicFile currMf = MusicList.get(position); infotextView1.setText(currMf.getMusicName()); infotextView2.setText(currMf.getSinger()); //获取专辑图片 imageView.setImageResource(currMf.getImageID()); //点击列表项,ImageView的初始状态都为play musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause)); if (currState == IDLE) { //记录当前列表项的下标 currPosition = position; play(position); //在播放中的音乐,重置掉 } else { mediaPlayer.reset(); //记录下标 currPosition = position; play(position); } }
//播放 private void play(int position) { //根据当前列表项的下标,获取MusicList中对应的歌曲以及路径 MusicFile currMf = MusicList.get(position); int currPath = currMf.getPath(); //初始化mediaPlayer mediaPlayer = MediaPlayer.create(this, currPath); mediaPlayer.start(); //初始化seekBar initSeekBar(); //设置当前状态为START currState = START; //开启新线程 new Thread(new Runnable() { @Override public void run() { flag = true; while (flag) { //实时更新进度条 if (mediaPlayer.getCurrentPosition() < seekBar.getMax()) { seekBar.setProgress(mediaPlayer.getCurrentPosition()); Message msg = handler.obtainMessage(CURR_TIME_VALUE, toTime(mediaPlayer.getCurrentPosition())); handler.sendMessage(msg); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } else { flag = false; } } } }).start(); }
handler初始化:
handler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what) { case CURR_TIME_VALUE: musiclistCurrentTime.setText(msg.obj.toString()); } } };
其中initSeekBar()
//初始化进度条 private void initSeekBar() { //获取总时长 int duration = mediaPlayer.getDuration(); seekBar.setMax(duration); seekBar.setProgress(0); if (duration > 0) { musiclistTotalTime.setText(toTime(duration)); } }
对要显示的时间进行格式化处理,显示“xx:xx”:totime()
//格式化时间 private String toTime(int duration) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("mm:ss", Locale.getDefault()); sdf.setTimeZone(TimeZone.getTimeZone("GMT+0")); date.setTime(duration); return sdf.format(date); }
基本点击播放效果也已经完成,接下来就是下方按钮(此处用的是ImageView进行布局,没有本质差别)所产生的对应效果。
onPlayClick()
//播放按钮(前提是已经点击列表项) public void onPlayClick(View v) { switch (currState) { case PAUSE: mediaPlayer.start(); musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause)); currState = START; break; case START: mediaPlayer.pause(); musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.play)); currState = PAUSE; break; } }
点击下一首和上一首,原理基本相同,稍微有一点差别就是对于两端音乐上下首的处理。
上一首:onPreviousClick()
//上一首 public void onPreviousClick(View v) { previous(); } private void previous() { if (currPosition > 0) { switch (currState) { case IDLE: //滚动列表到下一项 musiclist.smoothScrollToPosition(currPosition - 1); //模拟点击Item时间,触发监听器 musiclist.performItemClick( musiclist.getAdapter().getView(currPosition - 1, null, null), currPosition - 1, musiclist.getItemIdAtPosition(currPosition - 1)); break; case START: case PAUSE: stop(); musiclist.smoothScrollToPosition(currPosition - 1); musiclist.performItemClick( musiclist.getAdapter().getView(currPosition - 1, null, null), currPosition - 1, musiclist.getItemIdAtPosition(currPosition - 1)); break; } //如果currPosition等于0,也就是列表第一个 } else { switch (currState) { case IDLE: //则设置滚动到列表尾,也就是最后一个musiclist.getCount() - 1 musiclist.smoothScrollToPosition(musiclist.getCount() - 1); musiclist.performItemClick( musiclist.getAdapter().getView(musiclist.getCount() - 1, null, null), musiclist.getCount() - 1, musiclist.getItemIdAtPosition(musiclist.getCount() - 1)); break; case START: case PAUSE: stop(); musiclist.smoothScrollToPosition(musiclist.getCount() - 1); musiclist.performItemClick( musiclist.getAdapter().getView(musiclist.getCount() - 1, null, null), musiclist.getCount() - 1, musiclist.getItemIdAtPosition(musiclist.getCount() - 1)); break; } } }
下一首onNextClick():
//下一首 public void onNextClick(View v) { next(); } private void next() { if (currPosition != musiclist.getCount()-1) { switch (currState) { case IDLE: musiclist.smoothScrollToPosition(currPosition + 1); musiclist.performItemClick( musiclist.getAdapter().getView(currPosition + 1, null, null), currPosition + 1, musiclist.getItemIdAtPosition(currPosition + 1)); break; case START: case PAUSE: stop(); musiclist.smoothScrollToPosition(currPosition + 1); musiclist.performItemClick( musiclist.getAdapter().getView(currPosition + 1, null, null), currPosition + 1, musiclist.getItemIdAtPosition(currPosition + 1)); break; } //如果currPosition等于musiclist.getCount() - 1,也就是列表最后一个 } else { switch (currState) { case IDLE: //那么,设置滚动到列表首,也就是第一首下标为0 musiclist.smoothScrollToPosition(0); musiclist.performItemClick( musiclist.getAdapter().getView(0, null, null), 0, musiclist.getItemIdAtPosition(0)); break; case START: case PAUSE: stop(); musiclist.smoothScrollToPosition(0); musiclist.performItemClick( musiclist.getAdapter().getView(0, null, null), 0, musiclist.getItemIdAtPosition(0)); break; } } }
performItemClick源码
public boolean performItemClick(View view, int position, long id) { if (mOnItemClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnItemClickListener.onItemClick(this, view, position, id); if (view != null) { view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } return true; } return false; }
点击上一首或者下一首时,如果有其他在播放,终止其mediaPlayer,stop():
//终止当前播放 private void stop() { initState(); mediaPlayer.stop(); currState = IDLE; }
initState():
//更新界面 private void initState() { musiclistCurrentTime.setText("00:00"); musiclistTotalTime.setText("00:00"); flag = false; seekBar.setProgress(0); musiclistPlay.setImageDrawable(getResources().getDrawable(R.drawable.pause)); }