安卓案例:基于网络乐库音乐播放器V1.0

目录

一、项目功能要求

二、涉及知识点

1、MVC模式

2、安卓核心组件

3、意图(Intent)

4、安卓控件

5、自定义适配器

6、游标(Cursor)

7、数组列表(ArrayList)

8、补间动画(Tween Animation)

9、菜单(Menu)

10、共享参数(SharedPreferences)

10、Java数据库连接(JDBC)

11、MySQL数据库

12、Servlet基础知识

13、JSP基础知识

三、数据库设计

1、用户表(user)结构及记录

2、音乐表(music)结构及记录

四、Web服务器端:MusicServer

1、在Intellij IDEA里创建Java项目MusicServer

2、在web目录下创建lib目录,添加项目所需jar包

3、在web目录下创建music目录,拷贝若干mp3音乐

4、在web目录下创建images目录,拷贝音乐专辑图片

5、在web目录下创建index.jsp页面

6、在部署描述文件web.xml里设置首页

7、配置Tomcat服务器

(1)打开“编辑配置”对话框

(2)在Defaults列表项里找到Tomcat Server,点开后选择local:

(3)单击【+】按钮,配置本项目的Web服务器

8、在web目录下创建登录页面login.jsp

9、创建net.hw.music.bean包,在里面创建用户实体类User

10、在net.hw.music.bean包里创建音乐实体类Music

11、创建net.hw.music.util包,在里面创建数据库连接管理类ConnectionManager

12、创建net.hw.music.dao包,在里面创建用户数据访问接口UserDao

13、在net.hw.music.dao包里创建音乐数据访问接口MusicDao

14、在net.hw.music.dao里创建impl包,在里面创建用户数据访问接口实现类UserDaoImpl

15、在net.hw.music.dao.impl包里创建音乐数据访问接口实现类MusicDaoImpl

16、创建net.hw.music.service包,在里面创建用户服务接口UserService

17、在net.hw.music.service包里创建音乐服务接口MusicService

18、在net.hw.music.service包里创建impl子包,在impl里创建用户服务接口实现类UserServiceImpl

19、在net.hw.music.service.impl包里创建音乐服务接口实现类MusicServiceImpl

20、创建net.hw.music.servlet包,在里面创建登录处理类LoginServlet

21、在net.hw.music.servlet包里,创建获取音乐列表处理类GetMusicListServlet

四、安卓客户端:WebMusicPlayerV1.0

1、创建安卓应用WebMusicPlayerV1.0

2、准备图片素材,拷贝到res下的mipmap目录

3、创建ui子包,将SplashScreenActivity拖进ui子包

4、在res里创建anim目录,在里面创建动画资源文件animator.xml

5、创建自定义边框配置文件custom_border.xml

6、在drawable目录里创建按钮背景选择器

(1)下一首按钮背景选择器(next_button_selector.xml)

(2)暂停按钮背景选择器(pause_button_selector.xml)

(3)播放按钮背景选择器(play_button_selector.xml)

(4)上一首按钮背景选择器(previous_button_selector.xml)

7、创建entity子包,在里面创建Music实体类

8、创建app子包,在里面创建应用程序常量接口AppConstants

9、修改模块的build.gradle文件

10、在app子包里创建网络音乐播放器应用程序类WebMusicPlayerApplicaton

(1)编写网络音乐播放器应用程序类WebMusicPlayerApplicaton

(2)在项目清单文件AndroidManifest.xml文件注册

11、创建adapter子包,在里面创建音乐适配器MusicAdapter

(1)在layout目录里创建音乐列表项模板music_list_item.xml

(2)编写音乐适配器MusicAdapter

12、创建service子包,在里面创建音乐播放服务类MusicPlayService

(1)编写音乐播放服务类MusicPlayService

(2)在项目清单文件里注册音乐播放服务

13、启动界面类SplashScreenActivity

(1)启动界面布局文件activity_splash_screen.xml

(2)字符串资源文件strings.xml

(3)编写启动界面类SplashScreenActivity

14、在ui子包里创建登录界面LoginActivity

(1)登录界面布局文件activity_login.xml

(2)字符串资源文件strings.xml

(3)编写登录界面类LoginActivity

15、在ui子包里创建主界面类

(1)主界面布局文件activity_main.xml

(2)在res下创建menu目录,在里面创建主界面菜单资源文件main.xml

(3)字符串资源文件strings.xml

(4)编写主界面类MainActivity

16、在项目清单文件里授权访问因特网

五、运行项目

1、启动服务器端MusicServer

2、启动安卓客户端WebMusicPlayerV1.0


一、项目功能要求

1、数据源来自服务器端音乐库(数据库保存专辑图片与音乐路径)
2、列表显示音乐文件(专辑封面图片、音乐名、演唱者、播放时长)
3、音乐列表可按音乐标识符、标题或播放时长排序
4、利用自定义Service类来实现音乐的播放与暂停以及进度的更新
5、界面类(Activity)与服务类(Service)之间通过广播接收者进行通信
6、主界面包含播放列表、可设置播放模式、显示当前音乐名、拖拽条显示和设置播放进度、显示播放进度值和播放时长、包含上一首按钮、下一首按钮、播放|暂停按钮

二、涉及知识点

1、MVC模式

MVC模式强制性地使应用程序的输入、处理和输出分开。使用MVC模式,应用程序被分成三个核心部件:模型(Model,即M)、视图(View,即V)、控制器(Controller,即C)。它们各自处理自己的任务。
 
分层架构:展现层(Activity)<——>控制层(Servlet)<——>业务层(Service)<——>模型层(Dao)<——>数据库(DB)

2、安卓核心组件

活动(Activity)、服务(Service)、广播接收者(BroadcastReceiver)

3、意图(Intent)

通过意图激活Activity和Service、发送广播。

4、安卓控件

按钮(Button)、标签(TextView)、拖拽条(SeekBar)、列表控件(ListView)、单选按钮(RadioButton)、媒体播放器(MediaPlayer)

5、自定义适配器

继承基适配器(BaseAdapter)创建自定义适配器

6、游标(Cursor)

7、数组列表(ArrayList)

8、补间动画(Tween Animation)

9、菜单(Menu)

10、共享参数(SharedPreferences)

10、Java数据库连接(JDBC)

11、MySQL数据库

12、Servlet基础知识

13、JSP基础知识

 
  • 难点1:通过异步任务从服务器端下载专辑图片并显示在列表项里。
  • 难点2:解决中文数据在网络传输过程中产生的乱码问题。

三、数据库设计

MySQL数据库music_store包含两张表:用户表(user)、音乐表(music) 

1、用户表(user)结构及记录

2、音乐表(music)结构及记录

四、Web服务器端:MusicServer

1、在Intellij IDEA里创建Java项目MusicServer

输入项目名:

打开项目结构对话框的Modules项,单击【+】按钮,增加Web支持:

单击【Create Artifact】按钮:

2、在web目录下创建lib目录,添加项目所需jar包

jar包下载链接:https://pan.baidu.com/s/16w3lPewWYjVvlIir6dYXtw  提取码:sqja

选中全部jar包,单击右键,选择“Add as Libary”菜单项:

3、在web目录下创建music目录,拷贝若干mp3音乐

4、在web目录下创建images目录,拷贝音乐专辑图片

5、在web目录下创建index.jsp页面

6、在部署描述文件web.xml里设置首页

7、配置Tomcat服务器

(1)打开“编辑配置”对话框

(2)在Defaults列表项里找到Tomcat Server,点开后选择local:

单击【Configure】按钮,配置好Tomcat的根目录:

(3)单击【+】按钮,配置本项目的Web服务器

在列表里去找Tomcat Server:

输入Tomcat Server的名称:tomcat 8.0

单击【Fix】按钮:

单击【运行】按钮:

此时,可以访问项目的静态资源(音乐专辑图片):

可以访问项目的静态资源(音乐文件):

8、在web目录下创建登录页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登录</title>
    <script type="text/javascript">
        function checkLoginForm() {
            // 获取用户名与密码文本框
            var txtusername = document.getElementById("username");
            var txtpassword = document.getElementById("password");

            // 获取用户名与密码
            var username = txtusername.value;
            var password = txtpassword.value;

            // 非空校验
            if (username == "") {
                alert("用户名不能为空!");
                txtusername.focus();
                return false;
            }
            if (password == "") {
                alert("密码不能为空!");
                txtpassword.focus();
                return false;
            }
            return true;
        }

    </script>
</head>
<body>
<h3>用户登录</h3>
<hr/>
<form id="frmLogin" action="login" method="post">
    <table border="1">
        <tr>
            <td>账号:</td>
            <td>
                <input id="username" name="username" type="text"/>
            </td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>
                <input id="password" name="password" type="password"/>
            </td>
        </tr>
        <tr align="center">
            <td colspan="2">
                <input type="submit" value="登录" οnclick="return checkLoginForm()"/>
                <input type="reset" value="重置"/>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

9、创建net.hw.music.bean包,在里面创建用户实体类User

package net.hw.music.bean;

/**
 * 用户实体类
 */
public class User {    
    private int id;
    private String username;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

10、在net.hw.music.bean包里创建音乐实体类Music

package net.hw.music.bean;

/**
 * 音乐实体类
 */
public class Music {
    /**
     * 音乐标识
     */
    private int id;
    /**
     * 音乐数据(路径+文件名)
     */
    private String data;
    /**
     * 音乐标题
     */
    private String title;
    /**
     * 演唱者
     */
    private String artist;
    /**
     * 音乐时长
     */
    private int duration;
    /**
     * 音乐专辑图片(路径+文件名)
     */
    private String album;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    @Override
    public String toString() {
        return "Music{" +
                "id=" + id +
                ", data='" + data + '\'' +
                ", title='" + title + '\'' +
                ", artist='" + artist + '\'' +
                ", duration=" + duration +
                ", album='" + album + '\'' +
                '}';
    }
}

11、创建net.hw.music.util包,在里面创建数据库连接管理类ConnectionManager

package net.hw.music.util;

import javax.swing.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {
    /**
     * 数据库驱动程序
     */
    private static final String DRIVER = "com.mysql.jdbc.Driver";
    /**
     * 数据库统一资源定位符
     */
    private static final String URL = "jdbc:mysql://localhost:3306/music_store";
    /**
     * 数据库用户名
     */
    private static final String USER = "root";
    /**
     * 数据库密码
     */
    private static final String PASSWORD = "1";

    /**
     * 私有化构造方法,禁止实例化
     */
    private ConnectionManager() {
    }

    /**
     * 获取数据库连接
     *
     * @return
     */
    public static Connection getConnection() {
        // 声明数据库连接
        Connection conn = null;

        try {
            // 安装数据库驱动程序
            Class.forName(DRIVER);
            // 获取数据库连接
            conn = DriverManager.getConnection(URL + "?useUnicode=true&characterEncoding=UTF8", USER, PASSWORD);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 返回数据库连接
        return conn;
    }

    /**
     * 关闭数据库连接
     *
     * @param conn
     */
    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                if (!conn.isClosed()) {
                    // 关闭连接
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 主方法:测试数据库连接
     *
     * @param args
     */
    public static void main(String[] args) {
        Connection conn = getConnection();
        if (conn != null) {
            JOptionPane.showMessageDialog(null, "恭喜,数据库连接成功!");
        } else {
            JOptionPane.showMessageDialog(null, "遗憾,数据库连接失败!");
        }
    }
}
运行程序,结果如下:

12、创建net.hw.music.dao包,在里面创建用户数据访问接口UserDao

package net.hw.music.dao;

import net.hw.music.bean.User;

/**
 * 用户数据访问接口
 */
public interface UserDao {
    boolean login(User user);
}

13、在net.hw.music.dao包里创建音乐数据访问接口MusicDao

package net.hw.music.dao;

import net.hw.music.bean.Music;

import java.util.List;

/**
 * 音乐数据访问接口
 */
public interface MusicDao {
    List<Music> getMusicList();
}

14、在net.hw.music.dao里创建impl包,在里面创建用户数据访问接口实现类UserDaoImpl

package net.hw.music.dao.impl;

import net.hw.music.bean.User;
import net.hw.music.dao.UserDao;
import net.hw.music.util.ConnectionManager;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 用户数据访问接口实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public boolean login(User user) {
        // 获取数据库连接
        Connection conn = ConnectionManager.getConnection();
        // 定义SQL字符串
        String strSQL = "SELECT * FROM user WHERE username = ? AND password = ?";
        try {
            // 创建预备语句对象
            PreparedStatement pstmt = conn.prepareStatement(strSQL);
            // 设置占位符的值
            pstmt.setString(1, user.getUsername());
            pstmt.setString(2, user.getPassword());
            // 执行查询,返回结果集
            ResultSet rs = pstmt.executeQuery();
            // 判断结果集里是否有记录
            if (rs.next()) {
                // 登录成功
                return true;
            }
            // 关闭结果集
            rs.close();
            // 关闭预备语句对象
            pstmt.close();
            // 关闭数据库连接
            ConnectionManager.closeConnection(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // 登录失败
        return false;
    }

    /**
     * 主方法:测试用户登录
     *
     * @param args
     */
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("admin");
        user.setPassword("12345");
        UserDao userDao = new UserDaoImpl();
        if (userDao.login(user)) {
            System.out.println("恭喜,用户[" + user.getUsername() + "]登录成功!");
        } else {
            System.out.println("遗憾,用户[" + user.getUsername() + "]登录失败!");
        }
    }
}
运行程序,结果如下:

15、在net.hw.music.dao.impl包里创建音乐数据访问接口实现类MusicDaoImpl

package net.hw.music.dao.impl;

import net.hw.music.bean.Music;
import net.hw.music.dao.MusicDao;
import net.hw.music.util.ConnectionManager;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * 音乐数据访问接口实现类
 */
public class MusicDaoImpl implements MusicDao {
    @Override
    public List<Music> getMusicList() {
        // 创建音乐列表
        List<Music> musicList = new ArrayList<>();

        // 获取数据库连接
        Connection conn = ConnectionManager.getConnection();
        // 定义SQL字符串
        String strSQL = "SELECT * FROM music";
        try {
            // 创建语句对象
            Statement stmt = conn.createStatement();
            // 执行查询返回结果集
            ResultSet rs = stmt.executeQuery(strSQL);
            // 遍历结果集
            while (rs.next()) {
                // 创建音乐实体
                Music music = new Music();
                // 利用当前记录的字段值设置音乐实体属性
                music.setId(rs.getInt("id"));
                music.setData(rs.getString("data"));
                music.setTitle(rs.getString("title"));
                music.setDuration(rs.getInt("duration"));
                music.setArtist(rs.getString("artist"));
                music.setAlbum(rs.getString("album"));
                // 将音乐实体添加到音乐列表
                musicList.add(music);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 返回音乐列表
        return musicList;
    }

    /**
     * 主方法:测试音乐列表的获取
     *
     * @param args
     */
    public static void main(String[] args) {
        MusicDao musicDao = new MusicDaoImpl();
        List<Music> musicList = musicDao.getMusicList();
        for (Music music : musicList) {
            System.out.println(music);
        }
    }
}
运行程序,结果如下:

16、创建net.hw.music.service包,在里面创建用户服务接口UserService

package net.hw.music.service;

import net.hw.music.bean.User;

/**
 * 用户服务接口
 */
public interface UserService {
    boolean login(User user);
}

17、在net.hw.music.service包里创建音乐服务接口MusicService

package net.hw.music.service;

import net.hw.music.bean.Music;

import java.util.List;

/**
 * 音乐服务接口
 */
public interface MusicService {
    List<Music> getMusicList();
}

18、在net.hw.music.service包里创建impl子包,在impl里创建用户服务接口实现类UserServiceImpl

package net.hw.music.service.impl;

import net.hw.music.bean.User;
import net.hw.music.dao.UserDao;
import net.hw.music.dao.impl.UserDaoImpl;
import net.hw.music.service.UserService;

/**
 * 用户服务接口实现类 
 */
public class UserServiceImpl implements UserService{
    private UserDao userDao;    
    
    public UserServiceImpl() {
        userDao = new UserDaoImpl();
    }
    
    @Override
    public boolean login(User user) {
        return userDao.login(user);
    }
}

19、在net.hw.music.service.impl包里创建音乐服务接口实现类MusicServiceImpl

package net.hw.music.service.impl;

import net.hw.music.bean.Music;
import net.hw.music.dao.MusicDao;
import net.hw.music.dao.impl.MusicDaoImpl;
import net.hw.music.service.MusicService;

import java.util.List;

/**
 * 音乐服务接口实现类
 */
public class MusicServiceImpl implements MusicService {
    private MusicDao musicDao;
    
    public MusicServiceImpl() {
        musicDao = new MusicDaoImpl();
    }
    
    @Override
    public List<Music> getMusicList() {
        return musicDao.getMusicList();
    }
}

20、创建net.hw.music.servlet包,在里面创建登录处理类LoginServlet

package net.hw.music.servlet;

import net.hw.music.bean.User;
import net.hw.music.service.UserService;
import net.hw.music.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置请求对象的字符编码
        request.setCharacterEncoding("utf-8");

        // 获取客户端表单提交的数据
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 定义用户
        User user = new User();
        // 设置用户属性
        user.setUsername(username);
        user.setPassword(password);

        // 创建用户服务对象
        UserService us = new UserServiceImpl();
        // 获取打印输出流
        PrintWriter out = response.getWriter();
        // 判断用户登录是否成功
        if (us.login(user)) {
            // 向客户端输出登录成功信息
            out.print("success");
            // 在服务器端输出登录成功信息
            System.out.print("恭喜,用户[" + user.getUsername() + "]登录成功!");
        } else {            
            // 向客户端输出登录失败信息
            out.print("failure");
            // 在服务器端输出登录成功信息
            System.out.print("遗憾,用户[" + user.getUsername() + "]登录失败!");
        }
        // 刷新输出流
        out.flush();
        // 关闭输出流
        out.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
}
启动服务器,访问登录页面,输入用户名和密码:

单击【登录】按钮:

查看服务器端控制台:

21、在net.hw.music.servlet包里,创建获取音乐列表处理类GetMusicListServlet

package net.hw.music.servlet;

import net.hw.music.bean.Music;
import net.hw.music.service.MusicService;
import net.hw.music.service.impl.MusicServiceImpl;
import net.sf.json.JSONArray;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * 获取音乐列表处理类
 */
@WebServlet("/getMusicList")
public class GetMusicListServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 创建音乐服务对象
        MusicService ms = new MusicServiceImpl();
        // 获取音乐列表对象
        List<Music> musicList = ms.getMusicList();
        // 将音乐列表对象转换成JSON字符串用于网络传输
        String musicListJson = JSONArray.fromObject(musicList).toString();
        // 给响应对象设置字符编码
        response.setCharacterEncoding("gbk");
        // 获取打印输出流
        PrintWriter out = response.getWriter();
        // 向客户端输出音乐列表JSON字符串
        out.print(musicListJson);
        // 刷新输出流
        out.flush();
        // 关闭输出流
        out.close();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}
启动服务器,在浏览器地址栏里访问 http://localhost:8080/getMusicList

上图显示的是JSON数组,每个数组元素都是一个JSON对象,由六个键值对组成。六个键分别是id、data、title、artist、duration、album。

四、安卓客户端:WebMusicPlayerV1.0

1、创建安卓应用WebMusicPlayerV1.0

2、准备图片素材,拷贝到res下的mipmap目录

3、创建ui子包,将SplashScreenActivity拖进ui子包

4、在res里创建anim目录,在里面创建动画资源文件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>

5、创建自定义边框配置文件custom_border.xml

在res下的drawable目录里创建自定义边框配置文件custom_border.xml,用于设置播放模式线性布局的边框:

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

    <corners android:radius="5dp" />

    <solid android:color="#eeeeee"/>

    <stroke
        android:width="1dp"
        android:color="#555" />

    <padding
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp"
        android:top="10dp" />

</shape>

6、在drawable目录里创建按钮背景选择器

(1)下一首按钮背景选择器(next_button_selector.xml)

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

    <item android:drawable="@mipmap/next_button_pressed" android:state_pressed="true"/>
    <item android:drawable="@mipmap/next_button" android:state_pressed="false"/>

</selector>

(2)暂停按钮背景选择器(pause_button_selector.xml)

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

    <item android:drawable="@mipmap/pause_button_pressed" android:state_pressed="true"/>
    <item android:drawable="@mipmap/pause_button" android:state_pressed="false"/>

</selector>

(3)播放按钮背景选择器(play_button_selector.xml)

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

    <item android:drawable="@mipmap/play_button_pressed" android:state_pressed="true"/>
    <item android:drawable="@mipmap/play_button" android:state_pressed="false"/>

</selector>

(4)上一首按钮背景选择器(previous_button_selector.xml)

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

    <item android:drawable="@mipmap/previous_button_pressed" android:state_pressed="true"/>
    <item android:drawable="@mipmap/previous_button" android:state_pressed="false"/>

</selector>

7、创建entity子包,在里面创建Music实体类

package net.hw.web_music_player.entity;

/**
 * 音乐实体类
 */
public class Music {
    /**
     * 音乐标识
     */
    private int id;
    /**
     * 音乐数据(路径+文件名)
     */
    private String data;
    /**
     * 音乐标题
     */
    private String title;
    /**
     * 演唱者
     */
    private String artist;
    /**
     * 音乐时长
     */
    private int duration;
    /**
     * 音乐专辑图片(路径+文件名)
     */
    private String album;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    @Override
    public String toString() {
        return "Music{" +
                "id=" + id +
                ", data='" + data + '\'' +
                ", title='" + title + '\'' +
                ", artist='" + artist + '\'' +
                ", duration=" + duration +
                ", album='" + album + '\'' +
                '}';
    }
}

8、创建app子包,在里面创建应用程序常量接口AppConstants

package net.hw.web_music_player.app;

/**
 * 应用程序常量接口
 *
 * Created by howard on 2018/1/27.
 */

public interface AppConstants {
    /**
     * 应用程序标记
     */
    String TAG = "net.hw.web_music_player";
    /**
     * 音乐服务器网址
     */
    String MUSIC_SERVER_URL = "http://192.168.86.100:8080";
    /**
     * 广播频道常量:播放上一首
     */
    String INTENT_ACTION_PREVIOUS = TAG + ".intent.action.PREVIOUS";
    /**
     * 广播频道常量:播放下一首
     */
    String INTENT_ACTION_NEXT = TAG + ".intent.action.NEXT";
    /**
     * 广播频道常量:播放或暂停
     */
    String INTENT_ACTION_PLAY_OR_PAUSE = TAG + ".intent.action.PLAY_OR_PAUSE";
    /**
     * 广播频道常量:播放
     */
    String INTENT_ACTION_PLAY = TAG + ".intent.action.PLAY";
    /**
     * 广播频道常量:更新播放进度
     */
    String INTENT_ACTION_UPDATE_PROGRESS = TAG + ".intent.action.UPDATE_PROGRESS";
    /**
     * 广播频道常量:用户改变播放进度
     */
    String INTENT_ACTION_USER_CHANGE_PROGRESS = TAG + ".intent.action.USER_CHANGE_PROGRESS";
    /**
     * 控制图标常量:播放或暂停
     */
    String CONTROL_ICON = "control_icon";
    /**
     * 播放时长常量
     */
    String DURATION = "duration";
    /**
     * 播放模式:顺序播放
     */
    int PLAY_MODE_ORDER = 0;
    /**
     * 播放模式:随机播放
     */
    int PLAY_MODE_RANDOM = 1;
    /**
     * 播放模式:单曲循环
     */
    int PLAY_MODE_LOOP = 2;
    /**
     * 用户配置文件名
     */
    String USER_CONFIG = "user-config";
}
注意:必须将IP地址改成你电脑的IP地址。

9、修改模块的build.gradle文件

在android元素里添加:useLibrary 'org.apache.http.legacy'

10、在app子包里创建网络音乐播放器应用程序类WebMusicPlayerApplicaton

(1)编写网络音乐播放器应用程序类WebMusicPlayerApplicaton

package net.hw.web_music_player.app;

import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

import net.hw.web_music_player.entity.Music;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * 网络音乐播放器应用程序类
 *
 * Created by howard on 2018/1/27.
 */

public class WebMusicPlayerApplication extends Application implements AppConstants {
    /**
     * 简单日期格式
     */
    private SimpleDateFormat sdf;
    /**
     * 音乐列表
     */
    private List<Music> musicList;
    /**
     * 专辑图片数组
     */
    private Bitmap[] albums;
    /**
     * 专辑图片索引
     */
    private int albumIndex;
    /**
     * 当前音乐索引
     */
    private int currentMusicIndex;
    /**
     * 音乐当前播放位置
     */
    private int currentPosition;
    /**
     * 播放模式
     */
    private int playMode;
    /**
     * 用户修改的播放进度
     */
    private int progressChangedByUser;

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建简单日期格式对象
        sdf = new SimpleDateFormat("mm:ss");
        // 定义获取音乐列表的网址
        String strGetMusicListUrl = MUSIC_SERVER_URL + "/getMusicList";
        // 执行获取音乐列表异步任务,传入一个参数:获取音乐列表的网址
        new GetMusicListTask().execute(strGetMusicListUrl);
    }
    
    

    /**
     * 获取音乐列表异步任务类
     */
    private class GetMusicListTask extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params) {
            // 定义结果字符串
            String result = "";
            // 创建get请求
            HttpGet request = new HttpGet(params[0]);
            // 创建http客户端
            HttpClient client = new DefaultHttpClient();
            try {
                // 执行get请求,返回响应对象
                HttpResponse response = client.execute(request);
                // 根据响应对象里的状态码判断是否请求成功
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    // 获取响应数据实体
                    HttpEntity entity = response.getEntity();
                    // 将响应数据实体转换成字符串作为返回值
                    result = EntityUtils.toString(entity, "gbk");
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

            // 返回结果字符串
            return result;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            // 判断结果是否为空
            if (!result.equals("")) {
                /* 将获取json字符串转换成数组列表 */
                // 创建音乐列表
                musicList = new ArrayList<>();
                try {
                    // 基于json字符串创建json数组
                    JSONArray array = new JSONArray(result);
                    // 创建专辑图片数组
                    albums = new Bitmap[array.length()];
                    // 创建json对象
                    JSONObject object;
                    // 遍历json数组
                    for (int i = 0; i < array.length(); i++) {
                        // 获取json数组元素
                        object = array.getJSONObject(i);
                        // 创建音乐实体
                        Music music = new Music();
                        // 设置音乐实体属性
                        music.setId(object.getInt("id"));
                        music.setData(object.getString("data"));
                        music.setTitle(object.getString("title"));
                        music.setArtist(object.getString("artist"));
                        music.setDuration(object.getInt("duration"));
                        music.setAlbum(object.getString("album"));
                        // 执行获取专辑图片异步任务,传入两个参数(专辑图片索引,专辑图片路径)
                        new GetAlbumTask().execute(String.valueOf(i), music.getAlbum());
                        // 将音乐实体添加到音乐列表
                        musicList.add(music);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取专辑图片异步任务
     */
    private class GetAlbumTask extends AsyncTask<String, Void, Void> {
        @Override
        protected Void doInBackground(String... params) {
            // 获取专辑图片索引
            albumIndex = Integer.parseInt(params[0]);
            // 获取专辑图片路径
            String albumPath = params[1];
            // 声明URL连接
            HttpURLConnection conn = null;
            // 声明位图对象
            Bitmap bitmap = null;
            try {
                // 定义URL对象
                URL url = new URL(MUSIC_SERVER_URL + albumPath);
                // 打开URL连接
                conn = (HttpURLConnection) url.openConnection();
                // 获取响应码
                int responseCode = conn.getResponseCode();
                // 根据响应码执行不同操作
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    // 由连接获取字节输入流
                    InputStream in = conn.getInputStream();
                    // 利用位图工厂生成位图对象
                    bitmap = BitmapFactory.decodeStream(in);
                } else {
                    Log.d(TAG, "没有得到响应数据。");
                }
                // 设置专辑图片数组元素值
                albums[albumIndex] = bitmap;

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
        }
    }

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

    public List<Music> getMusicList() {
        return musicList;
    }

    public Bitmap[] getAlbums() {
        return albums;
    }

    public int getCurrentMusicIndex() {
        return currentMusicIndex;
    }

    public void setCurrentMusicIndex(int currentMusicIndex) {
        this.currentMusicIndex = currentMusicIndex;
    }

    public int getCurrentPosition() {
        return currentPosition;
    }

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

    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;
    }
}

(2)在项目清单文件AndroidManifest.xml文件注册

11、创建adapter子包,在里面创建音乐适配器MusicAdapter

(1)在layout目录里创建音乐列表项模板music_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp" >

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="10dp"
        android:src="@mipmap/music" />

    <TextView
        android:id="@+id/tv_music_name"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_alignTop="@+id/iv_icon"
        android:layout_toRightOf="@+id/iv_icon"
        android:text="Someday The Dream Will End"
        android:textColor="#000000"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/tv_duration"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_alignBaseline="@+id/tv_music_name"
        android:layout_alignParentRight="true"
        android:text="04:34"
        android:textColor="#aaaaaa"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/tv_artist"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_alignLeft="@+id/tv_music_name"
        android:layout_below="@+id/tv_music_name"
        android:layout_marginTop="10dp"
        android:text="Nubuo Uematsu"
        android:textColor="#0000ff"
        android:textSize="15sp" />

</RelativeLayout>

(2)编写音乐适配器MusicAdapter

package net.hw.web_music_player.adapter;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import net.hw.web_music_player.R;
import net.hw.web_music_player.app.AppConstants;
import net.hw.web_music_player.app.WebMusicPlayerApplication;
import net.hw.web_music_player.entity.Music;

import java.util.List;

/**
 * 音乐适配器
 *
 * Created by howard on 2018/1/24.
 */

public class MusicAdapter extends BaseAdapter implements AppConstants {
    /**
     * 上下文环境
     */
    private Context context;
    /**
     * 音乐列表
     */
    private List<Music> musicList;
    /**
     * 访问网络乐库应用程序
     */
    private WebMusicPlayerApplication app;

    /**
     * 构造方法
     *
     * @param context
     * @param musicList
     */
    public MusicAdapter(Context context, List<Music> musicList) {
        this.context = context;
        this.musicList = musicList;
        // 获取访问网络乐库应用程序对象
        app = (WebMusicPlayerApplication) ((Activity) context).getApplication();
    }

    /**
     * 获取列表项个数
     */
    @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;

        // 判断转换视图是否为空
        if (convertView == null) {
            // 将音乐列表项模板映射成转换视图
            convertView = LayoutInflater.from(context).inflate(R.layout.music_list_item, null);
            // 实例化视图容器
            holder = new ViewHolder();
            // 获取视图容器各控件实例
            holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
            holder.tvMusicName = (TextView) convertView.findViewById(R.id.tv_music_name);
            holder.tvDuration = (TextView) convertView.findViewById(R.id.tv_duration);
            holder.tvArtist = (TextView) convertView.findViewById(R.id.tv_artist);
            // 将视图容器附加到转换视图
            convertView.setTag(holder);
        } else {
            // 从转换视图里获取视图容器
            holder = (ViewHolder) convertView.getTag();
        }

        // 获取音乐实体作为数据源
        Music music = musicList.get(position);
        // 设置音乐专辑图片
        if (app.getAlbums()[position] != null) {
            holder.ivIcon.setImageBitmap(app.getAlbums()[position]);
        } else {
            holder.ivIcon.setImageResource(R.mipmap.music);
        }
        // 设置音乐名
        holder.tvMusicName.setText(music.getTitle());
        // 设置演唱者
        holder.tvArtist.setText(music.getArtist());
        // 设置音乐时长
        holder.tvDuration.setText(app.getFormatTime(music.getDuration()));
        // 返回转换视图
        return convertView;
    }

    /**
     * 视图容器
     */
    private static class ViewHolder {
        ImageView ivIcon;
        TextView tvMusicName;
        TextView tvDuration;
        TextView tvArtist;
    }
}

12、创建service子包,在里面创建音乐播放服务类MusicPlayService

(1)编写音乐播放服务类MusicPlayService

package net.hw.web_music_player.service;

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 net.hw.web_music_player.R;
import net.hw.web_music_player.app.AppConstants;
import net.hw.web_music_player.app.WebMusicPlayerApplication;
import net.hw.web_music_player.entity.Music;

import java.io.IOException;
import java.util.List;
import java.util.Random;

/**
 * 音乐服务类
 *
 * Created by howard on 2018/1/27.
 */

public class MusicPlayService extends Service implements AppConstants {
    /**
     * 媒体播放器
     */
    private MediaPlayer mp;
    /**
     * 音乐列表(数据源)
     */
    private List<Music> musicList;
    /**
     * 音乐文件名
     */
    private String musicName;
    /**
     * 更新音乐播放进度的线程
     */
    private Thread thread;
    /**
     * 线程循环控制变量
     */
    private boolean isRunning;
    /**
     * 音乐播放器应用程序
     */
    private WebMusicPlayerApplication app;
    /**
     * 音乐广播接收器
     */
    private MusicReceiver receiver;

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

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

        // 获取音乐列表(数据源)
        musicList = app.getMusicList();

        // 创建媒体播放器
        mp = new MediaPlayer();
        // 给媒体播放器起注册完成监听器
        mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                // 切换到下一首音乐
                nextMusic();
            }
        });


        // 设置线程循环控制变量为真
        isRunning = true;
        // 创建线程更新播放进度
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRunning) {
                    // 判断音乐是否在播放
                    if (mp.isPlaying()) {
                        // 设置音乐当前播放位置
                        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.pause_button_selector);
                        // 按意图发送广播
                        sendBroadcast(intent);
                    }
                    // 让线程睡眠500毫秒
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 启动线程
        thread.start();

        // 创建音乐广播接收者
        receiver = new MusicReceiver();
        // 创建意图过滤器
        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) {
        // 返回非粘性服务
        return Service.START_NOT_STICKY;
    }

    /**
     * 上一首音乐
     */
    private void previousMusic() {
        // 更新音乐索引
        if (app.getCurrentMusicIndex() > 0) {
            app.setCurrentMusicIndex(app.getCurrentMusicIndex() - 1);
        } else {
            app.setCurrentMusicIndex(musicList.size() - 1);
        }
        // 当前播放位置归零
        app.setCurrentPosition(0);
        // 调用播放方法
        play();
    }

    /**
     * 下一首音乐
     */
    private void nextMusic() {
        // 根据播放模式来更新音乐索引
        switch (app.getPlayMode()) {
            // 顺序播放模式
            case PLAY_MODE_ORDER:
                if (app.getCurrentMusicIndex() < musicList.size() - 1) {
                    app.setCurrentMusicIndex(app.getCurrentMusicIndex() + 1);
                } else {
                    app.setCurrentMusicIndex(0);
                }
                break;
            // 随机播放模式
            case PLAY_MODE_RANDOM:
                // 随机设置索引
                app.setCurrentMusicIndex(new Random().nextInt(app.getMusicList().size()));
                break;
            // 单曲循环模式
            case PLAY_MODE_LOOP:
                // 音乐索引保持不变
                break;
        }
        // 当前播放位置归零
        app.setCurrentPosition(0);
        // 调用播放方法
        play();
    }

    /**
     * 播放方法
     */
    private void play() {
        try {
            // 重置播放器
            mp.reset();
            // 获取当前播放的音乐名
            musicName = musicList.get(app.getCurrentMusicIndex()).getData();
            // 设置播放源
            mp.setDataSource(MUSIC_SERVER_URL + musicName);
            // 缓冲播放源,加载到内存
            mp.prepare();
            // 定位到暂停时的播放位置
            mp.seekTo(app.getCurrentPosition());
            // 启动音乐的播放
            mp.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停方法
     */
    private void pause() {
        // 暂停播放
        mp.pause();
        // 保存当前音乐播放位置
        app.setCurrentPosition(mp.getCurrentPosition());
        /* 发送广播给前台MainActivity,更改图标、更改播放进度 */
        // 创建意图
        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 MusicReceiver 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;
                }
            }
        }
    }

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

(2)在项目清单文件里注册音乐播放服务

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.hw.web_music_player">

    <application
        android:name=".app.WebMusicPlayerApplication"
        android:allowBackup="true"
        android:icon="@mipmap/app_icon"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ui.SplashScreenActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".service.MusicPlayService" />
    </application>

</manifest>

13、启动界面类SplashScreenActivity

(1)启动界面布局文件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="@mipmap/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="@mipmap/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>

(2)字符串资源文件strings.xml

<resources>
    <string name="app_name">基于网络乐库音乐播放器V1.0</string>
    <string name="title">基于网络乐库音乐播放器</string>
    <string name="version">Version 1.0</string>
    <string name="author">泸职院信息系·华卫</string>
</resources>

(3)编写启动界面类SplashScreenActivity

package net.hw.web_music_player.ui;

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.hw.web_music_player.R;
/**
 * 启动界面类
 *
 * Created by howard on 2018/1/27.
 */

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, LoginActivity.class));
                // 关闭启动界面
                finish();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }
}

14、在ui子包里创建登录界面LoginActivity

(1)登录界面布局文件activity_login.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:background="@mipmap/background"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="10dp" >

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

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="10dp"
        android:background="#bbbbbb" />

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

        <TextView
            android:id="@+id/tv_user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/username"
            android:textColor="#000000"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/edt_username"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="@string/input_username"
            android:singleLine="true" />
    </LinearLayout>

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

        <TextView
            android:id="@+id/tv_password"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/password"
            android:textColor="#000000"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/edt_password"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="@string/input_password"
            android:inputType="textPassword"
            android:singleLine="true" />
    </LinearLayout>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="20dp"
        android:background="#bbbbbb" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="doLogin"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            android:text="@string/login"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn_cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="doCancel"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            android:text="@string/cancel"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

(2)字符串资源文件strings.xml

<resources>
    <string name="app_name">基于网络乐库音乐播放器V1.0</string>
    <string name="title">基于网络乐库音乐播放器</string>
    <string name="version">Version 1.0</string>
    <string name="author">泸职院信息系·华卫</string>
    <string name="user_login">用户登录</string>
    <string name="username">账号:</string>
    <string name="password">密码:</string>
    <string name="login">登录</string>
    <string name="cancel">取消</string>
    <string name="input_username">输入账号</string>
    <string name="input_password">输入密码</string>
</resources>

(3)编写登录界面类LoginActivity

package net.hw.access_web_music_store.ui;

import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import net.hw.access_web_music_store.R;
import net.hw.access_web_music_store.app.AppConstants;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

public class LoginActivity extends Activity implements AppConstants {
    /**
     * 用户名编辑框
     */
    private EditText edtUsername;
    /**
     * 密码编辑框
     */
    private EditText edtPassword;

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

        // 通过资源标识获得控件实例
        edtUsername = findViewById(R.id.edt_username);
        edtPassword = findViewById(R.id.edt_password);
    }

    /**
     * 登录按钮单击事件处理方法
     *
     * @param view
     */
    public void doLogin(View view) {
        // 获取用户名
        String username = edtUsername.getText().toString().trim();
        // 获取密码
        String password = edtPassword.getText().toString().trim();

        // 用户名非空校验
        if (username.equals("")) {
            Toast.makeText(this, "用户名不能为空!", Toast.LENGTH_SHORT).show();
            edtUsername.setFocusable(true);
            edtUsername.requestFocus();
            return;
        }

        // 密码非空校验
        if (password.equals("")) {
            Toast.makeText(this, "密码不能为空!", Toast.LENGTH_SHORT).show();
            edtPassword.setFocusable(true);
            edtPassword.requestFocus();
            return;
        }

        // 定义登录网址字符串
        String strLoginUrl = MUSIC_SERVER_URL + "/login";
        // 执行登录异步任务,传入三个参数
        new LoginTask().execute(strLoginUrl, username, password);
    }

    /**
     * 登录异步任务
     */
    private class LoginTask extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params) {
            // 定义结果字符串
            String result = "";

            // 获取登录网址字符串
            String strLoginUrl = params[0];
            // 创建post请求
            HttpPost request = new HttpPost(strLoginUrl);
            // 创建名值对列表
            List<NameValuePair> list = new ArrayList<>();
            // 将用户提交的数据添加到名值对列表
            list.add(new BasicNameValuePair("username", params[1]));
            list.add(new BasicNameValuePair("password", params[2]));
            try {
                // 将名值对列表封装成url编码格式实体作为请求参数
                request.setEntity(new UrlEncodedFormEntity(list, "utf-8"));
                // 创建http客户端
                HttpClient client = new DefaultHttpClient();
                // 执行post请求,获取响应对象
                HttpResponse response = client.execute(request);
                // 如果请求成功
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    // 获取响应数据实体
                    HttpEntity entity = response.getEntity();
                    // 将响应数据实体转换成字符串保存到结果字符串里
                    result = EntityUtils.toString(entity);
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return result;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            // 根据响应结果执行不同操作
            if (result.trim().equals("success")) {
                // 跳转到主界面
                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                // 关闭登录界面
                finish();
            } else {
                Toast.makeText(LoginActivity.this, "用户名或密码错误,登录失败!", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**
     * 取消按钮单击事件处理方法
     *
     * @param view
     */
    public void doCancel(View view) {
        edtUsername.setText("");
        edtPassword.setText("");
        edtUsername.requestFocus();
    }
}

15、在ui子包里创建主界面类

(1)主界面布局文件activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/background"
    android:orientation="vertical"
    android:padding="10dp">

    <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/lv_music_list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:background="#aaaaaa" />

    <TextView
        android:id="@+id/tv_music_name"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="0.5"
        android:textColor="#0000ff"
        android:textSize="16sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:orientation="horizontal">

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

        <SeekBar
            android:id="@+id/sb_music_progress"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="6" />

        <TextView
            android:id="@+id/tv_duration"
            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="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_previous"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/previous_button_selector"
            android:onClick="doPrevious" />

        <Button
            android:id="@+id/btn_play_pause"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/play_button_selector"
            android:onClick="doPlayOrPause" />

        <Button
            android:id="@+id/btn_next"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:background="@drawable/next_button_selector"
            android:onClick="doNext" />
    </LinearLayout>

</LinearLayout>

(2)在res下创建menu目录,在里面创建主界面菜单资源文件main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_settings"
        android:icon="@mipmap/order_mode"
        android:orderInCategory="100"
        android:title=""
        android:showAsAction="always">
        <menu>
            <item
                android:id="@+id/action_default_order"
                android:icon="@mipmap/default_order"
                android:checked="true"
                android:showAsAction="always"
                android:title="@string/default_order"/>
            <item
                android:id="@+id/action_title_order"
                android:icon="@mipmap/title_order"
                android:showAsAction="always"
                android:title="@string/title_order"/>
            <item
                android:id="@+id/action_duration_order"
                android:icon="@mipmap/duration_order"
                android:showAsAction="always"
                android:title="@string/duration_order"/>
        </menu>
    </item>

</menu>

(3)字符串资源文件strings.xml

<resources>
    <string name="app_name">基于网络乐库音乐播放器V1.0</string>
    <string name="title">基于网络乐库音乐播放器</string>
    <string name="version">Version 1.0</string>
    <string name="author">泸职院信息系·华卫</string>
    <string name="user_login">用户登录</string>
    <string name="username">账号:</string>
    <string name="password">密码:</string>
    <string name="login">登录</string>
    <string name="cancel">取消</string>
    <string name="input_username">输入账号</string>
    <string name="input_password">输入密码</string>
    <string name="play_mode">播放模式:</string>
    <string name="order">顺序</string>
    <string name="loop">单曲循环</string>
    <string name="random">随机</string>    
     <string name="default_order">默认排序</string>
     <string name="title_order">按标题排序</string>
     <string name="duration_order">按时长排序</string>
</resources>

(4)编写主界面类MainActivity

package net.hw.web_music_player.ui;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import net.hw.web_music_player.R;
import net.hw.web_music_player.adapter.MusicAdapter;
import net.hw.web_music_player.app.AppConstants;
import net.hw.web_music_player.app.WebMusicPlayerApplication;
import net.hw.web_music_player.entity.Music;
import net.hw.web_music_player.service.MusicPlayService;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MainActivity extends Activity implements AppConstants {
    /**
     * 音乐文件名
     */
    private String musicName;
    /**
     * 显示音乐名的标签
     */
    private TextView tvMusicName;
    /**
     * 播放|暂停按钮
     */
    private Button btnPlayOrPause;
    /**
     * 显示当前播放位置的标签
     */
    private TextView tvCurrentPosition;
    /**
     * 显示音乐播放时长的标签
     */
    private TextView tvDuration;
    /**
     * 音乐播放拖拽条
     */
    private SeekBar sbMusicProgress;
    /**
     * 音乐名列表控件
     */
    private ListView lvMusicList;
    /**
     * 音乐列表(数据源)
     */
    private List<Music> musicList;
    /**
     * 音乐适配器
     */
    private MusicAdapter adapter;
    /**
     * 网络音乐播放器应用程序对象
     */
    private WebMusicPlayerApplication app;
    /**
     * 音乐广播接收器
     */
    private MusicReceiver receiver;
    /**
     * 播放模式单选按钮组
     */
    private RadioGroup rgPlayMode;

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

        // 通过资源标识获得控件实例
        lvMusicList = findViewById(R.id.lv_music_list);
        tvMusicName = findViewById(R.id.tv_music_name);
        btnPlayOrPause = findViewById(R.id.btn_play_pause);
        tvCurrentPosition = findViewById(R.id.tv_current_position);
        tvDuration = findViewById(R.id.tv_duration);
        sbMusicProgress = findViewById(R.id.sb_music_progress);
        rgPlayMode = findViewById(R.id.rg_play_mode);

        // 创建网络音乐播放器应用程序对象
        app = (WebMusicPlayerApplication) getApplication();

        // 获得音乐列表作为数据源
        musicList = app.getMusicList();
        // 判断网络乐库里是否有音乐
        if (musicList != null) {
            // 创建音乐适配器
            adapter = new MusicAdapter(this, musicList);
            // 列表控件设置适配器
            lvMusicList.setAdapter(adapter);

            // 获取当前音乐文件名(完整路径)
            musicName = app.getMusicList().get(app.getCurrentMusicIndex()).getData();
            // 获取当前音乐时长
            int duration = app.getMusicList().get(app.getCurrentMusicIndex()).getDuration();

            // 设置正在播放文件名(去掉扩展名)
            tvMusicName.setText("No." + (app.getCurrentMusicIndex() + 1) + "  "
                    + musicName.substring(musicName.lastIndexOf("/") + 1,
                    musicName.lastIndexOf(".")));
            // 设置播放当前位置
            tvCurrentPosition.setText(app.getFormatTime(0));
            // 设置音乐时长
            tvDuration.setText(app.getFormatTime(duration));

            // 启动音乐播放服务
            startService(new Intent(MainActivity.this, MusicPlayService.class));

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

            // 给播放模式单选按钮组注册监听器
            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;
                    }
                }
            });

            // 给音乐播放拖拽条注册监听器
            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);
        } else {
            Toast.makeText(this, "网络乐库里没有音乐文件!", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 创建选项菜单
     *
     * @param menu
     * @return
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            // 默认排序
            case R.id.action_default_order:
                Collections.sort(app.getMusicList(), new Comparator<Music>() {

                    @Override
                    // lhs: left-hand-side; rhs: right-hand-side
                    public int compare(Music lhs, Music rhs) {
                        return (int) (lhs.getId() - rhs.getId());
                    }
                });

                saveDefaultOrderBy("default");
                break;
            // 按标题排序
            case R.id.action_title_order:
                Collections.sort(app.getMusicList(), new Comparator<Music>() {

                    @Override
                    // lhs: left-hand-side; rhs: right-hand-side
                    public int compare(Music lhs, Music rhs) {
                        return lhs.getTitle().compareTo(rhs.getTitle());
                    }
                });

                saveDefaultOrderBy("title");
                break;
            // 按时长排序
            case R.id.action_duration_order:
                Collections.sort(app.getMusicList(), new Comparator<Music>() {

                    @Override
                    // lhs: left-hand-side; rhs: right-hand-side
                    public int compare(Music lhs, Music rhs) {
                        return (int) (lhs.getDuration() - rhs.getDuration());
                    }
                });
                saveDefaultOrderBy("duration");
                break;
        }

        // 更新列表控件
        adapter.notifyDataSetInvalidated();

        return true;
    }

    /**
     * 保存个性化设置(偏好设置):默认的列表排序方法
     *
     * @param orderBy
     *            列表排序方法
     */
    private void saveDefaultOrderBy(String orderBy) {
        // 获得共享参数对象
        SharedPreferences sp = getSharedPreferences(USER_CONFIG, MODE_PRIVATE);
        // 获得共享参数的编辑器
        SharedPreferences.Editor editor = sp.edit();
        // 存放数据
        editor.putString("order-by", orderBy);
        // 提交数据
        editor.commit();
    }


    /**
     * 上一首音乐按钮单击事件处理方法
     *
     * @param view
     */
    public void doPrevious(View view) {
        // 创建意图
        Intent intent = new Intent();
        // 设置广播频道
        intent.setAction(INTENT_ACTION_PREVIOUS);
        // 按意图发送广播
        sendBroadcast(intent);
    }

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

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

    /**
     * 音乐广播接收者
     */
    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.getCurrentMusicIndex()).getData();
                    // 设置正在播放的文件名(去掉扩展名)
                    tvMusicName.setText("No." + (app.getCurrentMusicIndex() + 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);
                }
            }
        }
    }

    /**
     * 销毁方法
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止音乐播放服务
        stopService(new Intent(MainActivity.this, MusicPlayService.class));
        // 注销广播接收者
        unregisterReceiver(receiver);
    }
}

16、在项目清单文件里授权访问因特网

五、运行项目

1、启动服务器端MusicServer

2、启动安卓客户端WebMusicPlayerV1.0


猜你喜欢

转载自blog.csdn.net/howard2005/article/details/79337588