安卓实训:基于SD卡音乐播放器V06日志

该博客围绕音乐播放器V06展开,介绍了在安卓平台的开发过程。实现了对SD卡音乐的读取与播放,界面有进度条和歌曲时长显示,具备上一曲、下一曲按钮,用ListView展示音乐并可点击切换。还实现了应用启动动画和音乐播放模式切换,涵盖项目结构、准备工作及多个文件的创建与修改。

音乐播放器V06:
实现对整个SD卡音乐读取
播放读取出的音乐
界面实现进度条和歌曲时长
上一曲和下一曲按钮
使用ListView展示所有音乐,并能通过点击ListView切换音乐
音乐播放与前台展示相分离
实现应用启动动画
实现音乐播放模式切换

效果:

在这里插入图片描述

项目结构

在这里插入图片描述

准备工作

创建安卓应用

在这里插入图片描述

拷贝图片到drawable和mipmap

在这里插入图片描述

向DDMS里添加音乐

SDK—》tools—》monitor.bat
在这里插入图片描述

创建按钮背景图片选择器

在这里插入图片描述

播放按钮

在这里插入图片描述

暂停按钮

在这里插入图片描述

上一首按钮

在这里插入图片描述

下一首按钮

在这里插入图片描述

创建样式选择器

在这里插入图片描述
在这里插入图片描述

activity_main.xml

<?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:orientation="vertical"
    android:padding="20dp"
    android:background="@drawable/background"
    tools:context=".ui.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ProgressBar
            android:id="@+id/pbScanMusic"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:visibility="gone"/>

        <TextView
            android:id="@+id/tvScanMusic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/scan_music"
            android:textColor="#0000ff"
            android:textSize="25sp"
            android:visibility="gone"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/custom_border"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_play_mode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/play_mode"
            android:textSize="13sp" />

        <RadioGroup
            android:id="@+id/rg_play_mode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/rb_order"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text="@string/order"
                android:textSize="13sp" />

            <RadioButton
                android:id="@+id/rb_random"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/random"
                android:textSize="13sp" />

            <RadioButton
                android:id="@+id/rb_loop"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/loop"
                android:textSize="13sp" />
        </RadioGroup>
    </LinearLayout>


    <ListView
        android:id="@+id/lvMusicName"
        android:layout_width="match_parent"
        android:layout_height="450dp"
        android:layout_marginBottom="16dp"
        android:layout_weight="8" />
    <TextView
        android:id="@+id/tvMusicName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_weight="0.5"
        android:textColor="#0000ff"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:layout_weight="0.5"
        android:gravity="center">

        <TextView
            android:id="@+id/tvCurrentPosition"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#ff0000"/>

        <SeekBar
            android:id="@+id/sbMusicProgress"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="6" />
        <TextView
            android:id="@+id/tvDuration"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#ff00ff"/>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_weight="1"
        android:gravity="center">
        <Button
            android:id="@+id/btnPrevious"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/previous_button_selector"
            android:onClick="doPrevious"/>
        <Button
            android:id="@+id/btnPlayOrPause"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/play_button_selector"
            android:onClick="doPlayOrPause"/>
        <Button
            android:id="@+id/btnNext"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/next_button_selector"
            android:onClick="doNext"/>
    </LinearLayout>
</LinearLayout>

music_name_list_item.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="match_parent"
    android:orientation="vertical"
    android:padding="20dp">

    <TextView
        android:id="@+id/tvMusicName"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:textSize="20sp"/>
</LinearLayout>

activity_splash_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/splash_background"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_music_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:src="@drawable/music" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="@string/title"
        android:textColor="#0000ff"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/tv_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/version"
        android:textColor="#ff0000"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_author"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/author"
        android:textColor="#000000"
        android:textSize="20sp" />
</LinearLayout>

动画animator.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <alpha
        android:duration="3000"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:toDegrees="+360" />
</set>

string.xml

<resources>
    <string name="app_name">基于SD卡音乐播放器V06</string>
    <string name="scan_music">音乐扫描中,请稍后...</string>
    <string name="title">基于存储卡音乐播放器</string>
    <string name="version">Version 6.0</string>
    <string name="author">得加钱</string>
    <string name="play_mode">播放模式:</string>
    <string name="order">顺序</string>
    <string name="loop">单曲循环</string>
    <string name="random">随机</string>
</resources>

MusicAdapter .java

import java.util.List;

public class MusicAdapter extends BaseAdapter {

    private Context context; // 上下文
    private List<Music> musicList; // 音乐列表

    /**
     * 构造方法
     *
     * @param context
     * @param musicList
     */
    public MusicAdapter(Context context, List<Music> musicList) {
        this.context = context;
        this.musicList = musicList;
    }

    @Override
    public int getCount() {
        return musicList.size();
    }

    @Override
    public Object getItem(int position) {
        return musicList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 声明视图容器
        ViewHolder holder = null;

        // 判断转换视图是否为空
        if (convertView == null) {
            // 将列表项模板映射成转换视图
            convertView = LayoutInflater.from(context).inflate(R.layout.music_name_list_item, null);
            // 创建视图容器对象
            holder = new ViewHolder();
            // 实例化转换视图里的控件
            holder.tvMusicName = convertView.findViewById(R.id.tvMusicName);
            // 将视图容器附加到转换视图
            convertView.setTag(holder);
        } else {
            // 从转换视图里取出视图容器
            holder = (ViewHolder) convertView.getTag();
        }

        // 获取列表项要显示的数据
        Music music = musicList.get(position);
        // 设置列表项控件的属性(去掉路径和扩展名)
        holder.tvMusicName.setText(music.getMusicName().substring(
                music.getMusicName().lastIndexOf("/")+1, music.getMusicName().lastIndexOf(".")));

        // 返回转换视图
        return convertView;
    }

    /**
     * 视图容器
     */
    private static class ViewHolder {
        TextView tvMusicName;
    }
}

Music

public class Music {
    private String musicName;//音乐名

    public String getMusicName() {
        return musicName;
    }

    public void setMusicName(String musicName) {
        this.musicName = musicName;
    }

    @Override
    public String toString() {
        return "Music{" +
                "musicName='" + musicName + '\'' +
                '}';
    }
}

MusicPlayerApplication .java

import android.app.Application;
import android.os.Environment;
import net.lj.sdcard_music_player_v06.entity.Music;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class MusicPlayerApplication extends Application {

    private SimpleDateFormat sdf;//简单日期格式
    private int currentMusirIndex;//当前音乐索引
    private int currentPosition;//当前播放位置
    private int progressChangedByUser;//用户修改的播放进度

    private int playMode;//播放模式


    public int getPlayMode() {
        return playMode;
    }

    public void setPlayMode(int playMode) {
        this.playMode = playMode;
    }

    public int getProgressChangedByUser() {
        return progressChangedByUser;
    }

    public void setProgressChangedByUser(int progressChangedByUser) {
        this.progressChangedByUser = progressChangedByUser;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //实例化简单日期格式
        sdf=new SimpleDateFormat("mm:ss");
    }

    public int getCurrentMusirIndex() {
        return currentMusirIndex;
    }

    public void setCurrentMusirIndex(int currentMusirIndex) {
        this.currentMusirIndex = currentMusirIndex;
    }

    public int getCurrentPosition() {
        return currentPosition;
    }

    public void setCurrentPosition(int currentPosition) {
        this.currentPosition = currentPosition;
    }

    //获取格式化时间
    public  String getFormatTime(int time){
        return sdf.format(time);
    }

    //生成指定目录下某种类型的文件列表
    public  void makeTypeFileList(File dir, String suffix, List<String> typeFileList){
        //dir:目录地址  suffix:文件后缀名  typeFileLis:返回的数组
        //获取指定目录下的File数组(File既可以指向目录,也可以指向文件)
        File[] files=dir.listFiles();
        //遍历File数组
        for(File file:files){
            //判断file是否是文件
            if(file.isFile()){//file是文件
                //按照后缀来过滤文件
                if(file.getName().endsWith(suffix)){
                    //
                    typeFileList.add(file.getAbsolutePath());
                }
            }else {//file是目录
                //目录非空,递归调用
                if(file.list() !=null){
                    makeTypeFileList(file,suffix,typeFileList);
                }
            }
        }
    }

    //获取音乐列表
    public List<Music> getMusicList(){
        //声明音乐列表
        List<Music> musicList=null;
        //获取外置存储卡根目录
        File sdRootDir= Environment.getExternalStorageDirectory();
        //创建后缀字符串
        String suffix=".mp3";
        //创建音乐文件列表
        List<String> musicFileList=new ArrayList<>();
        //调用方法,生成指定目录下某种类型文件列表
        makeTypeFileList(sdRootDir,suffix,musicFileList);
        //判断音乐文件列表里是否有元素
        if(musicFileList.size()>0){
            //实例化音乐列表
            musicList = new ArrayList<>();
            //遍历音乐文件列表
            for(String musicFile:musicFileList){
                //创建音乐实体
                Music music=new Music();
                //设置实体属性
                music.setMusicName(musicFile);
                //将音乐实体添加到音乐列表
                musicList.add(music);
            }
        }
        //返回音乐列表
        return musicList;
    }
}

接口AppConstants.java

public interface AppConstants {
    String TAG="net.lj.sdcard_music_player_v06";//应用程序标记
    String INTENT_ACTION_PREVIOUS=TAG+".intent.action.PREVIOUS";//广播频道常量:播放上一首
    String INTENT_ACTION_PLAY=TAG+".intent.action.PLAY";//广播频道常量:播放
    String INTENT_ACTION_PLAY_OR_PAUSE=TAG+".intent.action.PLAY_OR_PAUSE";//广播频道常量:播放或暂停
    String INTENT_ACTION_NEXT=TAG+".intent.action.NEXT";//广播频道常量:播放下一首
    String INTENT_ACTION_UPDATE_PROGRESS=TAG+".intent.action.UPDATE_PROGRESS";//广播频道常量:更新进度
    String CONTROL_ICON="control_icon";//控制图标名称常量
    String DURATION="duration";//播放时长名称常量
    String INTENT_ACTION_USER_CHANGE_PROGRESS = TAG + ".intent.action.USER_CHANGE_PROGRESS";//广播频道常量:用户改变播放进度
    int PLAY_MODE_ORDER = 0;//播放模式:顺序播放
    int PLAY_MODE_RANDOM = 1;//播放模式:随机播放
    int PLAY_MODE_LOOP = 2;//播放模式:单曲循环
}

MusicPlayService.java

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.IBinder;
import androidx.annotation.Nullable;
import net.lj.sdcard_music_player_v06.R;
import net.lj.sdcard_music_player_v06.app.AppConstants;
import net.lj.sdcard_music_player_v06.app.MusicPlayerApplication;
import net.lj.sdcard_music_player_v06.entity.Music;
import java.io.IOException;
import java.util.List;
import java.util.Random;

public class MusicPlayService extends Service implements AppConstants {

    private MediaPlayer mp;//媒体播放器
    private String musicName;//音乐文件名
    private Thread thread;//更新音乐播放器进度的线程
    private boolean isRunning;//线程循环控制变量
    private List<Music> musicList;//音乐列表(数据源)
    private MusicPlayerApplication app;//音乐播放器应用程序对象
    private MusiceReceiver receiver;//音乐广播接收器

    @Override
    public void onCreate() {
        super.onCreate();
        //获取音乐播放器应用程序对象
        app= (MusicPlayerApplication) getApplication();
        //获取音乐列表
        musicList=app.getMusicList();

        //创建媒体播放器
        mp=new MediaPlayer();
        //给媒体播放器注册完成监听器
        mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                //切换到下一首音乐
                nextMusic();
            }
        });
        //设置线程循环控制变量为真
        isRunning=true;
        //创建更新播放进度的线程
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRunning){
                    //判断音乐是否在播放
                    if(mp.isPlaying()){
                        //创建意图
                        Intent intent=new Intent();
                        //设置广播频道:更新播放进度
                        intent.setAction(INTENT_ACTION_UPDATE_PROGRESS);
                        //设置当前播放位置
                        app.setCurrentPosition(mp.getCurrentPosition());
                        //让意图携带播放时长数据
                        intent.putExtra(DURATION,mp.getDuration());
                        //让意图携带控制图标数据(暂停图标)
                        intent.putExtra(CONTROL_ICON,R.drawable.pause_button_selector);
                        //按意图发送广播
                        sendBroadcast(intent);
                        try {
                            //让线程睡眠500毫秒
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    };
                }
            }
        });
        //启动更新播放进度的线程
        thread.start();

        //创建音乐广播接收器
        receiver=new MusiceReceiver();
        //创建意图过滤器
        IntentFilter filter=new IntentFilter();
        //通过意图过滤器添加广播频道
        filter.addAction(INTENT_ACTION_PLAY_OR_PAUSE);
        filter.addAction(INTENT_ACTION_PLAY);
        filter.addAction(INTENT_ACTION_PREVIOUS);
        filter.addAction(INTENT_ACTION_NEXT);
        filter.addAction(INTENT_ACTION_USER_CHANGE_PROGRESS);

        //注册广播接收器
        registerReceiver(receiver,filter);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //获取当前播放音乐名
        musicName=musicList.get(app.getCurrentMusirIndex()).getMusicName();
        try {
            //设置播放源
            mp.setDataSource(musicName);
            //缓冲播放源(从存储卡加载到内存)
            mp.prepare();
            //音乐当前播放位置归零
            app.setCurrentPosition(0);
            //创建意图
            Intent intent1=new Intent();
            //设置广播频道:更新播放进度
            intent.setAction(INTENT_ACTION_UPDATE_PROGRESS);
            //让意图携带播放时长
            intent1.putExtra(DURATION,mp.getDuration());
            //按意图发送广播
            sendBroadcast(intent1);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //返回非粘性服务
        return Service.START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //释放媒体播放器
        if(mp!=null){
            mp.release();
            mp=null;
        }
        //注销广播接收者
        unregisterReceiver(receiver);
        //设置线程循环控制变量
        isRunning=false;
        //销毁子线程
        thread=null;
    }

    //播放方法
    private void play() {
        try {
            //重置媒体播放器
            mp.reset();
            //获取当前播放的音乐名
            musicName=musicList.get(app.getCurrentMusirIndex()).getMusicName();
            //设置播放源(完整音乐名=音乐目录+音乐名)
            mp.setDataSource(musicName);
            //缓冲播放源(从存储卡加载到内存)
            mp.prepare();
            //定位到暂停时的播放的位置
            mp.seekTo(app.getCurrentPosition());
            //启动音乐的播放
            mp.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //上一首音乐
    private void previousMusic() {
        //更新音乐索引
        if(app.getCurrentMusirIndex() > 0){
            app.setCurrentMusirIndex(app.getCurrentMusirIndex()-1);
        }else{
            app.setCurrentMusirIndex(musicList.size()-1);//最后一首
        }
        //当前播放位置归零
        app.setCurrentPosition(0);
        //调用播放方法
        play();
    }
    //下一首音乐
    private void nextMusic() {
        // 根据播放模式来更新音乐索引
        switch (app.getPlayMode()) {
            // 顺序播放模式
            case PLAY_MODE_ORDER:
                if (app.getCurrentMusirIndex() < musicList.size() - 1) {
                    app.setCurrentMusirIndex(app.getCurrentMusirIndex() + 1);
                } else {
                    app.setCurrentMusirIndex(0);
                }
                break;
            // 随机播放模式
            case PLAY_MODE_RANDOM:
                // 随机设置索引
                app.setCurrentMusirIndex(new Random().nextInt(app.getMusicList().size()));
                break;
            // 单曲循环模式
            case PLAY_MODE_LOOP:
                // 音乐索引保持不变
                break;
        }
        // 当前播放位置归零
        app.setCurrentPosition(0);
        // 调用播放方法
        play();
    }
    
    //暂停方法
    private void pause(){
        //暂停播放
        mp.pause();
        //保存音乐播放的当前位置
        app.setCurrentPosition(mp.getCurrentPosition());
        //发送广播给主界面,更改图标,更改播放进度
        //创建意图
        Intent intent=new Intent();
        //设置广播频道:更新播放进度
        intent.setAction(INTENT_ACTION_UPDATE_PROGRESS);
        //让意图携带播放时长数据
        intent.putExtra(DURATION,mp.getDuration());
        //让意图携带控制图标数据(播放图标
        intent.putExtra(CONTROL_ICON, R.drawable.play_button_selector);
        //按意图发送广播
        sendBroadcast(intent);
    }

    //音乐广播接收器
    private class MusiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //获取意图动作(广播频道)
            String action=intent.getAction();
            //对广播频道非空判断
            if(action!=null){
                //要把不同频道进行不同操作
                switch (action){
                    case INTENT_ACTION_PLAY://主界面单击了音乐列表项
                        //播放进度归零
                        app.setCurrentPosition(0);
                        //调用播放方法
                        play();
                        break;
                    case  INTENT_ACTION_PLAY_OR_PAUSE://主界面单击了播放|暂停按钮
                        //判断音乐是否在播放
                        if(mp.isPlaying()){
                            pause();
                        }else {
                            play();
                        }
                        break;
                    case INTENT_ACTION_PREVIOUS://主界面单击了上一首按钮
                        //切换到上一首音乐
                        previousMusic();
                        break;
                    case INTENT_ACTION_NEXT://主界面单击了下一首按钮
                        //切换到下一首音乐
                        nextMusic();
                        break;
                    case INTENT_ACTION_USER_CHANGE_PROGRESS:
                        // 根据拖拽条的进度值计算当前播放位置
                        app.setCurrentPosition(app.getProgressChangedByUser() * mp.getDuration() / 100);
                        // 根据音乐当前播放位置开始播放音乐
                        play();
                        break;
                }
            }
        }
    }
}

修改项目清单文件

在这里插入图片描述

MainActivity.java

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import net.lj.sdcard_music_player_v06.R;
import net.lj.sdcard_music_player_v06.adapter.MusicAdapter;
import net.lj.sdcard_music_player_v06.app.AppConstants;
import net.lj.sdcard_music_player_v06.app.MusicPlayerApplication;
import net.lj.sdcard_music_player_v06.entity.Music;
import net.lj.sdcard_music_player_v06.service.MusicPlayService;

import java.util.List;

public class MainActivity extends AppCompatActivity implements AppConstants {

    private String musicName;//音乐文件名
    private TextView tvMusicName;//音乐名标签
    private Button btnPlayOrPause;//播放|暂停按钮
    private MusicReceiver receiver;//音乐广播接收器
    private TextView tvCurrentPosition;//显示当前播放位置的标签
    private TextView tvDuration;//显示音乐播放时长的标签
    private SeekBar sbMusicProgress;//音乐播放进度条
    private ListView lvMusicName;//音乐名列表控件
    private List<Music> musicList;//音乐列表(数据源)
    private MusicAdapter adapter;//音乐适配器
    private MusicPlayerApplication app;//音乐播放器应用程序对象
    private ProgressBar pbScanMusic;//扫描存储卡音乐进度条
    private TextView tvScanMusic;//扫描存储卡音乐标签(提示用户耐心等候)
    private RadioGroup rgPlayMode;//播放模式单选按钮组

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //利用布局资源文件设置用户界面
        setContentView(R.layout.activity_main);

        //通过资源标识符获取控件实例
        lvMusicName=findViewById(R.id.lvMusicName);
        tvMusicName = findViewById(R.id.tvMusicName);
        btnPlayOrPause = findViewById(R.id.btnPlayOrPause);
        tvCurrentPosition = findViewById(R.id.tvCurrentPosition);
        tvDuration = findViewById(R.id.tvDuration);
        sbMusicProgress = findViewById(R.id.sbMusicProgress);
        pbScanMusic=findViewById(R.id.pbScanMusic);
        tvScanMusic=findViewById(R.id.tvScanMusic);
        rgPlayMode = findViewById(R.id.rg_play_mode);


        //获取音乐播放器应用程序对象
        app= (MusicPlayerApplication) getApplication();

        //定义存储读写权限数组
        String[] PERMISSIONS_STORAGE = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        //检查是否有读权限
        final int permission = ActivityCompat.checkSelfPermission(MainActivity.this, PERMISSIONS_STORAGE[0]);
        //如果没有权限,那么就请求读权限
        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, 0);
            return;
        }

        //执行填充音乐列表的异步任务
        new FillMusicListTask().execute();

        // 给播放模式单选按钮组注册监听器
        rgPlayMode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
                // 判断用户选择何种播放模式
                switch (checkedId) {
                    // 顺序播放模式
                    case R.id.rb_order:
                        app.setPlayMode(PLAY_MODE_ORDER);
                        break;
                    // 随机播放模式
                    case R.id.rb_random:
                        app.setPlayMode(PLAY_MODE_RANDOM);
                        break;
                    // 单曲循环模式
                    case R.id.rb_loop:
                        app.setPlayMode(PLAY_MODE_LOOP);
                        break;
                }
            }
        });


        //给音乐列表控件注册监听器
        lvMusicName.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                //获取音乐索引
                app.setCurrentMusirIndex(position);
                //播放进度归零
                app.setCurrentPosition(0);
                //获取音乐名
                musicName=app.getMusicList().get(position).getMusicName();
                //设置音乐名标签内容,去掉路径和扩展名
                tvMusicName.setText("No." + (app.getCurrentMusirIndex() + 1) + " " + musicName.substring(
                        musicName.lastIndexOf("/") + 1, musicName.lastIndexOf(".")));
                //创建意图
                Intent intent=new Intent();
                //设置广播频道:播放
                intent.setAction(INTENT_ACTION_PLAY);
                //按意图发送广播
                sendBroadcast(intent);
            }
        });

        // 给音乐播放拖拽条注册监听器
        sbMusicProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // 判断进度是否为用户修改
                if (fromUser) {
                    // 设置用户修改的播放进度
                    app.setProgressChangedByUser(progress);
                    // 创建意图
                    Intent intent = new Intent();
                    // 设置广播频道:用户修改播放进度
                    intent.setAction(INTENT_ACTION_USER_CHANGE_PROGRESS);
                    // 按意图发送广播
                    sendBroadcast(intent);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });




        //创建音乐广播接收器
        receiver=new MusicReceiver();
        //创建意图过滤器
        IntentFilter filter=new IntentFilter();
        //通过意图过滤器添加广播频道
        filter.addAction(INTENT_ACTION_UPDATE_PROGRESS);
        //注册音乐广播接收器
        registerReceiver(receiver,filter);
    }

    //音乐广播接收器
    private class MusicReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //获取广播频道
            String action=intent.getAction();
            //判断广播频道是否为空
            if(action!=null){
                //根据不同广播频道执行不同操作
                if(INTENT_ACTION_UPDATE_PROGRESS.equals(action)){
                    //获取播放时长
                    int duration=intent.getIntExtra(DURATION,0);
                    //获取播放控制图标
                    int controlIcon=intent.getIntExtra(CONTROL_ICON,R.drawable.play_button_selector);
                    //计算进度值
                    int progress=app.getCurrentPosition()*100/duration;
                    //获取音乐名
                    musicName=app.getMusicList().get(app.getCurrentMusirIndex()).getMusicName();
                    //设置正在播放的文件名(去掉扩展名)
                    tvMusicName.setText("No." + (app.getCurrentMusirIndex() + 1) + " " + musicName.substring(
                            musicName.lastIndexOf("/") + 1, musicName.lastIndexOf(".")));
                    //设置播放进度值标签
                    tvCurrentPosition.setText(app.getFormatTime(app.getCurrentPosition()));
                    //设置播放时长标签
                    tvDuration.setText(app.getFormatTime(duration));
                    //设置播放进度条的进度值
                    sbMusicProgress.setProgress(progress);
                    //设置【播放|暂停】按钮显示的图标
                    btnPlayOrPause.setBackgroundResource(controlIcon);
                }
            }
        }
    }

    //填充音乐列表异步任务类
    private class FillMusicListTask extends AsyncTask<Void,Integer,Void> {
        //耗时工作执行前
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //显示扫描音乐进度条
            pbScanMusic.setVisibility(View.VISIBLE);
            //显示扫描音乐标签
            tvScanMusic.setVisibility(View.VISIBLE);
        }

        @Override
        protected Void doInBackground(Void... voids) {
            //获取音乐列表
            musicList=app.getMusicList();
            //故意耗时,要不然扫描太快
            for(long i=0;i<2000000000;i++){

            }
            return null;
        }

        //耗时工作执行后
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            //隐藏扫描音乐进度条
            pbScanMusic.setVisibility(View.GONE);
            //隐藏扫描音乐标签
            tvScanMusic.setVisibility(View.GONE);
            //判断音乐列表里是否有元素
            if (musicList.size() > 0) {
                //创建联系人数据
                adapter = new MusicAdapter(MainActivity.this, musicList);
                //给列表控件设置适配器
                lvMusicName.setAdapter(adapter);
                //获取当前要播放的音乐名(默认是音乐播放列表的第一首
                musicName = musicList.get(0).getMusicName();
                //设置音乐名标签内容,去掉扩展名,添加一个序号
                tvMusicName.setText("No." + (app.getCurrentMusirIndex() + 1) + " " + musicName.substring(
                        musicName.lastIndexOf("/") + 1, musicName.lastIndexOf(".")));
                //创建意图,用于启动音乐播放服务
                Intent intent=new Intent(MainActivity.this, MusicPlayService.class);
                //按意图启动服务
                startService(intent);
            } else {
                //提示用户没有音乐文件
                Toast.makeText(MainActivity.this, "外置存储卡上没有音乐文件!", Toast.LENGTH_SHORT).show();
            }
        }
    }
    //上一首音乐按钮单击事件处理方法
    public void doPrevious(View view){
        //创建意图
        Intent intent=new Intent();
        //设置广播频道
        intent.setAction(INTENT_ACTION_PREVIOUS);
        //按意图发送广播
        sendBroadcast(intent);
    }

    //下一首音乐按钮单击事件处理方法
    public void doNext(View view){
        //创建意图
        Intent intent=new Intent();
        //设置广播频道
        intent.setAction(INTENT_ACTION_NEXT);
        //按意图发送广播
        sendBroadcast(intent);
    }
    //播放|暂停按钮单击事件处理方法
    public void doPlayOrPause(View view){
        //创建意图
        Intent intent=new Intent();
        //设置广播频道
        intent.setAction(INTENT_ACTION_PLAY_OR_PAUSE);
        //按意图发送广播
        sendBroadcast(intent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //停止音乐播放服务
        stopService(new Intent(MainActivity.this, MusicPlayService.class));
        //注销广播接收器
        if(receiver!=null){
            unregisterReceiver(receiver);
        }
    }
}

SplashScreenActivity.java

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import net.lj.sdcard_music_player_v06.R;

public class SplashScreenActivity extends Activity {

    private Animation animation;//动画对象
    private ImageView ivMusicIcon;//音乐图标图像控件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用布局资源文件设置用户界面
        setContentView(R.layout.activity_splash_screen);

        // 通过资源标识获得控件实例
        ivMusicIcon = findViewById(R.id.iv_music_icon);

        // 加载动画资源文件,创建动画对象
        animation = AnimationUtils.loadAnimation(this, R.anim.animator);
        // 让音乐图标图像控件启动动画
        ivMusicIcon.startAnimation(animation);
        // 给动画对象设置监听器
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // 启动主界面
                startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
                // 关闭启动界面
                finish();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }
}
课 程 名: 软件工程综合实践 实践项目: 基于Android的手机音乐播放器 一、课程设计题目 基于Android的手机音乐播放器 二、课程设计内容(含技术指标) 本程序是基于Android开源系统技术,利用Java语言和Eclipse编辑工具对播放器进行编 写。程序功能如下: 用户可以按照以下规则进行操作: 1.音乐播放:界面对歌曲操作有播放、暂停、停止、音量控制、上一首/下一首 播放模式:随机播放、顺序播放、单曲循环。 2. 音乐管理:音乐列表、专辑:显示全部音乐文件,能直接选择播 放歌曲。 艺术家:归类艺术家的音乐文件。 最近播放:记录用户最近播放音乐文件,方便用户再次使用。 3.显示歌词:能显示播放歌曲的歌词。 4.添加音乐:在文件夹中添加音乐播放列表。 5.删除音乐:删除播放列表的歌曲。 三、进度安排 1.第四周分组分工,5人。并选择题目 2.第五~九周独立完成各自内容及相关文档。 3.第十周周整合,联调。 4.第十一周答辩。 四、基本要求 1.各组指定组长一名,组长负责分工,分工要求要明确,不得重叠。组长有加分。 各个成员独立完成各自的部分,不得抄袭。 2.答辩要求每个人讲清楚自己做的部分。 课程负责人签名: 年 月 日 目录 第一章 引言 6 1.1 目的及范围 6 第二章 需求说明 7 2.1 系统参与者 7 2.2 系统用例 7 2.3领域模型分析 8 第三章 架构设计说明 11 3.1 逻辑视图 11 3.2 进程视图 11 3.3 开发视图 12 3.4 物理视图 13 3.5 用例视图 14 第四章 用例设计 15 4.1 播放音乐用例设计 15 4.2暂停音乐用例设计 16 4.3 切换音乐用例设计 16 4.4 显示音乐列表用例设计 17 4.5 删除音乐用例设计 18 4.6 显示艺术家用例设计 19 4.7 显示专辑用例设计 20 4.8 显示最近播放用例设计 21 4.9 启动随机播放用例设计 22 4.10 启动单曲循环播放用例设计 23 4.11 播放进度调节用例设计 24 第五章 设计类说明 26 5.1 设计类字典 26 第六章 测试 28 6.1 测试需求 28 6.2 测试用例 28 6.3 缺陷 35 参考文献 37 心得体会 38 第一章 引言 1.1 目的及范围 随着科技的进步,手机的功能也不断的得到丰富,从最早的"大哥大"到现在的商务手机 ,只能手机,音乐手机等等,功能也从单纯的打电话发展到了发短信,听歌,上网……手 机和我们生活的联系越来越紧密,手机的功能也是很多人们关注的焦点。 目前手机的音乐播放器已经是大家比较关注的一个部分,不少人在购买手机的时候都 会关心手机的音乐播放的能力,这也足以看出目前大家对音乐播放功能的重视,所以一 款性能良好的手机音乐播放器软件一定会受到欢迎。和传统的音乐播放器相比,手机播 放音乐更加的容易携带,其他方面也不输于传统的音乐播放器,而且还具有网上听音乐 动能,所以开发一款受欢迎的手机音乐播放器软件是具有良好的市场和应用前景。 一款专业的音乐播放器软件是手机中必不可少的要素,几乎市场上所有的手机都拥有 一个自带的音乐播放器软件,通过软件可以对音乐欣赏中一些必要的元素进行人性化的 设置,同时在操作的时候也能得到一定的便利,但是不能满足用户的所有要求,所以网 上就出现了各种各样的手机音乐播放器软件,来满足不用用户的不同要求。不同手机平 台的手机,所支持的音乐播放软件也是各不相同,而现在最流行的手机平台就是google 的android手机平台,所以本课题就致力于开发出一款基于android手机平台的音乐播放 器,来满足用户的使用需求。 1.2 术语 Android:一种以Linux为基础的开放源码操作系统,主要使用于便携设备。 SDK:Software Development Kit,即软件开发工具包,是一些被软件工程师用于为特定的软件包、软件框架、硬件平 台、操纵系统等建立应用软件的开发工具的集合。 AVD:Android Virtual Device,即Android运行的虚拟设备。 DDMS:Dalvik Debug Monitor Service,是Android开发环境中的Dalvik虚拟机调试监控服务。 Activity:Android组件中最基本的组件,在一个Android应用中,一个Activity通常就 是一个单独的屏幕。 Intent是Android中的重要组件,可以被认为是不同组件之间通信的"媒介"或者"信使" 。使用它可以启动Activity,Service还可以发起一个广播(Broadcast)。Intent对象 由Action、Data、Category、Component和Extra组成。 Service:一种
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值