系统运行界面展示:
第一步要先把歌曲导进模拟器,导入模拟器的时候要先获取权限:
- 首先,要获得虚拟设备Root权限。
- 命令行工具,切换到『sdk目录』/platform-tools/ (或者将上述目录加入环境变量Path) 。
- 运行 adb root
- 然后,开启ADB集成
- Tools->Android->勾选Enable ADB Integration
- 向手机中推送音频文件:
- Tools—Android—Android Device Monitor
1系统需求分析
1.1 登录模块
输入用户名和密码(初始用户名为admin,密码为123456),实现用户的登入和退出功能。
1.2 音乐列表
显示所有的歌手名和歌曲名,单击某首歌曲跳转到播放界面,长按实现删除歌曲的功能。
1.3 播放界面
显示正在播放歌曲的相关信息,如歌曲名,对应的歌手名,当前播放时间,歌曲总时间等,并且可拖动进度条实现快进、快退功能以及循环播放和单曲循环功能。
2系统总体设计
2.1系统整体结构
2.2数据存储设计
MediaStore类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去调用那些封装好的接口就可以进行数据库的操作了。比如,这次实验利用了ContentProvider(内容提供者)去调用安卓系统自带SQLite音频数据库,路径地址为MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,返回的结果为cursor结果集接口,此过程简单方便,适合此次设计的使用。
3系统详细设计
3.1 登录功能
用户输入账号和密码实现登录,并且在此界面实现获取系统权限的功能。
关键代码如下:
public class LoginActivity extends AppCompatActivity {
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (ContextCompat.checkSelfPermission(LoginActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(LoginActivity.this,
new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
...
login = (Button) findViewById(R.id.login);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
...
if (account.equals("admin") && password.equals("123456")) {
Intent intent = new Intent(LoginActivity.this, MusicListActivity.class);
startActivity(intent);
} else {
Toast.makeText(LoginActivity.this, "用户名或密码错误,请重新输入!", Toast.LENGTH_SHORT).show();
}
}
});
}
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] != PackageManager.
PERMISSION_GRANTED) {
Toast.makeText(this, "拒绝权限将无法使用程序",
Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
break;
}
}
}
3.2 音乐列表功能
用户可查看音乐列表,选择播放或删除某一首音乐,单击实现跳转播放,长按实现是否选择删除功能。
关键代码如下:
//查询媒体数据库
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
//遍历数据库
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
//路径
String uri = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
...
File file = new File(uri);
if (isMusic != 0 && file.exists()) {
Music music = new Music(uri, musicName, artistName);
musicList.add(music);
}
}
}
...
//短按实现跳转播放
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Intent intent = new Intent(MusicListActivity.this, MusicPlayActivity.class);
intent.putExtra("position", position);
startActivity(intent);
}
});
//长按选择是否删除歌曲
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
final int position, long id) {
final Music music = musicList.get(position);
AlertDialog.Builder dialog = new AlertDialog.Builder(MusicListActivity.this);
dialog.setTitle("提示:");
dialog.setMessage("是否删除这首歌曲?");
dialog.setCancelable(false);
dialog.setPositiveButton("删除", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//删除音乐文件
File file = new File(music.getUri());
file.delete();
//删除媒体数据库记录
getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
MediaStore.Audio.Media.DATA + " LIKE ?", new String[]{music.getUri()});
musicList.remove(position);
adapter.notifyDataSetChanged();
}
});
dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show();
return true;
}
});
}
3.3 播放界面功能
用户可选择不同的播放模式,如顺序播放或单曲循环,并且可选择播放上一首或下一首乐曲,以及可实现拖拉进度条调整音乐播放进度的功能。
关键代码如下:
public Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
musicName.setText(songName);
artist.setText(artistName);
musicTime.setText(time.format(mediaPlayer.getCurrentPosition()));
musicTotal.setText(time.format(mediaPlayer.getDuration()));
seekBar.setMax(mediaPlayer.getDuration());
seekBar.setProgress(mediaPlayer.getCurrentPosition());
break;
default:
break;
}
}
};
protected void onCreate(Bundle savedInstanceState) {
...
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
// 拖动条停止拖动的时候调用
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
mediaPlayer.seekTo(progress);
}
...
);
new Thread(new Runnable() {
@Override
public void run() {
if (flag) {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
handler.postDelayed(this, 1000);
}
}
}).start();
}
private void initMediaPlayer() {
try {
Music music = musicList.get(position);//得到当前播放音乐的路径
artistName = music.getArtistName();//获得歌手名
songName = music.getMusicName();//获得歌名
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);// 指定参数为音频文件
mediaPlayer.setDataSource(music.getUri());//为多媒体对象设置播放路径
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.prev://上一首
position = position == 0 ? musicList.size() - 1 : position - 1;
mediaPlayer.reset();
initMediaPlayer();
play.setText("■");
musicName.setText(songName);
artist.setText(artistName);
mediaPlayer.start();
break;
case R.id.play://开始
if (!mediaPlayer.isPlaying()) {
play.setText("■");
musicName.setText(songName);
artist.setText(artistName);
mediaPlayer.start();
} else {//暂停
play.setText("▶");
mediaPlayer.pause();
}
break;
case R.id.next://下一首
position = position == musicList.size() - 1 ? 0 : position + 1;
mediaPlayer.reset();
initMediaPlayer();
play.setText("■");
musicName.setText(songName);
artist.setText(artistName);
mediaPlayer.start();
break;
default:
break;
}
}
//顺序播放或单曲循环
public void automaticPlay() {
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
if (valid) {
position = position == musicList.size() - 1 ? 0 : position + 1;
mediaPlayer.reset();
initMediaPlayer();
play.setText("■");
musicName.setText(songName);
mediaPlayer.start();
//Log.d("valid", "true");
} else {
mediaPlayer.reset();
initMediaPlayer();
mediaPlayer.start();
Log.d("valid", "false");
}
}
});
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sequential_play:
valid = true;
Toast.makeText(this, "顺序播放", Toast.LENGTH_SHORT).show();
break;
case R.id.single_play:
valid = false;
Toast.makeText(this, "单曲循环", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
}
4结论与心得体会
通过对各个模块的调试,基本都实现了该系统的功能。