簡易播放器
本次實驗要求實現一個簡單的音樂播放器。
主要實現的功能有:可以實現多首音樂播放(此處音樂放置在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)); }