基於android的網路音樂播放器-網路音樂的搜尋和展示(五)
作為android初學者,最近把瘋狂android講義和瘋狂Java講義看了一遍,看到書中介紹的知識點非常多,很難全部記住,為了更好的掌握基礎知識點,我將開發一個網路音樂播放器-EasyMusic來鞏固下,也當作是練練手。感興趣的朋友可以看看,有設計不足的地方也歡迎指出。
開發之前首先介紹下該音樂播放器將要開發的功能(需求):
1.本地音樂的載入和播放;
2.網路音樂的搜尋,試聽和下載;
3.音樂的斷點下載;
4.點選播放圖示載入專輯圖片,點選歌詞載入歌詞並滾動顯示(支援滑動歌詞改變音樂播放進度);
5.支援基於popupWindow的彈出式選單;
6.支援後臺工作列顯示和控制。
該篇主要是介紹在NetFragment中實現網路音樂的搜尋和展示功能開發:
先看下效果圖如下
首先介紹下大概的實現思路:NetFragment佈局頂部為搜尋框SearchView,下面為一個ListView用來顯示搜尋到的網路音樂
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height ="match_parent"
android:orientation="vertical" >
<SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f0f" >
</SearchView>
<ListView
android:id="@+id/netmusiclist"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
搜尋框中輸入搜尋關鍵字並點選搜尋(軟鍵盤最後一個搜尋鍵),為了避免在主執行緒執行耗時任務,搜尋時建立一個非同步任務(NetFragment的內部類)執行搜尋任務(包括建立Http連線,解析json資料)。非同步任務的定義如下:
// 負責搜尋音樂的非同步任務,搜尋完成後顯示網路音樂列表
private class SearchMusicTask extends AsyncTask<String, Void, Void> {
private ListView musicList;
public SearchMusicTask(ListView musicList) {
this.musicList = musicList;
}
protected void onPreExecute() {
super.onPreExecute();
}
protected Void doInBackground(String... params) {
String url = params[0];
try {
HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
conn.setConnectTimeout(5000);
//使用快取提高處理效率
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
//網路響應賦值給成員變數searchResponse
searchResponse = sb.toString();
parseResponse();
Log.d(TAG, "searchResponse = " + searchResponse);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
//adapter資料更新後通知列表更新
netMusicListAdapter.notifyDataSetChanged();
//musicList.setAdapter(netMusicListAdapter);
}
//json解析網路響應
private void parseResponse() {
try {
JSONObject response = new JSONObject(searchResponse);
JSONObject result = response.getJSONObject("result");
JSONArray songs = result.getJSONArray("songs");
if (netMusicList.size() > 0) netMusicList.clear();
for (int i = 0; i < songs.length(); i++) {
JSONObject song = songs.getJSONObject(i);
//獲取歌曲名字
String title = song.getString("name");
//獲取歌詞演唱者
String artist = song.getJSONArray("artists")
.getJSONObject(0).getString("name");
//獲取歌曲專輯圖片的url
String albumPicUrl = song.getJSONObject("album").getString(
"picUrl");
//獲取歌曲音訊的url
String audioUrl = song.getString("audio");
Log.d(TAG, "doenloadUrl = " + audioUrl);
//儲存音樂資訊的Map
Map<String, Object> item = new HashMap<>();
item.put("title", title);
item.put("artist", artist);
item.put("picUrl", albumPicUrl);
picUrlMap.put(title + artist, new SoftReference<String>(
albumPicUrl));
item.put("audio", audioUrl);
//將一條歌曲資訊存入list中
netMusicList.add(item);
}
Log.d(TAG, "搜到" + netMusicList.size() + "首歌");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
非同步任務在執行之前會呼叫onPreExecute(這裡可以進行一些初始化的操作),然後呼叫核心方法doInBackground(該方法執行在獨立的執行緒中,耗時操作往往放在該方法裡),當doInBackground執行完成後會回撥onPostExecute方法,當執行完任務在該方法中更新UI,值得指出來的是非同步任務的onPreExecute,onProgressUpdate(該方法通常用在需要實時更新任務進度的場景),onPostExecute都是執行在主執行緒中。該非同步任務的主要難點是json資料的解析上。json資料的解析可以參考我的另一篇部落格–json資料的解析。
根據搜尋得到網路音樂列表後應該考慮如下進行下載了,在非同步任務裡我們已經將音樂的音訊url儲存起來了,下一步只需要根據點選的歌曲取出相應的url建立Http連線並取出輸入流檔案-音訊檔案。該篇主要介紹網路音樂的搜尋和展示,下載任務的建立和下載列表的更新將放在下一篇中將。下面附上NetFragment.java的完整程式碼。
package com.sprd.easymusic.fragment;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.sprd.easymusic.MainActivity;
import com.sprd.easymusic.R;
import com.sprd.easymusic.util.DownloadTask;
import com.sprd.easymusic.util.DownloadUtil;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.support.v4.app.Fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.Toast;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
public class NetFragment extends Fragment {
private final String TAG = "NetFragment";
//音樂搜尋介面
public static final String CLOUD_MUSIC_API_PREFIX = "http://s.music.163.com/search/get/?";
private Context mContext;
//獲取輸入的搜尋框
private SearchView searchView;
//根據關鍵字搜尋到的音樂列表
private ListView netMusicListView;
//存放搜尋到的音樂資訊的list
private List<Map<String, Object>> netMusicList = new ArrayList<>();
//獲取的json資料格式的網路響應賦值給searchResponse
private String searchResponse = null;
private LayoutInflater inflater;
//快取專輯圖片的下載連結,當下載完成並播放歌曲時,點選播放圖示載入專輯圖示時避免重新聯網搜尋,直接從快取中獲取
private Map<String, SoftReference<String>> picUrlMap = new HashMap<String, SoftReference<String>>();
public void onAttach(Activity activity) {
super.onAttach(activity);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this.getActivity();
inflater = LayoutInflater.from(mContext);
Log.d(TAG, "onCreate");
}
// NetFragment向外界展示的內容,返回值為view
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.netmusic, container, false);
netMusicListView = (ListView) view.findViewById(R.id.netmusiclist);
netMusicListView.setAdapter(netMusicListAdapter);
netMusicListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//點選搜尋到的網路音樂列表的第position項即可建立下載任務下載對應歌曲,下一篇介紹
//createDownloadTask(position);
}
});
searchView = (SearchView) view.findViewById(R.id.searchView);
//搜尋框監聽,點選搜尋建立非同步任務執行搜尋任務
searchView.setOnQueryTextListener(new OnQueryTextListener() {
public boolean onQueryTextSubmit(String query) {
Toast.makeText(mContext, "搜尋內容為:" + query, 100).show();
String musicUrl = getRealUrl(query);
new SearchMusicTask(netMusicListView).execute(musicUrl);
return false;
}
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnCloseListener(new OnCloseListener() {
public boolean onClose() {
netMusicListView.setAdapter(null);
// netMusicList.clear();adapter.notifyDataSetChanged();
return false;
}
});
return view;
}
// 網路音樂列表介面卡
private BaseAdapter netMusicListAdapter = new BaseAdapter() {
@Override
public int getCount() {
return netMusicList.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView,
ViewGroup parent) {
View view = convertView;
Map<String, Object> item = netMusicList.get(position);
if (convertView == null) {
view = inflater.inflate(R.layout.netmusiclist_item, null);
}
TextView musicTitle = (TextView) view.findViewById(R.id.musicTitle);
musicTitle.setTag("title");
TextView musicArtist = (TextView) view.findViewById(R.id.musicArtist);
musicTitle.setText((String) item.get("title"));
musicArtist.setText((String) item.get("artist"));
return view;
}
};
//返回可以訪問的網路資源
private String getRealUrl(String query) {
String key = null;
try {
key = URLEncoder.encode(query, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return CLOUD_MUSIC_API_PREFIX + "type=1&s='" + key
+ "'&limit=20&offset=0";
}
public void onDestroy() {
super.onDestroy();
}
// 負責搜尋音樂的非同步任務,搜尋完成後顯示網路音樂列表
private class SearchMusicTask extends AsyncTask<String, Void, Void> {
private ListView musicList;
public SearchMusicTask(ListView musicList) {
this.musicList = musicList;
}
protected void onPreExecute() {
super.onPreExecute();
}
protected Void doInBackground(String... params) {
String url = params[0];
try {
HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
conn.setConnectTimeout(5000);
//使用快取提高處理效率
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
//網路響應賦值給成員變數searchResponse
searchResponse = sb.toString();
parseResponse();
Log.d(TAG, "searchResponse = " + searchResponse);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
//adapter資料更新後通知列表更新
netMusicListAdapter.notifyDataSetChanged();
//musicList.setAdapter(netMusicListAdapter);
}
//json解析網路響應
private void parseResponse() {
try {
JSONObject response = new JSONObject(searchResponse);
JSONObject result = response.getJSONObject("result");
JSONArray songs = result.getJSONArray("songs");
if (netMusicList.size() > 0) netMusicList.clear();
for (int i = 0; i < songs.length(); i++) {
JSONObject song = songs.getJSONObject(i);
//獲取歌曲名字
String title = song.getString("name");
//獲取歌詞演唱者
String artist = song.getJSONArray("artists")
.getJSONObject(0).getString("name");
//獲取歌曲專輯圖片的url
String albumPicUrl = song.getJSONObject("album").getString(
"picUrl");
//獲取歌曲音訊的url
String audioUrl = song.getString("audio");
Log.d(TAG, "doenloadUrl = " + audioUrl);
//儲存音樂資訊的Map
Map<String, Object> item = new HashMap<>();
item.put("title", title);
item.put("artist", artist);
item.put("picUrl", albumPicUrl);
picUrlMap.put(title + artist, new SoftReference<String>(
albumPicUrl));
item.put("audio", audioUrl);
//將一條歌曲資訊存入list中
netMusicList.add(item);
}
Log.d(TAG, "搜到" + netMusicList.size() + "首歌");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}