2021@SDUSC
移动互联网开发技术教学网站项目研究第二篇
歌曲相关方法分析
歌曲实体类
private String name;//歌曲名
private String singer;//歌手
private long size;//歌曲所占空间大小
private int duration;//歌曲时间长度
private String path;//歌曲地址
private long albumId;//图片id
private long id;//歌曲id
歌曲属性有:歌曲名、歌手、歌曲播放时间、存放在手机本地空间的地址,歌曲包的大小,歌曲封面、歌曲id。定义了getter和setter方法。
歌曲列表
获取本地音乐列表 private List<Song> pick() 该方法获取设备本地音乐列表,返回一个List<Song> 。
Cursor cursor= getApplicationContext().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,null,null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
用Cursor对象对Android本地的SQLite数据库进行操作,存放获取的歌曲列表信息。先获取应用程序环境的上下文,然后通过Android的内容提供器访问位于不同程序中的音乐文件数据,并且保证被访问数据的安全性。最后查询Android系统中媒体文件的音乐文件信息,从而获得本地音乐列表。 因为需要返回的音乐列表是全局数据,播放的Activity和其他view也需要访问它,不能随当前Activity关闭而消失,所以先用getApplicationContext() 获取应用程序环境的上下文,它返回的上下文会随应用存在而存在。如果用getApplication() 虽然与上述方法返回值相同,但是生命周期不同。 getContentResolver() 获取内容提供器中共享的数据,query() 提供查询操作,Audio,Files, Images, Video等媒体文件的URI信息和数据表结构信息封装在android.provider.MediaStore 类中,属性EXTERNAL_CONTENT_URI 明确标识访问的数据位于哪个程序某一张表里面的数据,属性DEFAULT_SORT_ORDER 为列表默认的排序方式。其他projection : selection: selectionArfs: 指定查询列的条件,不需要考虑,为null 。查询完成后返回的仍为Cursor对象,后面需要将数据从查询到的对象中读取出来。
if(cursor!=null){
while (cursor.moveToNext()){
song=new Song();
//获取名字等信息
String name=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
String singer=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
String path=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
int duration=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
long size=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));
long albumid=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
long id=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
//有的时候名字和歌手会放在一起
if(name.contains("-")){
String[] str=name.split("-");
singer=str[0];
name=str[1];
}
//把它放入song中
song.setName(name);
song.setSinger(singer);
song.setPath(path);
song.setDuration(duration);
song.setSize(size);
song.setAlbumId(albumid);
song.setId(id);
//把建好的歌曲信息放入list中
//除去Unknown的作者
if(!singer.contains("unknown")) {
list.add(song);
}
}
}
如果查询结果不为空,用方法moveToNext() 遍历Cursor cursor 对象的每一行,方法getColumnIndexOrThrow() 获取歌曲的各种信息。最后存放放到该方法开始建立的List<Song> list=new ArrayList<Song>(); 中,这样就获取了设备本地的歌曲列表。
音乐适配器——把数据映射到前端页面上的中介
ublic class SongAdapter extends ArrayAdapter {
private final int resourceid;
public SongAdapter(Context context, int textViewResourceid, List<Song> songs){
super(context,textViewResourceid,songs);
resourceid=textViewResourceid;
}
@SuppressLint("ResourceType")
public View getView(int positon , View convertView, ViewGroup parent){
Song song=(Song)getItem(positon);
View view = LayoutInflater.from(getContext()).inflate(resourceid,null);
ImageView songImage=(ImageView)view.findViewById(R.id.song_cover);
TextView textView=(TextView)view.findViewById(R.id.song_name);
TextView singer_View=(TextView)view.findViewById(R.id.song_singer);
songImage.setImageResource(R.raw.music);
textView.setText(song.getName());
singer_View.setText(song.getSinger());
return view;
}
}
为了将已取出的音乐列表显示到前端的页面中创建一个音乐适配器类SongAdapter ,继承ArrayAdapter 类。重写getView() 方法绘制页面,int positon 判断当前显示的条目在屏幕上的位置,然后通过getItem(positon) 在定义的列表中取值显示在屏幕上。当需要浏览已经加载过但是不在当前界面中的条目时,可以将这些条目缓存到View convertView ,提高效率。ViewGroup parent 存放当前界面的所有条目。根据资源布局idresourceid 找到音乐条目前端页面文件。findViewById(R.id.song_name) 在R中的索引按id找到音乐条目对应的song_name组件。setImageResource(R.raw.music) 找到歌曲的封面id。setText(song.getName()) 设置歌曲名称。最后返回绘制好的条目界面。
音乐列表显示和播放监听器void init()
SongAdapter songAdapter=new SongAdapter(
Music_List.this,R.layout.music_item,list
);
初始化一个歌曲适配器,music_item 是歌曲条目布局。
List<String> data=new ArrayList<String>();
Iterator<Song> iterator=list.iterator();
while (iterator.hasNext()){
Song song=(Song) iterator.next();
data.add(song.getName()+"-"+song.getSinger());
}
遍历歌曲列表,获取歌曲名称和歌手。
ListView listView=(ListView)findViewById(R.id.List_song);
listView.setAdapter(songAdapter);
添加歌曲列表List_song 布局,并为其添加适配器。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Song song = list.get(position);
//跳转到播放界面
if (intent == null) {
intent = new Intent(Music_List.this, MusicActivity.class);
intent.putExtra("path", song.getPath());
intent.putExtra("song_name", song.getName());
intent.putExtra("singer", song.getSinger());
finish();
startActivity(intent);
} else {
Toast.makeText(Music_List.this,"还有歌曲在播放",Toast.LENGTH_SHORT);
}
}
});
找到用户想要播放的歌曲,然后跳转到音乐播放界面MusicActivity 。如果当前有音乐在播放,弹出提示框两秒Toast.LENGTH_SHORT 。
权限管理
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode==1){
if(permissions[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE)&&grantResults[0]== PackageManager.PERMISSION_GRANTED){
//用户同意授权,执行读取文件的代码
list = pick();
init();
}else{
//若用户不同意授权,直接暴力退出应用。
finish();
}
}
}
读取手机的存储空间需要向用户获取权限,Android 6.0 之后需要动态获取权限,所以请求用户访问设备上文件的权限Manifest.permission.READ_EXTERNAL_STORAGE 。取得权限后就获取音乐列表并显示。如果用户拒绝则直接退出应用程序。
onCreate() 初始化
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.music_list);
//检查当前权限(若没有该权限,值为-1;若有该权限,值为0)
int hasReadExternalStoragePermission = ContextCompat.checkSelfPermission(getApplication(),Manifest.permission.READ_EXTERNAL_STORAGE);
if(hasReadExternalStoragePermission== PackageManager.PERMISSION_GRANTED){
list = pick();
init();
}else{
//若没有授权,会弹出一个对话框(这个对话框是系统的,开发者不能自己定制),用户选择是否授权应用使用系统权限
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
}
}
在 onCreate 函数中初始化调用上述方法,hasReadExternalStoragePermission== PackageManager.PERMISSION_GRANTED 检查用户是否给予权限,如果没有则弹出下图图所示对话框。
|