新乡学院计算机与信息工程学院
项目实训报告
20##年 12月29日
目 录
第1章 概述................................................................................................................. 1
第2章 需求分析......................................................................................................... 2
2.1 项目背景........................................................................................................ 2
2.2项目需求........................................................................................................ 2
第3章 相关技术......................................................................................................... 3
3.1 Adnroid平台介绍.......................................................................................... 3
3.2 关键技术研究................................................................................................ 3
第4章 概要设计......................................................................................................... 4
4.1 系统体系结构................................................................................................ 4
4.2 用户界面设.................................................................................................... 4
第5章 详细设计......................................................................................................... 6
5.1 播放设置以及后台播放源码...................................................................... 6
5.2 获取网络歌曲分类列表的代码实现........................................................ 13
第6章 结论............................................................................................................... 24
第1章 概述
本课题要求:设计一个基于Android的手机端音乐播放器软件。实现的功能有:
a) 能在Android上利用其播放本地和远程的歌曲。
b) 能通过一个搜索框实现本地和远程的搜索功能.
c) 可以实现后台播放,并能流畅的实现多界面的跳转。
需重点研究的关键问题:实现播放,后台播放,进度条显示进度等,以及网络请求数据和处理。
解决思路:浏览Android开源网站,认证学习,并请教老师,学习相关知识;不过主要还是靠自己的解题思路,解决问题的能力。
第2章 需求分析
2.1 项目背景
移动互联网时代的来临,直接促进了搭载智能操作系统、具有强大扩展性的智能手机、平板电脑等移动终端产业的蓬勃发展。搭载安卓操作系统的智能手机目前已经成为市场上最炙手可热的智能产品。开放的源代码和零成本已经让安卓在全世界范围内受到青睐并迅速获得了主要手机厂商和数百万软件开发者的支持,无疑android已经是智能设别领域的老大.
在这个移动互联网飞速发展的时代,带动全球的经济重心向着轻小得移动端发展.人们的衣食住行已经和手机这种设别息息相关,甚至是不可分割的状态.出于这个时代的背景,Android端的娱乐软件更是层出不穷.这些软件丰富了人们的闲暇之余.所以我打算入手一款娱乐软件,为大家揭秘神秘的android软件.
该课题的主要设计任务是实现android设备上的音乐播放器功能,属于自己设计的一款软件,轻巧方便,没有广告,内存利用率高,是一款实用而且高端的手机软件。
2.2项目需求
“i音乐”是一款手机音乐播放器,它通过搜索本地文件,实现本地音乐文件的播放,同时又能获取网络歌曲,简单一句话:麻雀虽小五脏俱全。
“i音乐”的主要目的是开发一个可以播放主流的音乐格式,本设计实现主要功能是播放MP3,WAV,OGG等格式的音乐文件,并且控制播放,暂停,停止,音量控制,选择上一曲,选择下一曲,视觉外观,列表文件和歌曲文件的管理操作等多种播放控制,界面简明,操作简单。
第3章 相关技术
3.1 Adnroid平台介绍
Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。Android操作系统最初由Andy Rubin开发,主要支持手机。20##年8月由Google收购注资。20##年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。第一部Android智能手机发布于20##年10月。Android逐渐扩展到平板电脑及其他领域上,如电视、数码相机、游戏机等。20##年第一季度,Android在全球的市场份额首次超过塞班系统,跃居全球第一。 20##年11月数据显示,Android占据全球智能手机操作系统市场76%的份额,中国市场占有率为90%。20##年09月24日谷歌开发的操作系统Android在迎来了5岁生日,全世界采用这款系统的设备数量已经达到10亿台。
3.2 关键技术研究
应用SQLLite数据库:SQLite提供结构化的数据存取。SQLite是一款轻型的数据库,它的设计目标是嵌入式的,目前已经在很多嵌入式产品使用了它,它占用的资源非常低。它能够支持Windows/Linux/Unix等主流的操作系统,同时能够跟很多程序语言相结合,比如php,Java等,还有ODBC接口,同样比起Mysql,PostgreSQL来讲,它的处理速度比它们都快。它无需安装和管理配置,它是一个储存在单一磁盘文件中的完整数据库,体积小,虽只有250KB但支持数据库大小至2TB。它虽然很小巧,但是支持的SQL语句毫不逊色于其它开源数据库。同时它还支持事务功能和并发处理等等。SQLite也是一套开放性的关联式资料库,其特点在于轻量性方面的设计结构,更适合在手机上使用。
第4章 概要设计
4.1 系统体系结构
系统旨在实现音乐播放和控制,主要有如下几个核心功能模块如图5所示。
图5 系统功能模块图
4.2 用户界面设
4.2.1 i音乐图
4.2.2 显示主界面和菜单栏
4.2.3 音乐清单
第5章 详细设计
5.1 播放设置以及后台播放源码
具体实现代码如下:
package cn.com.xxu.imusic;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.com.xxu.imusic.app.MyApp;
import cn.com.xxu.imusic.constant.Constants;
import cn.com.xxu.imusic.fragment.PlayFragment;
import cn.com.xxu.imusic.fragment.PlayListFragment;
import cn.com.xxu.imusic.utils.CommonUtils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.Toast;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class PlayActivity extends FragmentActivity {
@Bind(R.id.vp_play_pages)
ViewPager viewPager;
private FragmentPagerAdapter adapter;
private List<Fragment> fragments;
@Bind(R.id.header_play_activity_title)
RelativeLayout rlHeader;
@Bind(R.id.iv_fragment_play_next)
ImageView ivNext;
@Bind(R.id.iv_fragment_play_pauseOrplay)
ImageView ivPlayOrPause;
@Bind(R.id.iv_fragment_play_previous)
ImageView ivPrevious;
@Bind(R.id.pb_fragment_play_progress)
SeekBar pbProgress;
MyApp app;
private static final int CURRENT_SEEKBAR_PROGRESS = 100;
private Handler handler;
BroadcastReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_play);
ButterKnife.bind(this);
initFragments();
initViewPager();
viewPager.setCurrentItem(1);
initHeader();
receiver = new InnerBrocastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Constants.INTENT_ACTION_IS_PLAYING);
registerReceiver(receiver, filter );
handler = new Handler(new InnerHandlerCallBack());
app = MyApp.getInstance();
initView();
}
/**
* ≥? ºªØtitle
*/
private void initHeader() {
CommonUtils.setHeaderText(rlHeader, getString(R.string.local_music));
CommonUtils.setHeaderLeftIamgeView(rlHeader, R.drawable.back_arrow, new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
}, getResources());
}
/**
* ≥? ºªØFragment
*/
private void initFragments() {
fragments = new ArrayList<Fragment>();
fragments.add(new PlayListFragment());
// Fragment fragment = new PlayFragment();
// Bundle bundle = new Bundle();
// bundle.putString("mainActivity", "mainActivity");
// fragment.setArguments(bundle);
fragments.add(new PlayFragment());
}
/**
* ≥? ºªØViewPager
*/
private void initViewPager() {
adapter = new InnerFragmentAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int arg0) {
//TODO µº∫Ωπÿ¡?ViewPager
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
/**
* ?‘?®“¿?ºÃ≥–?‘FragmentPagerAdapter ≈‰??
* @author feixiang
*
*/
private class InnerFragmentAdapter extends FragmentPagerAdapter{
private List<Fragment> fragments;
public InnerFragmentAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int arg0) {
return fragments.get(arg0);
}
@Override
public int getCount() {
return fragments == null ? 0 : fragments.size();
}
}
/**
* ≥? ºªØ ∏?÷÷View øÿº?
*/
private void initView() {
pbProgress.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int currentPosition = app.getCurrentMusicDuration()/100*pbProgress.getProgress();
app.setCurrentPosition(currentPosition);
Intent intent = new Intent();
intent.setAction(Constants.INTENT_ACTION_SEEK_TO_POSITION);
sendBroadcast(intent);
Log.i("info"," currentPosition" + currentPosition);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
});
}
private class UpdateSeekBarThread extends Thread {
@Override
public void run() {
while ( true) {
Message.obtain(handler,CURRENT_SEEKBAR_PROGRESS).sendToTarget();
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private class InnerHandlerCallBack implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case CURRENT_SEEKBAR_PROGRESS:
int a = app.getCurrentPosition();
int b = app.getCurrentMusicDuration();
int percent = 100 * a / b;
// Log.i("info","≤?∑≈Ω¯?» "+ percent + "µ±«∞≤?∑≈ ±≥§" + a +"?? ±≥§" +b);
pbProgress.setProgress(percent);
break;
}
return false;
}
}
private class InnerBrocastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Constants.INTENT_ACTION_IS_PLAYING.equals(action)) {
if (app.isPlaying()) {
ivPlayOrPause.setImageResource(R.drawable.kg_btn_noti_pause_pressed);
} else {
ivPlayOrPause.setImageResource(R.drawable.kg_btn_noti_play_pressed);
}
new UpdateSeekBarThread().start();
}
}
}
@OnClick({R.id.iv_fragment_play_next,R.id.iv_fragment_play_pauseOrplay,R.id.iv_fragment_play_previous})
public void options(View v){
Intent intent = new Intent();
switch (v.getId()) {
case R.id.iv_fragment_play_previous:
intent.setAction(Constants.INTENT_ACTION_PREVIOUS);
Toast.makeText(getApplicationContext(), "previous", Toast.LENGTH_SHORT).show();
break;
case R.id.iv_fragment_play_pauseOrplay:
intent.setAction(Constants.INTENT_ACTION_PLAY_OR_PAUSE);
Toast.makeText(getApplicationContext(), "pauseOrplay", Toast.LENGTH_SHORT).show();
break;
case R.id.iv_fragment_play_next:
intent.setAction(Constants.INTENT_ACTION_NEXT);
Toast.makeText(getApplicationContext(), "next", Toast.LENGTH_SHORT).show();
break;
}
sendBroadcast(intent);
}
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
}
5.2 获取网络歌曲分类列表的代码实现.
具体的代码实现:
package cn.com.xxu.imusic;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.RelativeLayout;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.com.xxu.imusic.adapter.NetNewAlbumAdapter;
import cn.com.xxu.imusic.constant.Constants;
import cn.com.xxu.imusic.entity.NewNetAlbum;
import cn.com.xxu.imusic.utils.CommonUtils;
import cn.com.xxu.imusic.utils.HttpUtils;
import cn.com.xxu.imusic.utils.JsonUtils;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
import com.handmark.pulltorefresh.library.PullToRefreshGridView;
public class NetAlbumActivity extends Activity implements Constants{
@Bind(R.id.et_net_music_search)
EditText etSearch;
@Bind(R.id.btn_net_music_search)
Button btnSearch;
@Bind(R.id.lv_net_album_HY)
PullToRefreshGridView gridViewHY;
@Bind(R.id.lv_net_album_OM)
PullToRefreshGridView gridViewOM;
@Bind(R.id.lv_net_album_RH)
PullToRefreshGridView gridViewRH;
private TabHost tbHost;
private Handler handler;
@Bind(R.id.rl_net_music_header)
RelativeLayout rlHeader;
/**
* ≈‰??
*/
private NetNewAlbumAdapter adapterHY;
private NetNewAlbumAdapter adapterOM;
private NetNewAlbumAdapter adapterRH;
/**
* ?æ?‘¥
*/
private List<NewNetAlbum> dataHY;
private List<NewNetAlbum> dataOM;
private List<NewNetAlbum> dataRH;
/**
* Õ¯¬Á ?æ?“≥¬Î
*/
private int pageHY = 1;
private int pageOM = 1;
private int pageRH = 1;
private String uriHY;
private String uriOM;
private String uriRH;
private final int WHAT_SUCCESS = 110;
private final int DATA_TYPE_HY = 0;
private final int DATA_TYPE_OM = 1;
private final int DATA_TYPE_RH = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_net_music);
ButterKnife.bind(this);
handler = new Handler(new InnerCallback());
initHeader();
initTabHost();
initGridViewHY();
initGridViewOM();
initGridViewRH();
uriHY = URI_NEW_HY_ALBUMS + "page=" + pageHY;
new GetNewMusicThread(uriHY, DATA_TYPE_HY).start();
uriOM = URI_NEW_OM_ALBUMS + "page=" + pageOM;
new GetNewMusicThread(uriOM, DATA_TYPE_OM).start();
uriRH = URI_NEW_RH_ALBUMS + "page=" + pageRH;
new GetNewMusicThread(uriRH, DATA_TYPE_RH).start();
}
/**
* ≥? ºªØtitle
*/
private void initHeader() {
CommonUtils.setHeaderText(rlHeader, "Õ¯¬Á?®º≠");
CommonUtils.setHeaderLeftIamgeView(rlHeader, R.drawable.back_arrow, new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
}, getResources());
}
/**
* ≥? ºªØGridViewHY
*/
private void initGridViewHY() {
dataHY = new ArrayList<NewNetAlbum>();
adapterHY = new NetNewAlbumAdapter(NetAlbumActivity.this, dataHY);
gridViewHY.setMode(Mode.PULL_FROM_END);
gridViewHY.setOnRefreshListener(new OnRefreshListener<GridView>() {
@Override
public void onRefresh(PullToRefreshBase<GridView> refreshView) {
pageHY ++;
uriHY = URI_NEW_HY_ALBUMS + "&page=" + pageHY;
new GetNewMusicThread(uriHY, DATA_TYPE_HY).start();
}
});
gridViewHY.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Builder builder = new AlertDialog.Builder(NetAlbumActivity.this);
View v = LayoutInflater.from(NetAlbumActivity.this).inflate(R.layout.dialog_view_layout, null);
builder.setTitle("–°Ã· æ").setView(v).setNegativeButton("∑µªÿ", null);
builder.create().show();
}
});
gridViewHY.setAdapter(adapterHY);
}
/**
* ≥? ºªØGridViewOM
*/
private void initGridViewOM() {
dataOM = new ArrayList<NewNetAlbum>();
adapterOM = new NetNewAlbumAdapter(NetAlbumActivity.this, dataOM);
gridViewOM.setMode(Mode.PULL_FROM_END);
gridViewOM.setOnRefreshListener(new OnRefreshListener<GridView>() {
@Override
public void onRefresh(PullToRefreshBase<GridView> refreshView) {
pageOM ++;
uriOM = URI_NEW_OM_ALBUMS + "&page=" + pageOM;
new GetNewMusicThread(uriOM, DATA_TYPE_OM).start();
}
});
gridViewOM.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Builder builder = new AlertDialog.Builder(NetAlbumActivity.this);
View v = LayoutInflater.from(NetAlbumActivity.this).inflate(R.layout.dialog_view_layout, null);
builder.setTitle("–°Ã· æ").setView(v).setNegativeButton("∑µªÿ", null);
builder.create().show();
}
});
gridViewOM.setAdapter(adapterOM);
}
/**
* ≥? ºªØGridViewRH
*/
private void initGridViewRH() {
dataRH = new ArrayList<NewNetAlbum>();
adapterRH = new NetNewAlbumAdapter(NetAlbumActivity.this, dataRH);
gridViewRH.setMode(Mode.PULL_FROM_END);
gridViewRH.setOnRefreshListener(new OnRefreshListener<GridView>() {
@Override
public void onRefresh(PullToRefreshBase<GridView> refreshView) {
pageRH ++;
uriRH = URI_NEW_RH_ALBUMS + "&page=" + pageRH;
new GetNewMusicThread(uriRH, DATA_TYPE_RH).start();
}
});
gridViewRH.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Builder builder = new AlertDialog.Builder(NetAlbumActivity.this);
View v = LayoutInflater.from(NetAlbumActivity.this).inflate(R.layout.dialog_view_layout, null);
builder.setTitle("–°Ã· æ").setView(v).setNegativeButton("∑µªÿ", null);
builder.create().show();
}
});
gridViewRH.setAdapter(adapterRH);
}
/**
* ≥? ºªØTabHost
*/
private void initTabHost() {
tbHost = (TabHost) findViewById(R.id.tabhost);
tbHost.setup();
TabSpec tab1 = tbHost.newTabSpec("First");
tab1.setIndicator(createView("ª?”Ô"));
tab1.setContent(R.id.rl_new_music_HY);
tbHost.addTab(tab1);
TabSpec tab2 = tbHost.newTabSpec("Second");
tab2.setIndicator(createView("≈∑√¿"));
tab2.setContent(R.id.rl_new_music_OM);
tbHost.addTab(tab2);
TabSpec tab3= tbHost.newTabSpec("Third");
tab3.setIndicator(createView("»’∫´"));
tab3.setContent(R.id.rl_new_music_RH);
tbHost.addTab(tab3);
}
/**
* ªÒ»°TabHostµ?±Í«©≤ºæ÷View
* @param tabName
* @return
*/
private View createView(String tabName){
View view = LayoutInflater.from(NetAlbumActivity.this).inflate(R.layout.tabhost_tab_layout, null);
TextView tv = (TextView) view.findViewById(R.id.tv_tabhost_tab);
tv.setText(tabName);
return view;
}
/**
* µ?ª?À—À?
*
* @param v
*/
@OnClick(R.id.btn_net_music_search)
public void search(View v){
String param = etSearch.getText().toString();
if (!TextUtils.isEmpty(param)) {
Intent intent = new Intent(NetAlbumActivity.this, SearchNetMusicResultActivity.class);
intent.putExtra("search", param);
startActivity(intent);
}
}
/**
* ?‘?®“¿? µ?÷Handler.CallbackΩ”ø?
* @author feixiang
*
*/
private class InnerCallback implements Handler.Callback{
@SuppressWarnings("unchecked")
@Override
public boolean handleMessage(Message msg) {
if (msg.what == WHAT_SUCCESS) {
List<NewNetAlbum> data = (List<NewNetAlbum>) msg.obj;
switch (msg.arg1) {
case DATA_TYPE_HY:
dataHY.addAll(data);
adapterHY.setData(dataHY);
gridViewHY.onRefreshComplete();
break;
case DATA_TYPE_OM:
dataOM.addAll(data);
adapterOM.setData(dataOM);
gridViewOM.onRefreshComplete();
break;
case DATA_TYPE_RH:
Log.d("TAG", "dataRH " + dataRH == null ? "null" : "not null");
Log.d("TAG", "data " + dataRH == null ? "null" : "not null");
dataRH.addAll(data);
adapterRH.setData(dataRH);
gridViewRH.onRefreshComplete();
break;
}
}
return false;
}
}
/**
* ªÒ»°?Ó–¬?®º≠??≥Ã
* @author feixiang
*
*/
private class GetNewMusicThread extends Thread{
private String params;
private int type;
private GetNewMusicThread(String params,int type) {
super();
this.type = type;
this.params = params;
}
@Override
public void run() {
String uri = params;
String jsonStr = HttpUtils.getNetJsonString(uri);
jsonStr = jsonStr.substring(jsonStr.indexOf("{"), jsonStr.length());
List<NewNetAlbum> data = JsonUtils.getNewNetAlbumsFromJson(jsonStr);
Message.obtain(handler, WHAT_SUCCESS, type,0,data).sendToTarget();
}
}
第6章 结论
经过累计一个多月的努力,基于Android平台的i音乐软件终于完成了。由于对Android浓厚的兴趣引发了我对JAVA的好奇心,毕竟Android是基于JAVA的,虽然这个软件的开发没有达到老师的期望值,但是在老师和同学的帮助下,我还是慢慢摸索完成了这个软件。
在这个过程中,我学会了很多知识,比如Android环境的搭建和配置问题、音频播放技术和Android的构架和基本组建等等,同时我学会的最重要的一点是,要利用现有的资源去解决问题,在图书馆、在网络上寻找答案,也可以请教老师或与同学相互讨论。虽然,这个软件目前运行良好,但是我认为代码的健壮还很差,在整个工程的构思方面也不足,用户操作界面还不够灵活,这些问题还需要今后逐一解决。与此同时,这个软件还可以进一步扩展,例如“显示歌词”、“本地下载”、“应用推荐”等,使功能更加强大,带给用户更好的体验与便捷的生活服务。