目錄
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、MVC模式
2、安卓核心組件
活動(Activity)、服務(Service)、廣播接收者(BroadcastReceiver)
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:通過異步任務從服務器端下載專輯圖片並顯示在列表項裏。
- 難點2:解決中文數據在網絡傳輸過程中產生的亂碼問題。
三、數據庫設計
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);
}
}
上圖顯示的是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";
}
9、修改模塊的build.gradle文件
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、在項目清單文件裏授權訪問因特網
五、運行項目