Android?Studio實(shí)現(xiàn)音樂(lè)播放器2.0的全過(guò)程
一、引言
我在一年前發(fā)過(guò)一篇用Android實(shí)現(xiàn)音樂(lè)播放器的教程:Android Studio如何實(shí)現(xiàn)音樂(lè)播放器(簡(jiǎn)單易上手)。當(dāng)時(shí)實(shí)現(xiàn)的功能也很簡(jiǎn)單,就是播放音樂(lè)、暫停音樂(lè)、繼續(xù)播放、退出播放、顯示音樂(lè)列表和專輯封面的功能。如下圖所示:

期間有很多同學(xué)問(wèn)到我是否可以加上一首下一首功能,確實(shí)可以加,只要獲取到歌曲文件的下標(biāo)position即可實(shí)現(xiàn)此功能,不難。但是因?yàn)閷?xiě)完這個(gè)最初版本后就沒(méi)有當(dāng)時(shí)那種心境再改了,我想大家都會(huì)有這種體會(huì)。
雖然前段時(shí)間很忙,還是沉下心來(lái)回顧了自己的音樂(lè)播放器項(xiàng)目,也發(fā)現(xiàn)了很多不足的地方,然后進(jìn)行了優(yōu)化升級(jí),主要有三點(diǎn):
增加了上一首下一首功能更換了按鈕樣式,使用更個(gè)性化的按鈕增加了大多數(shù)代碼的注釋,做到臨缺勿濫
所以,這篇博客就是對(duì)最初版本(1.0版本)的一個(gè)完善,即2.0版本,話不多說(shuō),下面開(kāi)始。
二、項(xiàng)目概述
1、需求分析
綜合運(yùn)用UI界面設(shè)計(jì)、數(shù)據(jù)存儲(chǔ)、Activity(活動(dòng))、Service(服務(wù))、MusicPlayer(音樂(lè)播放類(lèi))、ListView(列表)等知識(shí),設(shè)計(jì)開(kāi)發(fā)一款具有音樂(lè)列表的音樂(lè)播放器。

2、設(shè)計(jì)分析
整個(gè)項(xiàng)目包含五個(gè)java文件和五個(gè)layout文件,因?yàn)槭潜容^簡(jiǎn)單的項(xiàng)目,所以沒(méi)有用工程結(jié)構(gòu)去實(shí)現(xiàn)它,這里介紹下它們之間的關(guān)系。

3、資源文件分析
本項(xiàng)目的所有音樂(lè)文件都是存放在本地的,沒(méi)有用服務(wù)器,當(dāng)然也可以用,參考我的多媒體播放器:Android Studio實(shí)現(xiàn)多媒體播放器。
我在res文件夾下創(chuàng)建了raw文件夾,用來(lái)放音樂(lè)文件,音樂(lè)文件的命名注意是從music0開(kāi)始,很多同學(xué)從music1開(kāi)始命名導(dǎo)致獲取不到第一首音樂(lè)然后閃退。
在drawable文件夾存放了音樂(lè)專輯圖片bg.jpg、播放器背景圖片music_bg.jpg,歌手圓形圖片music0.png、music1.png等,還有按鈕圖片play.png、pause.png等。

三、開(kāi)發(fā)環(huán)境

四、優(yōu)化設(shè)計(jì)
1、上一首下一首
想實(shí)現(xiàn)跳轉(zhuǎn)到上一首和下一首歌曲的播放界面,肯定修改的是MusicActivity。
1.1、可以這樣考慮:如果每次都獲取對(duì)應(yīng)歌曲的intent(意圖)進(jìn)行跳轉(zhuǎn),這樣實(shí)現(xiàn)起來(lái)就比較復(fù)雜了??梢钥吹給nCreate方法里面intent1就是獲取到的歌曲列表界面跳轉(zhuǎn)到音樂(lè)播放界面的意圖。我們換個(gè)巧妙的方法,利用這個(gè)intent1來(lái)實(shí)現(xiàn)。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//綁定布局文件
setContentView(R.layout.activity_music);
//獲得意圖
intent1=getIntent();
//初始化
init();
}
在frag1的列表點(diǎn)擊事件里面,intent用鍵值對(duì)存儲(chǔ)了歌曲名name[position]和下標(biāo)position,分別存儲(chǔ)到“name”和"position"這兩個(gè)鍵中,然后傳給MusicActivity。所以,這個(gè)position就是我們需要利用的。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//創(chuàng)建Intent對(duì)象,啟動(dòng)音樂(lè)播放界面
Intent intent=new Intent(frag1.this.getContext(), MusicActivity.class);
//將數(shù)據(jù)存入Intent對(duì)象,利用鍵值對(duì)
intent.putExtra("name",name[position]);
intent.putExtra("position",String.valueOf(position));
//開(kāi)啟意圖
startActivity(intent);
}
});
1.2、我們首先聲明上一首、下一首這些按鈕變量,然后綁定控件,設(shè)置監(jiān)聽(tīng)器,修改下布局文件,這些大家肯定都很熟悉,所以這里也不再贅述了。
聲明變量是在onCreate()的上面
private Button play;//播放按鈕
private Button pause;//暫停按鈕
private Button con;//繼續(xù)播放按鈕
private Button pre;//上一首按鈕
private Button next;//下一首按鈕
private Button exit;//退出按鈕
private ImageView iv_music;//歌手圖片框
綁定以及設(shè)置監(jiān)聽(tīng)器是在init( )中
//依次綁定控件
play=findViewById(R.id.btn_play);
pause=findViewById(R.id.btn_pause);
con=findViewById(R.id.btn_continue_play);
pre=findViewById(R.id.btn_pre);
next=findViewById(R.id.btn_next);
exit=findViewById(R.id.btn_exit);
//依次設(shè)置監(jiān)聽(tīng)器
play.setOnClickListener(this);
pause.setOnClickListener(this);
con.setOnClickListener(this);
pre.setOnClickListener(this);
next.setOnClickListener(this);
exit.setOnClickListener(this);
完整的activity_music布局文件代碼會(huì)在后面給出。
1.3、下面就是重寫(xiě)onClick()方法了,首先intent1是從歌曲列表界面跳轉(zhuǎn)過(guò)來(lái)的意圖。它傳遞過(guò)來(lái)歌曲下標(biāo)position,因?yàn)橛胓etStringExtra()方法獲取的是字符串,所以用parseInt()轉(zhuǎn)成整數(shù)i,這樣就獲取到這首歌的下標(biāo)了。
//定義歌曲列表傳過(guò)來(lái)的下標(biāo)position
String position= intent1.getStringExtra("position");
//將字符串轉(zhuǎn)化為整型i
int i=parseInt(position);
1.4、iv_music就是圖片框,顯示歌手的圖片,name_song是歌曲名的文本框,顯示歌曲名稱。這里我在MusicActivity里面定義了一個(gè)musicName的數(shù)組,數(shù)組存放的就是歌曲名,和我們?cè)趂rag1里面定義的name數(shù)組內(nèi)容是一樣的。其實(shí)這樣我們就不需要?jiǎng)倓偟膄rag1傳值給我們歌曲名了。

1.5、這時(shí)候我們來(lái)獲取上一首歌,只需要i-1就可以了,但是問(wèn)題來(lái)了,這樣只能進(jìn)行一次,繼續(xù)下一首就會(huì)沒(méi)有反應(yīng),因?yàn)閛nClick方法是每次被點(diǎn)擊都要調(diào)用的。里面的position每次都初始化為那個(gè)i,所以你改變下標(biāo)只能改變一次而已。
1.6、既然這樣行不通,那么如何做到每次更新這個(gè)下標(biāo)呢,這里可以自己定義個(gè)全局變量change,用來(lái)記錄這個(gè)下標(biāo),每次加一或者減一,這個(gè)變量是全局變量,它的值是可以變化的,所以每次調(diào)用onClick()方法并不會(huì)復(fù)原值。
public int change=0;//記錄下標(biāo)的變化值
1.7、這樣,不管是上一首i-1還是下一首i+1都可以直接再加上change就行了,代碼如下:
case R.id.btn_pre://播放上一首
if((i+change)<1) {
Toast.makeText(MusicActivity.this, "已經(jīng)是第一首了", Toast.LENGTH_SHORT).show();
return;
}
else {
change--;
music_pic.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
musicControl.play(i+change);
animator.start();
break;
}
case R.id.btn_next://播放下一首
if((i+change)==musicName.length-1) {//這里musicName.length-1表示的最后一首歌的下標(biāo)
Toast.makeText(MusicActivity.this, "已經(jīng)是最后一首了", Toast.LENGTH_SHORT).show();
return;
}
else {
change++;
music_pic.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
musicControl.play(i+change);
animator.start();
break;
}
1.8、這樣在第一版基礎(chǔ)上可以成功實(shí)現(xiàn)上一首下一首的效果:

完整的MusicActivity代碼如下:
package zj.dzh.music_list;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import static java.lang.Integer.parseInt;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class MusicActivity extends AppCompatActivity implements View.OnClickListener{
//定義歌曲名稱的數(shù)組
public String[] musicName={"鄧紫棋——光年之外","蔡健雅——紅色高跟鞋","Taylor Swift——Love Story",
"樸樹(shù)——平凡之路","田馥甄——小幸運(yùn)","周杰倫——七里香","林俊杰——江南"};
private static SeekBar sb;//定義進(jìn)度條
private static TextView tv_progress,tv_total,name_song;//定義開(kāi)始和總時(shí)長(zhǎng),歌曲名控件
private ObjectAnimator animator;//定義旋轉(zhuǎn)的動(dòng)畫(huà)
private MusicService.MusicControl musicControl;//音樂(lè)控制類(lèi)
private Button play;//播放按鈕
private Button pause;//暫停按鈕
private Button con;//繼續(xù)播放按鈕
private Button pre;//上一首按鈕
private Button next;//下一首按鈕
private Button exit;//退出按鈕
private ImageView iv_music;//歌手圖片框
Intent intent1,intent2;//定義兩個(gè)意圖
MyServiceConn conn;
private boolean isUnbind =false;//記錄服務(wù)是否被解綁
public int change=0;//記錄下標(biāo)的變化值
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//綁定布局文件
setContentView(R.layout.activity_music);
//獲得意圖
intent1=getIntent();
//初始化
init();
}
//初始化
private void init(){
//依次綁定控件
tv_progress=findViewById(R.id.tv_progress);
tv_total=findViewById(R.id.tv_total);
sb=findViewById(R.id.sb);
name_song=findViewById(R.id.song_name);
iv_music=findViewById(R.id.iv_music);
play=findViewById(R.id.btn_play);
pause=findViewById(R.id.btn_pause);
con=findViewById(R.id.btn_continue_play);
pre=findViewById(R.id.btn_pre);
next=findViewById(R.id.btn_next);
exit=findViewById(R.id.btn_exit);
//依次設(shè)置監(jiān)聽(tīng)器
play.setOnClickListener(this);
pause.setOnClickListener(this);
con.setOnClickListener(this);
pre.setOnClickListener(this);
next.setOnClickListener(this);
exit.setOnClickListener(this);
//創(chuàng)建意圖對(duì)象
intent2=new Intent(this,MusicService.class);
conn=new MyServiceConn();//創(chuàng)建服務(wù)連接對(duì)象
bindService(intent2,conn,BIND_AUTO_CREATE);//綁定服務(wù)
//從歌曲列表傳過(guò)來(lái)的歌曲名
String name=intent1.getStringExtra("name");
//設(shè)置歌曲名顯示
name_song.setText(name);
//定義歌曲列表傳過(guò)來(lái)的下標(biāo)position
String position= intent1.getStringExtra("position");
//將字符串轉(zhuǎn)化為整型i
int i=parseInt(position);
//圖像框設(shè)置為frag1里面的圖標(biāo)數(shù)組,下標(biāo)為i
iv_music.setImageResource(frag1.icons[i]);
//為滑動(dòng)條添加事件監(jiān)聽(tīng)
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//當(dāng)滑動(dòng)條到末端時(shí),自動(dòng)播放下一首
if (progress==seekBar.getMax()){
change++;//自動(dòng)下一首,下標(biāo)加一
String position= intent1.getStringExtra("position");
int nowPosition=(parseInt(position)+change)%musicName.length;//當(dāng)前歌曲下標(biāo)
iv_music.setImageResource(frag1.icons[nowPosition]);
name_song.setText(musicName[nowPosition]);
// musicControl.play(nowPosition);//播放歌曲文件
// musicControl.seekTo(0);
// pause.setVisibility(View.VISIBLE);
//musicControl.seekTo(0);//重置播放進(jìn)度
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {//滑動(dòng)條開(kāi)始滑動(dòng)時(shí)調(diào)用
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {//滑動(dòng)條停止滑動(dòng)時(shí)調(diào)用
//根據(jù)拖動(dòng)的進(jìn)度改變音樂(lè)播放進(jìn)度
int progress=seekBar.getProgress();//獲取seekBar的進(jìn)度
musicControl.seekTo(progress);//改變播放進(jìn)度
}
});
animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f);
animator.setDuration(10000);//動(dòng)畫(huà)旋轉(zhuǎn)一周的時(shí)間為10秒
animator.setInterpolator(new LinearInterpolator());//勻速
animator.setRepeatCount(-1);//-1表示設(shè)置動(dòng)畫(huà)無(wú)限循環(huán)
}
public static Handler handler=new Handler(){//創(chuàng)建消息處理器對(duì)象
//在主線程中處理從子線程發(fā)送過(guò)來(lái)的消息
@Override
public void handleMessage(Message msg){
Bundle bundle=msg.getData();//獲取從子線程發(fā)送過(guò)來(lái)的音樂(lè)播放進(jìn)度
int duration=bundle.getInt("duration");
int currentPosition=bundle.getInt("currentPosition");
sb.setMax(duration);
sb.setProgress(currentPosition);
//歌曲總時(shí)長(zhǎng),單位為毫秒
int minute=duration/1000/60;
int second=duration/1000%60;
String strMinute=null;
String strSecond=null;
if(minute<10){//如果歌曲的時(shí)間中的分鐘小于10
strMinute="0"+minute;//在分鐘的前面加一個(gè)0
}else{
strMinute=minute+"";
}
if (second<10){//如果歌曲中的秒鐘小于10
strSecond="0"+second;//在秒鐘前面加一個(gè)0
}else{
strSecond=second+"";
}
tv_total.setText(strMinute+":"+strSecond);
//歌曲當(dāng)前播放時(shí)長(zhǎng)
minute=currentPosition/1000/60;
second=currentPosition/1000%60;
if(minute<10){//如果歌曲的時(shí)間中的分鐘小于10
strMinute="0"+minute;//在分鐘的前面加一個(gè)0
}else{
strMinute=minute+" ";
}
if (second<10){//如果歌曲中的秒鐘小于10
strSecond="0"+second;//在秒鐘前面加一個(gè)0
}else{
strSecond=second+" ";
}
tv_progress.setText(strMinute+":"+strSecond);
}
};
class MyServiceConn implements ServiceConnection{//用于實(shí)現(xiàn)連接服務(wù)
@Override
public void onServiceConnected(ComponentName name, IBinder service){
musicControl=(MusicService.MusicControl) service;
}
@Override
public void onServiceDisconnected(ComponentName name){
}
}
//未解綁則解綁
private void unbind(boolean isUnbind){
if(!isUnbind){//判斷服務(wù)是否被解綁
musicControl.pausePlay();//暫停播放音樂(lè)
unbindService(conn);//解綁服務(wù)
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
//獲取歌曲名的下標(biāo)字符串
String position=intent1.getStringExtra("position");
//將字符串轉(zhuǎn)為整數(shù)
int i=parseInt(position);
switch (v.getId()){
case R.id.btn_play://播放按鈕點(diǎn)擊事件
play.setVisibility(View.INVISIBLE);
musicControl.play(i);
animator.start();
break;
case R.id.btn_pre://播放上一首
if((i+change)<1) {
change=musicName.length-1-i;
musicControl.play(i+change);
iv_music.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
pause.setVisibility(View.VISIBLE);
animator.start();
break;
}
else {
change--;
iv_music.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
musicControl.play(i+change);
pause.setVisibility(View.VISIBLE);
animator.start();
break;
}
case R.id.btn_next://播放下一首
if((i+change)==musicName.length-1) {//這里musicName.length-1表示的最后一首歌的下標(biāo),即歌曲總數(shù)-1
change=-i;
musicControl.play(i+change);
iv_music.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
pause.setVisibility(View.VISIBLE);
animator.start();
break;
}
else {
change++;
iv_music.setImageResource(frag1.icons[i+change]);
name_song.setText(musicName[i+change]);
musicControl.play(i+change);
pause.setVisibility(View.VISIBLE);
animator.start();
break;
}
case R.id.btn_pause://暫停按鈕點(diǎn)擊事件
pause.setVisibility(View.INVISIBLE);
con.setVisibility(View.VISIBLE);
musicControl.pausePlay();
animator.pause();
break;
case R.id.btn_continue_play://繼續(xù)播放按鈕點(diǎn)擊事件
con.setVisibility(View.INVISIBLE);
pause.setVisibility(View.VISIBLE);
musicControl.continuePlay();
animator.start();
break;
case R.id.btn_exit://退出按鈕點(diǎn)擊事件
unbind(isUnbind);
isUnbind=true;
finish();
break;
}
}
@Override
protected void onDestroy(){
super.onDestroy();
unbind(isUnbind);//解綁服務(wù)
}
}
2、個(gè)性化按鈕
2.1、音樂(lè)播放器按鈕用這種框框看起來(lái)屬實(shí)有些別扭,所以我又花了幾個(gè)小時(shí)從各大圖標(biāo)網(wǎng)站找到了一款比較中意的UI圖。

2.2、通過(guò)WPS圖片剪切,將它們裁剪成矢量圖(其實(shí)就是剪成圓形),效果如下圖所示:

2.3、然后修改layout文件,這里要將播放按鈕、暫停按鈕、繼續(xù)播放按鈕三個(gè)按鈕進(jìn)行重疊。我通過(guò)android:layout_centerHorizontal="true"來(lái)將它們?nèi)齻€(gè)全部水平居中,這樣就重疊在一起了,但是重疊順序也必須有講究,最上層的是播放按鈕(btn_play),第二層是暫停按鈕(btn_pause),第三層是
繼續(xù)播放按鈕(btn_continue_play)。
同學(xué)甲:為什么最上層的是播放按鈕?
博主:因?yàn)椴シ虐粹o點(diǎn)擊之后要讓它消失,所以它必須放第一個(gè)。
同學(xué)乙:為什么第二層的是暫停按鈕?
博主:因?yàn)椴シ鸥枨罂隙ㄒ軙和8枨?,所以要能顯示暫停按鈕,而且暫停按鈕點(diǎn)擊之后會(huì)消失,顯示最底下的繼續(xù)播放按鈕。
同學(xué)丙:那我按下繼續(xù)播放按鈕是不是歌曲繼續(xù)播放,繼續(xù)播放按鈕消失,然后暫停按鈕出現(xiàn)?
博主:你說(shuō)的沒(méi)錯(cuò),就是這樣的邏輯。
這里給出完整的layout代碼:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/music_bg"
tools:context=".MusicActivity"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_music"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_gravity="center_horizontal"
android:layout_margin="15dp"
android:src="@drawable/music0"/>
<TextView
android:id="@+id/song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="歌曲名"
android:textSize="20sp"/>
<SeekBar
android:id="@+id/sb"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"/>
<TextView
android:id="@+id/tv_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="00:00"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_continue_play"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:background="@drawable/play"/>
<Button
android:id="@+id/btn_pause"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:background="@drawable/pause"/>
<Button
android:id="@+id/btn_play"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:background="@drawable/play" />
<Button
android:id="@+id/btn_pre"
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@drawable/pre"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/btn_play"/>
<Button
android:id="@+id/btn_next"
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@drawable/next"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/btn_play"/>
<Button
android:id="@+id/btn_exit"
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@drawable/exit"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:layout_toRightOf="@id/btn_next"/>
</RelativeLayout>
</LinearLayout>
五、運(yùn)行效果
1、打開(kāi)項(xiàng)目,編譯成功后運(yùn)行,會(huì)看到Android Studio執(zhí)行這樣的過(guò)程:Starting AVD啟動(dòng)模擬器,然后gradle build構(gòu)建項(xiàng)目,接著就會(huì)install安裝app,最后launch運(yùn)行出app。首先顯示的是歌曲列表界面:

2、我們選擇【平凡之路】進(jìn)行播放,跳轉(zhuǎn)到音樂(lè)播放界面:

3、點(diǎn)擊【播放】按鈕,音樂(lè)開(kāi)始播放,圖片開(kāi)始轉(zhuǎn)動(dòng):

4、拖動(dòng)進(jìn)度條,音樂(lè)直接快進(jìn)到指定位置:

5、點(diǎn)擊【下一首】按鈕,播放了【小幸運(yùn)】:

6、再點(diǎn)擊【下一首】按鈕,播放了【七里香】:

7、點(diǎn)擊【上一首】按鈕,又播放了【小幸運(yùn)】:

8、點(diǎn)擊【暫?!堪粹o,歌曲停止播放,圖片也停止旋轉(zhuǎn):

9、點(diǎn)擊繼續(xù)播放按鈕,歌曲繼續(xù)播放,圖片繼續(xù)旋轉(zhuǎn):

10、點(diǎn)擊【退出】按鈕,返回到歌曲列表界面:

11、點(diǎn)擊【專輯】選項(xiàng),進(jìn)入到專輯界面,依舊選的是老霉的《Red》專輯封面

六、項(xiàng)目總結(jié)
本次項(xiàng)目主要是對(duì)上一個(gè)版本的音樂(lè)播放器進(jìn)行功能和UI上的優(yōu)化,實(shí)現(xiàn)起來(lái)并不算復(fù)雜但是有些細(xì)節(jié)需要注意,不過(guò)有耐心就可以了。其實(shí)不管遇到什么問(wèn)題,都應(yīng)該耐心去分析問(wèn)題,然后想出解決思路,再然后解決問(wèn)題。這個(gè)過(guò)程可能會(huì)很短暫也可能會(huì)很漫長(zhǎng),但是我們要堅(jiān)信只要你用心去做,肯定會(huì)解決的。
到此這篇關(guān)于Android Studio實(shí)現(xiàn)音樂(lè)播放器2.0的文章就介紹到這了,更多相關(guān)Android Studio實(shí)現(xiàn)音樂(lè)播放器2.0內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android實(shí)現(xiàn)簡(jiǎn)單的音樂(lè)播放器
- Android?Studio實(shí)現(xiàn)音樂(lè)播放器的全過(guò)程(簡(jiǎn)單易上手)
- Android實(shí)現(xiàn)簡(jiǎn)易的音樂(lè)播放器
- Android Studio實(shí)現(xiàn)音樂(lè)播放器
- Android開(kāi)發(fā)簡(jiǎn)易音樂(lè)播放器
- android實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放器
- Android實(shí)現(xiàn)音樂(lè)播放器鎖屏頁(yè)
- Android音樂(lè)播放器簡(jiǎn)單實(shí)現(xiàn)案例
相關(guān)文章
Android實(shí)現(xiàn)自動(dòng)填寫(xiě)獲取驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)自動(dòng)填寫(xiě)獲取驗(yàn)證碼功能,感興趣的小伙伴們可以參考一下2016-03-03
Android?8.0實(shí)現(xiàn)藍(lán)牙遙控器自動(dòng)配對(duì)
這篇文章主要為大家詳細(xì)介紹了Android?8.0實(shí)現(xiàn)藍(lán)牙遙控器自動(dòng)配對(duì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
詳解Android數(shù)據(jù)存儲(chǔ)之Android 6.0運(yùn)行時(shí)權(quán)限下文件存儲(chǔ)的思考
本篇文章主要介紹了Android數(shù)據(jù)存儲(chǔ)之Android 6.0運(yùn)行時(shí)權(quán)限下文件存儲(chǔ)的思考,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。2016-12-12
Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南
Retrofit和OkHttp都是Square在GitHub上開(kāi)源的第三方HTTP支持包,兩個(gè)包可以搭配使用,本文即是來(lái)講解Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南:2016-07-07
react native打包apk文件安裝好之后進(jìn)入應(yīng)用閃退的解決方案
這篇文章主要介紹了react native打包apk文件安裝好之后進(jìn)入應(yīng)用閃退的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
Android Studio實(shí)現(xiàn)簡(jiǎn)易進(jìn)制轉(zhuǎn)換計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android Studio實(shí)現(xiàn)簡(jiǎn)易進(jìn)制轉(zhuǎn)換計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
如何為RecyclerView添加Header和Footer
這篇文章主要為大家詳細(xì)介紹了如何為RecyclerView添加Header和Footer,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android下2d物理引擎Box2d用法簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android下2d物理引擎Box2d用法,實(shí)例分析了在Android平臺(tái)上使用Box2d的基本技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
Android圖片無(wú)限輪播的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android圖片無(wú)限輪播的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12

