目录
1、在Intellij IDEA里创建Java项目MusicServer
(2)在Defaults列表项里找到Tomcat Server,点开后选择local:
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
3、创建ui子包,将SplashScreenActivity拖进ui子包
4、在res里创建anim目录,在里面创建动画资源文件animator.xml
5、创建自定义边框配置文件custom_border.xml
8、创建app子包,在里面创建应用程序常量接口AppConstants
10、在app子包里创建网络音乐播放器应用程序类WebMusicPlayerApplicaton
(1)编写网络音乐播放器应用程序类WebMusicPlayerApplicaton
(2)在项目清单文件AndroidManifest.xml文件注册
11、创建adapter子包,在里面创建音乐适配器MusicAdapter
(1)在layout目录里创建音乐列表项模板music_list_item.xml
12、创建service子包,在里面创建音乐播放服务类MusicPlayService
(1)启动界面布局文件activity_splash_screen.xml
(3)编写启动界面类SplashScreenActivity
一、项目功能要求
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="登录" onclick="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、在项目清单文件里授权访问因特网
五、运行项目