Android學習之如何讀取本地音樂以及圖片
最近做音樂播放器讀取本地音樂這一塊。簡單模仿網易雲音樂的介面。
首先我建議了一個music類
public class music {
private long id;
private long album_id;
private String title;
private String artist;
private long size;
private String url;
private int isMusic;
private long duration;
private String album;
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getIsMusic() {
return isMusic;
}
public void setIsMusic(int isMusic) {
this.isMusic = isMusic;
}
public long getAlbum_id() {
return album_id;
}
public void setAlbum_id (long album_id) {
this.album_id = album_id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
想要讀取本地的東西,肯定是用contentresolver了,得到cursor,進行查詢。
核心程式碼:
public List<music> getMusic(){
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
List<music> musicList = new ArrayList<>();
if(cursor.moveToFirst()){
for(int i = 0; i <cursor.getCount();i++) {
music m = new music();
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID));
String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
String url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
long album_id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
int ismusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));
if (ismusic != 0 && duration / (500 * 60) >= 1) {
m.setId(id);
m.setTitle(title);
m.setArtist(artist);
m.setDuration(duration);
m.setSize(size);
m.setUrl(url);
m.setAlbum(album);
m.setAlbum_id(album_id);
musicList.add(m);
}
cursor.moveToNext();
}
}
return musicList;
}
}
然後通過ListView展示出來。
ublic class bendiyinyueAdapter extends ArrayAdapter<music> {
Context context;
int res;
public bendiyinyueAdapter(Context context, int resource, List<music> objects) {
super(context, resource, objects);
this.context = context;
this.res = resource;
}
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
music item = getItem(position);
ViewHold viewHold;
View view;
if(convertView == null){
view= LayoutInflater.from(context).inflate(res,parent,false);
viewHold = new ViewHold();
viewHold.name = (TextView) view.findViewById(R.id.music_name);
viewHold.artist = (TextView)view.findViewById(R.id.artist);
view.setTag(viewHold);
}else {
view = convertView;
viewHold = (ViewHold)view.getTag();
}
viewHold.name.setText(item.getTitle());
viewHold.artist.setText(item.getArtist());
return view;
}
class ViewHold{
TextView name ;
TextView artist;
}
}
listView = (ListView)findViewById(R.id.bendiyinyue_list);
musics =getMusic();
adapter = new bendiyinyueAdapter(this,R.layout.bendi_item,musics);
listView.setAdapter(adapter);
效果:
接下來是得到專輯的圖片:
private static final Uri albumArtUri=Uri.parse(“content://media/external/audio/albumart”);
我一直在思考,我怎麼知道圖片的uri,然後我試著列印了一下:
Log.d(“aaaaa”,MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString();
得到的結果是:
content://media/external/audio/media
這是音訊媒體表的uri
然後我查詢API,發現了
MediaStore.Audio.Artists.Albums
列印它的所有的列:發現了album_art,它快取專輯封面。所以現在我們知道需要找album_art
所以當我們不知道albumid的時候,就通過歌曲來找:
Uri.parse(“content://media/external/audio/media/”+songid+”/albumart”);
接著通過uri來開啟這個檔案:
檔案描述符是程序用於讀取或寫入開啟檔案並開啟網路套接字的物件。
FileDescriptor可以編寫代表原始Linux檔案描述符識別符號的物件,並ParcelFileDescriptor返回對對原始檔案描述符操作的物件。返回的檔案描述符是原始檔案描述符的dup:物件和fd不同,但在相同的底層檔案流上操作,具有相同的位置。
然後獲取Bitmap
public static Bitmap getArtWorkFormFile(ContentResolver resolver,int songid,int album){
Bitmap bitmap = null;
if(album <0 &&songid <0){
throw new IllegalArgumentException("Must specify an album or song");
}
try{
BitmapFactory.Options options = new BitmapFactory.Options();
FileDescriptor fileDescriptor = null;
if(album <0){
Uri uri = Uri.parse("content://media/external/audio/media/"+songid+"/albumart");
ParcelFileDescriptor parcelFileDescriptor = resolver.openFileDescriptor(uri,"r");
if(parcelFileDescriptor !=null){
fileDescriptor = parcelFileDescriptor.getFileDescriptor();
}
}else {
Uri uri = ContentUris.withAppendedId(albumArtUri,album);
ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri,"r");
if(pfd != null){
fileDescriptor = pfd.getFileDescriptor();
}
}
options.inSampleSize = 1;
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
options.inSampleSize = calculateInSampleSize(options,50,50);
options.inJustDecodeBounds =false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
}catch (FileNotFoundException e){
e.printStackTrace();
}
return bitmap;
}
當我知道albumid的時候,直接用流來進行讀取:
public static Bitmap getArtwork(ContentResolver resolver,int songid,int albumid ,boolean allowdefalut,boolean samll){
if(albumid <0 ){
if(songid <0){
Bitmap bitmap = getArtWorkFormFile(resolver,songid,albumid);
if(bitmap !=null){
return bitmap;
}
}
return null;
}
Uri uri = ContentUris.withAppendedId(albumArtUri,albumid);
if(uri != null){
InputStream inputStream = null;
try{
inputStream = resolver.openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream,null,options);
if(samll){
options.inSampleSize = calculateInSampleSize(options,50,50);
}else {
options.inSampleSize = calculateInSampleSize(options,600,600);
}
options.inJustDecodeBounds= false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
inputStream = resolver.openInputStream(uri);
return BitmapFactory.decodeStream(inputStream,null,options);
}catch (FileNotFoundException e){
Bitmap bitmap = getArtWorkFormFile(resolver,songid,albumid);
if(bitmap != null){
if(bitmap.getConfig() == null){
bitmap = bitmap.copy(Bitmap.Config.RGB_565,false);
if(bitmap == null && allowdefalut){
return null;
}
}
}
return bitmap;
}
}
return null;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public Bitmap.Config inPreferredConfig
在API級別1中新增
如果這是非空的,解碼器將嘗試將其解碼為該內部配置。如果它為空,或者不能滿足請求,則解碼器將嘗試根據系統的螢幕深度和原始影象的特徵來選擇最佳匹配配置,例如,如果它具有每畫素alpha(需要一個配置也可以一樣)。ARGB_8888預設情況下,影象載入了配置。
Bitmap.Config:
可能的點陣圖配置。點陣圖配置描述畫素的儲存方式。這會影響質量(顏色深度)以及顯示透明/半透明顏色的能力。
Bitmap.Config .ALPHA_8 每個畫素儲存為單透明(alpha)通道。
Bitmap.Config .ARGB_4444 這個欄位在API級別13中已被棄用。由於此配置的質量差,建議使用ARGB_8888。
Bitmap.Config .ARGB_8888 每個畫素儲存在4個位元組上。每個通道(用於半透明的RGB和alpha)以8位精度儲存(256個可能的值)。該配置非常靈活,提供最好的質量。應儘可能使用。
Bitmap.Config .RGB_565 每個畫素儲存在2個位元組上,只有RGB通道被編碼:紅色儲存有5位精度(32個可能值),綠色以6位精度儲存(64個可能值),藍色儲存5位精確。 此配置可能會根據源的配置產生輕微的視覺偽影。例如,沒有抖動,結果可能會顯示綠色的色調。為了獲得更好的效果,應該應用抖動。當使用不需要高色彩保真度的不透明點陣圖時,此配置可能很有用。