带歌词的音乐播放器
山东建筑大学计算机科学与技术学院
综合训练说明书
题 目: 带歌词的音乐播放器 课 程: 移动设备软件开发 院 (部): 计算机科学与技术学院 专 业: 软件工程专业
班 级: 软件132班
学生姓名: 刘超
学 号: 20131112054 指导教师: 杨朝晖
完成日期: 2015-07-06
目 录
带歌词的音乐播放器 ........................................................ 2
一、 需求分析 ......................................................... 2 1(1系统功能需求.......................................................... 2 1(2模块划分.............................................................. 3
二、 概要设计 ......................................................... 4 2.1系统流程分析........................................................... 4 2.2系统整体结构分析。..................................................... 6
三、详细设计 .......................................................... 7 3.1 音乐播放条目与音乐播放 ................................................ 7
10 3.2 动态显示歌词模块 .....................................................3.3扫描歌曲.............................................................. 18
3.4列表显示全部歌曲...................................................... 23 3.5列表显示我的最爱...................................................... 30 3.6列出储存歌曲的文件夹.................................................. 34
3.7设置功能.............................................................. 36
四、系统测试 ......................................................... 40
.............................. 40 4.1 系统测试................................
4.1.1 引言 ........................................................... 40
4.1.2 测试目的 ....................................................... 40
4.1.3 测试用例及结果 ................................................. 41 4.2 测试结果分析.......................................................... 44
五、总结 ............................................................. 44 参考文献 ................................................................. 46
1
带歌词的音乐播放器
一 需求分析
现今社会生活紧张,而欣赏音乐是其中最好的舒缓压力的方式之一,本项目的目的是开发一个可以播放主流音乐文件
的播放器,本设计实现的主要功能是播放Mp3,Wav多种格式的音乐文件,并且能够控制播放,暂停,停止,播放列等基本播放控制功能,界面简明,操作简单。
本项目是一款基于Android手机平台的音乐播放器,使Android手机拥有个性的多媒体播放器,使手机显得更生动灵活化,与人们更为接近,让手机主人随时随地处于音乐视频的旋律之中。使人们的生活更加多样化。也使设计者更加熟练Android的技术和其它在市场上的特点。
1(1系统功能需求
(1)音乐播放条目与音乐播放
1,按截图要求构建音乐播放器的界面:
, 顶部设计音乐播放条目:
, 歌曲所在专辑的图片。
, 歌曲名称与演唱者。
, 播放按钮
, 底部设计音乐播放过程中用于显示歌词的文本视图。
2,当用户点击播放按钮后,音乐开始播放,图片切换成暂停按钮。
3,当用户点击暂停按钮后,音乐暂停(非停止),图片切换成播放按钮。
(2)动态显示歌词
1,根据音乐播放的时间节点匹配歌词。
2,如果歌词与当前播放节点吻合lyricView中呈现。
(3)扫描歌曲
1,自动获取手机中存放歌曲文件的文件夹
2,用户勾选可能包含歌曲文件的文件夹选项
3,当用户点击扫描歌曲的按钮时,扫描勾选文件夹,将扫描的歌曲加入歌曲列表
(4)列表显示全部歌曲
1,手机中扫描得到的全部歌曲用listview显示在界面中间
2,当用户点击每一个列表项时,弹出一个对话框,提示几个选项,包括从列表中
移出,从文件中移出,查看歌曲信息。
3,界面下部,依次出现的是歌曲专辑按钮,歌曲信息,上一首,开始暂停,下一
2
首的图片按钮。
(5)列表显示我的最爱
1,当用户点击每一个列表项时,弹出一个对话框,提示几个选项,包括从列表中
移出,从文件中移出,查看歌曲信息。
2,界面下部,依次出现的是歌曲专辑按钮,歌曲信息,上一首,开始暂停,下一
首的图片按钮。
3,在列表项底部显示共有多少首歌曲
(6)列出储存歌曲的文件夹
1,列表显示储存文件的文件夹都有哪些
2,当用户点击某一个文件夹时,列表显示每个文件夹内的所有歌曲
(7)设置功能
1,用户点击系统提供的图片进行背景更换
2,用户点击系统提供的颜色更换歌词颜色 1(2模块划分
带歌词的音乐播放器
全扫储音动我设 部描存乐态的置 歌歌文播显最选 曲 曲 爱 件放 示项 夹 歌 词
3
二 概要设计
2.1系统流程分析
进入到音乐播放器内,查看是否有音乐列表,如果没有自动搜索歌曲并显示在列表界面中,如果没有侧滑列表界面,点击扫描歌曲,接入扫描歌曲界面,勾选文件夹,点击扫描歌曲按钮,将歌曲添加到歌曲列表中。添加完成后,歌曲显示在列表中,点击一个歌曲的列表项,歌曲播放,同时获取该专辑的图片以及歌曲名字以及歌手名字,显示在列表下方,然后点击item的下方的控件,弹出一个对话框,对话框中显示是否从列表中移出,点击是,移出,否,不进行任何操作,返回文件列表。点击歌曲专辑图片,跳转到歌曲播放界面,界面中部动态显示歌词,点击界面中的红心,将歌曲标识为最爱,点击下一曲按钮,歌曲播放下一首。
开始
进入到APP
N 列表中是扫描歌曲列表 否含有歌
曲,
Y
文件夹是 N 勾选 否勾选
Y
4
歌曲列表
扫描歌曲
点击歌曲列表中的一项
播放歌曲
N 是否含 搜索歌词 有歌 词,
Y
歌曲播放
是否为
最爱, 5
点击上一曲 是否为第一加入最爱列表
首,
点击下一曲 是否为最后
一曲,
退出系统
结束
2.2系统整体结构分析。
用户点击app的图标,进入到音乐播放器内,查看是否有音乐列表,如果没有自动搜索歌曲并显示在列表界面中,如果没有侧滑列表界面,点击扫描歌曲,接入扫描歌曲界面,勾选文件夹,点击扫描歌曲按钮,将歌曲添加到歌曲列表中。添加完成后,歌曲显示在列表中,点击一个歌曲的列表项,歌曲播放,同时获取该专辑的图片以及歌曲名字以及歌手名字,显示在列表下方,然后点击item的下方的控件,弹出一个对话框,对话框中显示是否从列表中移出,点击是,移出,否,不进行任何操作,返回文件列表。点击歌曲专辑图片,跳转到歌曲播放界面,界面中部动态显示歌词,点击界面中的红心,将歌曲标识为最爱,点击下一曲按钮,歌曲播放下一首。点击上部返回按钮再次回到列表界面,左侧滑界面进入到列表选择,点击设置选项,进入到设置界面,点击更换界面背景,及时更换界面背景,点击更换歌词颜色,及时更换歌词颜色。再次返回到列表选择界面,点击我的最爱将显示我曾经标记为红心的歌曲列表,点击文件夹,将显示为我的手机内存放歌曲的相应的文件夹。点击退出按钮,将退出该系统。
6
三、详细设计
3.1 音乐播放条目与音乐播放
功能说明:
1,按截图要求构建音乐播放器的界面:
, 顶部设计音乐播放条目:
, 歌曲所在专辑的图片。
, 歌曲名称与演唱者。
, 播放按钮
, 底部设计音乐播放过程中用于显示歌词的文本视图。
,2,当用户点击播放按钮后,音乐开始播放,图片切换成暂停按钮。
,3,当用户点击暂停按钮后,音乐暂停(非停止),图片切换成播放按钮 实现思路:
利用xml文件布局实现的播放界面。设置了一个currentImage变量,并开始赋值为1,每点击一次播放按钮,currentImage 加1,然后判断currentImage%2,如果结果为零,将图片转换为暂停按钮,如果结果不为零,将图片转换为播放按钮。 代码:
8
3.1.2,
代码:
if (currentImage % 2 == 0) {
start.setImageResource(R.drawable.stop);
mp.pause();
} else {
start.setImageResource(R.drawable.start);
mp.start();
running = true;
9
thread = new Thread(new UIUpdateThread());
thread.start();
}
currentImage++;
}
截图展示:
3.2 动态显示歌词模块
功能说明:
1,根据音乐播放的时间节点匹配歌词。
2,如果歌词与当前播放节点吻合lyricView中呈现。
实现思路:
重写了TextView,先预将歌词存在lyricView 中,然后根据传进来的参数,不断动态的滚动显示预先存在的歌词文件,最后将lyricView将加入到布局文件中,作为一个组件使用。开启一个新线程,在线程中获取当前歌曲播放的具体位置,并以参数的方式传入到相应的歌词处理类中,动态的刷新歌词显示的视图。
10
开始
流程图:
播放歌曲
当前是否
有歌曲播
放,
Y
将歌曲位置传入
匹配LRC文件
N 文件是否提示没有歌词,去下载 存在,
Y
11
在视图中显示
代码:
private Thread thread = null;//定义一个线程监听歌曲播放的位置// 开线程用于获
取当前播放时间轴
if (thread == null) {
thread = new Thread(new UIUpdateThread());
thread.start();
}
if (!thread.isAlive()) {
System.out.println("~~~~~!thread.isAlive()");
running = false;
thread.interrupt();
thread = null;
thread = new Thread(new UIUpdateThread());
thread.start();
}
}
class UIUpdateThread implements Runnable {
@Override
public void run() {
while (running) {
if (!mp.isPlaying()) {
running = false;
thread.interrupt();
thread = null;
}
int time = mp.getCurrentPosition();//获取歌曲当前播放的位置,位
置是int型的
int sleeptime = 100;
12
lyricView.updateindex(time);//将滚动歌词调整到与时间匹配的位置
mHandler.post(mUpdateResults);
//
System.out.println("mp.isPlaying()~~~"+time+"!!"+sleeptime+"!!");
if (sleeptime == -1)
return;
try {
Thread.sleep(sleeptime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Handler mHandler = new Handler();
Runnable mUpdateResults = new Runnable() {
@Override
public void run() {
lyricView.invalidate();// 更新视图
}
}
代码:public class LyricView extends TextView{
private Paint mPaint;
private float mX;
private Paint mPathPaint;
13
public int index = 0; public float mTouchHistoryY; private int mY;
private int currentDuringTime;//当前歌词持续时间
middleY;//Y轴中间 private float
private final int DY = 40;//每一行的间隔 public float driftx;//x偏移量
public float drifty;//y偏移量
private float drift_r; public boolean showprogress;//滑动时显示进度 public int temp = 0;
public LyricView(Context context) {
super(context);
init();
}
public LyricView(Context context,AttributeSet attr){
super(context,attr);
init();
}
public LyricView(Context context,AttributeSet attr,int i){
super(context,attr,i);
init();
}
private void init(){
setFocusable(true);
//非高亮部分
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(18);
mPaint.setColor(Color.WHITE);
mPaint.setTypeface(Typeface.SERIF);
14
//高亮部分 当前歌词
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setTextSize(21);
); mPathPaint.setColor(Color.RED
mPathPaint.setTypeface(Typeface.SANS_SERIF);
}
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//滑动相关
//显示进度相关
int j =(int) (-drifty/40);
if(temp
j){
temp--;
}
if(index+temp>=0&&index+temp=0){
canvas.drawText(TimeParseTool.(time[index+temp]), MsecParseTime
0, middleY , p2);
}
else canvas.drawText("00:00", 0, middleY , p2);
canvas.drawLine(0, middleY+1, mX*2, middleY+1, p2);
}
float tempY = middleY + drift_r;
//画出本句之前的句子
for(int i = index-1;i >= 0;i --){
//向上推移
tempY = tempY - DY;
if(tempY < 0){
break;
}
canvas.drawText(text[i], mX, tempY, p);
}
tempY = middleY + drift_r;
//画出本句之后的句子
for(int i = index+1;i < text.length;i ++){
//向下推移
tempY = tempY + DY;
if(tempY > mY){
break;
}
canvas.drawText(text[i], mX, tempY, p);
}
16
}
protected void onSizeChanged(int w,int h,int ow,int oh){
super.onSizeChanged(w, h, ow, oh);
mX = w*0.5f;//屏幕中心坐标(转换为float?)
mY = h;
middleY = h*0.5f;
}
/**
* @author younger
* @param CurrentPosition
* 当前歌词的时间轴
* @return drift
* 可以返回数据(已经废弃)
*/
public float updateindex(int CurrentPosition){
//歌词数组的序号
if(CurrentPosition >= Integer.parseInt(time[index])&&CurrentPosition <
Integer.parseInt(time[index+1])){
currentDuringTime = Integer.parseInt(time[index+1]) -
Integer.parseInt(time[index]);
index++;
drifty = 0;
driftx = 0;
}else if(index == 0){
currentDuringTime=Integer.parseInt(time[index]);
}
if(drifty>-40.0)drifty =(float) (drifty -
40.0/(currentDuringTime/100)) ;
17
if(index == -1)return -1;
return drifty;
}
public boolean repair(){
if(index<=0){
index=0;
return false;
}
if(index>time.length-1)index=time.length-1;
return true;
}
}
截图展示:
3.3扫描歌曲
功能介绍:
,自动获取手机中存放歌曲文件的文件夹 1
2,用户勾选可能包含歌曲文件的文件夹选项
3,当用户点击扫描歌曲的按钮时,扫描勾选文件夹,将扫描的歌曲加入歌曲列表
18
实现思路:通过获取sd可得使用权限,扫描手机中的所有歌曲,显示在列表项中,可以自定义勾选,来扫描相应的文件夹。
开 始 流程图:
扫描列表
是否已 经勾勾选
选,
扫描歌曲
代码:
* 扫描歌曲
* @author longdw(longdawei1988@gmail.com)
*
*/
public class MenuScanActivity extends FragmentActivity {
public ViewPager mViewPager;
private List mFragmentList = new ArrayList();
@Override
protected void onCreate(Bundle arg0) {
19
super.onCreate(arg0);
setContentView(R.layout.menu_scan);
mViewPager = (ViewPager) findViewById(R.id.viewPager);
initViewPager();
}
private void initViewPager() {
Fragment leftFragment = new LeftFragment();
Fragment rightFragment = new RightFragment();
Fragment menuFragment = new MenuScanFragment();
mFragmentList.add(leftFragment);
mFragmentList.add(menuFragment);
mFragmentList.add(rightFragment);
mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(),
mFragmentList));
mViewPager.setOnPageChangeListener(new MyOnPageChangeListener());
mViewPager.setCurrentItem(1, true);
/*Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mViewPager.setCurrentItem(1, true);
}
};
handler.sendEmptyMessageDelayed(1, 1000);*/
}
20
private class MyPagerAdapter extends FragmentPagerAdapter {
List fragmentList;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragmentList = fragments;
}
@Override
public Fragment getItem(int arg0) {
return fragmentList.get(arg0);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
private class MyOnPageChangeListener implements OnPageChangeListener {
int onPageScrolled = -1;
// 当滑动状态改变时调用
@Override
21
public void onPageScrollStateChanged(int arg0) { // System.out.println("onPageScrollStateChanged--->" + arg0);
if ((onPageScrolled == 0 || onPageScrolled == 2) && arg0 == 0) {
setResult(1);
finish();
}
}
// 当当前页面被滑动时调用
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
onPageScrolled = arg0;
// System.out.println("onPageScrolled--->" + "arg0=" + arg0 + " arg1="
// + arg1 + " arg2=" + arg2);
}
// 当新的页面被选中时调用
@Override
public void onPageSelected(int arg0) {
// System.out.println("onPageSelected--->" + arg0);
}
}
@Override
public void onBackPressed() {
if(mViewPager.isShown()) {
mViewPager.setCurrentItem(0, true);
}
}
}
截图演示:
22
3.4列表显示全部歌曲
功能实现:
1,手机中扫描得到的全部歌曲用listview显示在界面中间
2,当用户点击每一个列表项时,弹出一个对话框,提示几个选项,包括从列表中
移出,从文件中移出,查看歌曲信息。
3,界面下部,依次出现的是歌曲专辑按钮,歌曲信息,上一首,开始暂停,下一
首的图片按钮。
实现思路:
将扫描的得到的全部歌曲显示在listview上,然后自动以Adapter,实现灵活适用于当前系统使用的适配器,并在点击每个list项时,出现一个对话框。 流程图:
扫描全部歌曲
列表项显示各歌曲
自定义Adapter
23
实现对话框形式
代码:
public class MyAdapter extends BaseAdapter implements IConstants {
private LayoutInflater mLayoutInflater;
private ArrayList mMusicList;
private ServiceManager mServiceManager;
private SlidingDrawerManager mSdm;
private int mPlayState, mCurPlayMusicIndex = -1; // private IQueryFinished mIQueryFinished;
private FavoriteInfoDao mFavoriteDao;
private MusicInfoDao mMusicDao;
private int mFrom;
class ViewHolder {
TextView musicNameTv, artistTv, durationTv;
ImageView playStateIconIv, favoriteIv;
}
public MyAdapter(Context context, ServiceManager sm, SlidingDrawerManager
sdm) {
mLayoutInflater = LayoutInflater.from(context);
mMusicList = new ArrayList();
this.mServiceManager = sm;
this.mSdm = sdm;
mFavoriteDao = new FavoriteInfoDao(context);
mMusicDao = new MusicInfoDao(context);
24
}
public void setData(List list, int from) {
setData(list);
this.mFrom = from;
}
/**
* 当数据库中有数据的时候会调用该方法来更新列表
*
* @param list
*/
public void setData(List list) {
mMusicList.clear();
if (list != null && list.size() > 0) {
mMusicList.addAll(list);
// 为list排序
Collections.sort(mMusicList, comparator);
notifyDataSetChanged();
}
}
public void refreshPlayingList() {
if(mMusicList.size() > 0) {
mServiceManager.refreshMusicList(mMusicList);
}
}
public void refreshFavoriteById(int id, int favorite) {
int position = MusicUtils.seekPosInListById(mMusicList, id);
mMusicList.get(position).favorite = favorite;
25
notifyDataSetChanged();
}
public List getData() {
return mMusicList;
}
public void setQueryFinished(IQueryFinished finish) {
// mIQueryFinished = finish;
}
Comparator comparator = new Comparator() {
char first_l, first_r;
@Override
public int compare(MusicInfo lhs, MusicInfo rhs) {
first_l = lhs.musicName.charAt(0);
first_r = rhs.musicName.charAt(0);
if (StringHelper.checkType(first_l) ==
StringHelper.CharType.CHINESE) {
first_l = StringHelper.getPinyinFirstLetter(first_l);
}
if (StringHelper.checkType(first_r) ==
StringHelper.CharType.CHINESE) {
first_r = StringHelper.getPinyinFirstLetter(first_r);
}
if (first_l > first_r) {
return 1;
} else if (first_l < first_r) {
return -1;
26
} else {
return 0;
}
}
};
public void setPlayState(int playState, int playIndex) {
mPlayState = playState;
mCurPlayMusicIndex = playIndex;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mMusicList.size();
}
@Override
public MusicInfo getItem(int position) {
return mMusicList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent)
{
final ViewHolder viewHolder;
27
final MusicInfo music = getItem(position);
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mLayoutInflater
.inflate(R.layout., null); musiclist_item
viewHolder.musicNameTv = (TextView) convertView
.findViewById(R.id.musicname_tv);
viewHolder.artistTv = (TextView) convertView
.findViewById(R.id.artist_tv);
viewHolder.durationTv = (TextView) convertView
.findViewById(R.id.duration_tv);
viewHolder.playStateIconIv = (ImageView) convertView
.findViewById(R.id.playstate_iv);
viewHolder.favoriteIv = (ImageView) convertView
.findViewById(R.id.favorite_iv);
convertView.setTag(viewHolder);
{ } else
viewHolder = (ViewHolder) convertView.getTag();
}
if (position != mCurPlayMusicIndex) {
viewHolder.playStateIconIv.setVisibility(View.GONE);
} else {
viewHolder.playStateIconIv.setVisibility(View.VISIBLE);
if (mPlayState == MPS_PAUSE) {
viewHolder.playStateIconIv
.setBackgroundResource(R.drawable.list_pause_state);
} else {
viewHolder.playStateIconIv
.setBackgroundResource(R.drawable.list_play_state);
}
28
}
viewHolder.favoriteIv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(music.favorite == 1) {
if(mFrom == START_FROM_FAVORITE) {
mMusicList.remove(position);
notifyDataSetChanged();
}
// music.favorite = 0;
mFavoriteDao.deleteById(music._id);
mMusicDao.setFavoriteStateById(music._id, 0);
viewHolder.favoriteIv.setImageResource(R.drawable.icon_favourite_normal);
mMusicList.get(position).favorite = 0;
mSdm.refreshFavorite(0);
} else {
// music.favorite = 1;
mFavoriteDao.saveMusicInfo(music);
mMusicDao.setFavoriteStateById(music._id, 1);
viewHolder.favoriteIv.setImageResource(R.drawable.icon_favourite_checked);
mMusicList.get(position).favorite = 1;
mSdm.refreshFavorite(1);
}
}
});
截图展示:
29
3.5列表显示我的最爱
功能实现:
1,当用户点击每一个列表项时,弹出一个对话框,提示几个选项,包括从列表中
移出,从文件中移出,查看歌曲信息。
2,界面下部,依次出现的是歌曲专辑按钮,歌曲信息,上一首,开始暂停,下一
首的图片按钮。
3,在列表项底部显示共有多少首歌曲
实现思路:
将扫描的得到的全部歌曲显示在listview上,然后自动以Adapter,实现灵活适用于当前系统使用的适配器,并在点击每个list项时,出现一个对话框。同时在每个歌曲界面都会设置一个心形图标,点击后将歌曲加入到我的最爱中。
流程图:
播放歌曲
是否加 入最 爱, Y
30
加入最爱列表
代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.playqueue);
initView();
initConnection();
}
private void initConnection() {
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = IMediaService.Stub.asInterface(service);
if(mService != null) {
try {
mCurMode = mService.getPlayMode();
31
mPlayModeIv.setBackgroundResource(modeDrawable[mCurMode]);
mService.getMusicList(mMusicList);
mCurMusicId = mService.getCurMusicId();
mPlayingSongPosition =
MusicUtils.seekPosInListById(mMusicList, mCurMusicId);
initListView();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
Intent intent = new Intent("com.ldw.music.service.MediaService");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
private void initView() {
mListView = (ListView) findViewById(R.id.listview_play_queue);
mTitleTv = (TextView) findViewById(R.id.title_tv);
mListView.setOnItemClickListener(this);
mPlayModeIv = (ImageView) findViewById(R.id.playmode_iv);
mPlayModeLayout = (LinearLayout) findViewById(R.id.playmode_layout);
mPlayModeLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
32
changeMode();
mService.setPlayMode(mCurMode);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private void changeMode() {
mCurMode++;
if (mCurMode > MPM_SINGLE_LOOP_PLAY) {
mCurMode = MPM_LIST_LOOP_PLAY;
}
mPlayModeIv.setBackgroundResource(modeDrawable[mCurMode]);
}
截图展示:
33
3.6列出储存歌曲的文件夹
功能说明:
1,列表显示储存文件的文件夹都有哪些
2,当用户点击某一个文件夹时,列表显示每个文件夹内的所有歌曲 实现思路:
通过获取sd的权限,扫描手机,并将含有歌曲的文件夹先出出来 流程图:
扫描手机中文件夹
是否含
有歌 曲,
加入到列表中
代码:public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.menu_scan_fragment, container,
false);
mScanBtn = (Button) view.findViewById(R.id.scanBtn);
mBackBtn = (ImageButton) view.findViewById(R.id.backBtn);
mScanBtn.setOnClickListener(this);
34
mBackBtn.setOnClickListener(this);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mProgress.dismiss();
((MenuScanActivity)getActivity()).mViewPager.setCurrentItem(0,
true);
}
};
return view;
}
private void getData() {
new Thread(new Runnable() {
@Override
public void run() {
mHelper.deleteTables(getActivity());
MusicUtils.queryMusic(getActivity(), START_FROM_LOCAL);
MusicUtils.queryAlbums(getActivity());
MusicUtils.queryArtist(getActivity());
MusicUtils.queryFolder(getActivity());
mHandler.sendEmptyMessage(1);
}
}).start();
}
@Override
35
public void onClick(View v) {
if(v == mScanBtn) {
mProgress = new ProgressDialog(getActivity());
mProgress.setMessage("正在扫描歌曲,请勿退出软件~");
mProgress.setCancelable(false);
mProgress.setCanceledOnTouchOutside(false);
mProgress.show();
getData();
} else if(v == mBackBtn) {
((MenuScanActivity)getActivity()).mViewPager.setCurrentItem(0,
true);
}
}
}
截图展示:
3.7设置功能
功能说明:
1,用户点击系统提供的图片进行背景更换
36
2,用户点击系统提供的颜色更换歌词颜色
实现思路:
跳转到设置界面,预先将背景图片和歌词颜色存放在系统文件内,用户通过点击使系统将歌词或背景进行切换。
流程图: 设置界面
是否更 未更换背景图片 换背
景,
背景更换成功
代码:
public class MenuSettingActivity extends FragmentActivity {
public ViewPager mViewPager;
private List mFragmentList = new ArrayList();
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.menu_setting);
mViewPager = (ViewPager) findViewById(R.id.viewPager);
initViewPager();
37
}
private void initViewPager() {
Fragment leftFragment = new LeftFragment();
Fragment rightFragment = new RightFragment();
Fragment menuFragment = new MenuSettingFragment();
mFragmentList.add(leftFragment);
mFragmentList.add(menuFragment);
mFragmentList.add(rightFragment);
mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(),
mFragmentList));
mViewPager.setOnPageChangeListener(new MyOnPageChangeListener());
mViewPager.setCurrentItem(1, true);
/*Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mViewPager.setCurrentItem(1, true);
}
};
handler.sendEmptyMessageDelayed(1, 1000);*/
}
private class MyPagerAdapter extends FragmentPagerAdapter {
List fragmentList;
public MyPagerAdapter(FragmentManager fm) {
38
super(fm);
}
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragmentList = fragments;
}
@Override
public Fragment getItem(int arg0) {
return fragmentList.get(arg0);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
private class MyOnPageChangeListener implements OnPageChangeListener {
int onPageScrolled = -1;
// 当滑动状态改变时调用
@Override
public void onPageScrollStateChanged(int arg0) { // System.out.println("onPageScrollStateChanged--->" + arg0);
if ((onPageScrolled == 0 || onPageScrolled == 2) && arg0 == 0) {
finish();
}
39
}
截图展示:
四、系统测试
4.1 系统测试
4.1.1 引言
1软件测试是保证软件质量的关键,是对需求分析和编码的最后复审。在开发软件的过程中,虽使用了许多保证软件质量的方法,分析、设计和实现软件,但难免还会在实际操作过程中犯错误。这样就会在软件产品中隐藏许多错误和缺陷。如果不在设计阶段排除这些错误,有可能导致系统功能不能正常运行,有时候甚至会造成巨大的损失。为了保证软件的质量,且在今后的使用过程中保证软件的精确性、安全性和较长的使用寿命,软件必须进行充分的测试。为此,在对本系统经过测试之后写下了如下的分析。
4.1.2 测试目的
编写本测试的目的,是对设计的系统的性能进行测试以满足客户的需求,看系统的最终设计是否和我们最初要实现的功能相一致,找出系统存在的缺点和不足,不断完善系统的功能。
40
4.1.3 测试用例及结果
1,音乐播放界面测试:
动态显示歌词测试:
扫描歌词界面测试:
41
全部歌曲列表测试:
我的最爱列表测试:
42
文件夹功能测试:
设置界面测试:
43
4.2 测试结果分析
测试结果还算正常,一切都可以正确运行,不过仍然有一点小八哥,就是在播放器暂停后,再次播放音乐会有歌词暂时无法正常显示,不过已经解决了,只需在按钮的事件监听时再次重新启动更新歌词的线程就可以同步了。
五、总结
通过本次课程设计,我独自完成了带歌词的音乐播放器的安卓程序,在设计本课程设计中,我遇到了很多的困难,刚开始时,我根本就不了解音乐播放器是怎样实现的,于是通过上网查和向别人寻求帮助的方式,渐渐的了解了什么是音乐播放器,以及音乐播放器的架构,但是在具体的编写程序时还是遇到了各种困难,比如,如何实现歌词与歌曲的同步,播放按钮的图片切换以及如何获取音乐文件并使用音乐文件等等。慢慢我懂得了如何去做,实现歌词与歌曲的同步需要开启一个新的线程,并不断地向这个线程中传入当前歌曲位置参数,不断地动态刷新歌词,同时歌词的处理我是重新定义了TextView类,实现动态滚动歌词,并将同步的歌词设置为红色。在课设中我遇到了很多问题,但正是因为这些问题我同样学到了很多的知识,我学会了如何开启新的子线程,学会如何自定义View类等等。同时,也感谢老师以及热心同学对我的热情指导,才让我能够完成这次课程设计。
.
44
45
参考文献
[1] 刘宝林. Java程序设计与案例. 高等教育出版社,2004
[2] 刘韬,楼兴华.SQL Server2000 数据库系统开发实例导航. 北京:人民邮电出版社 [3] 丁宝康,董健全. 数据库实验教程. 北京:清华大学出版社
[4] Michele Leroux Bustamants.Secure your ASP.NET Apps and WCF services with Windows
CardSpace. MSDN Magazine,April [5] 肖建编. ASP.NET 编程实例与技巧集粹. 北京:北京希望电子出版社 [6] 巴兹拉等. ASP.NET 安全性高级编程. 北京:清华大学出版社
46