项目中用到了MediaPlayer播放音频,趁这两天比较闲,试着写了一个音频播放器,还有很多不完善,仅当练手。实现了播放、暂停、退出,上下切歌,扫描音频文件并展示,点击音频列表可播放,打开指定音频文件播放的功能,不过功能之间并没有做特殊处理,所以多个功能之间使用会出现崩溃(笑哭)。下面是效果图:

已知问题:
1. 目前打开目录查找文件功能在PAD(安卓5.1)上可以,手机上(安卓8.0)上会崩溃。
2. 这个布局主要是针对手头上的PAD,对手机布局没有优化,同时布局也比较丑,但是这次Demo主要以功能练习为主,所以布局上也不再优化。
3. 因此Demo仅用于MediaPlayer学习,目前所做内容已超过预期。加之下周需要述职,没有精力花费在此,故代码逻辑并不严谨,封装也不好, 留待后期优化。

可优化:
后期打算增加播放时当前播放歌曲左侧列表高亮。
UI布局优化(不可能的,就是如此粗犷的人)

下面贴代码:

MainActivity.java,基本功能都是在此完成。

package com.example.qxb_810.audioplaydemo;import android.Manifest;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;import com.example.qxb_810.bean.ScanFile;
import com.example.qxb_810.utils.GetPathFromUri4kitkat;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;import butterknife.BindView;
import butterknife.ButterKnife;/*** author XXX* email XXX@XXXXXX.com* create 2018/9/5 19:08* desc  音乐播放  -- mediaPlayer的使用*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {@BindView(R.id.tv_play)TextView tvPlay;@BindView(R.id.tv_resume)TextView tvResume;@BindView(R.id.tv_stop)TextView tvStop;@BindView(R.id.sb_progress)SeekBar sbProgress;@BindView(R.id.tv_currprogress)TextView tvCurrprogress;@BindView(R.id.tv_maxprogress)TextView tvMaxprogress;@BindView(R.id.iv_pic)ImageView ivPic;@BindView(R.id.tv_find_file)TextView tvFindFile;@BindView(R.id.tv_scan_file)TextView tvScanFile;@BindView(R.id.tv_last)TextView tvLast;@BindView(R.id.tv_next)TextView tvNext;@BindView(R.id.tv_title)TextView tvTitle;@BindView(R.id.tv_artist)TextView tvArtist;@BindView(R.id.rv_file_list)RecyclerView rvFileList;private MediaPlayer mMediaPlayer;private int mDuration = 0;private boolean isMusicLoadSuccess = false;private boolean isPlayPause = false;private String mPath;private int mCurrPostion = 0;private MyAdapter myAdapter;private ObjectAnimator animator;private List<ScanFile> fileList = new ArrayList<ScanFile>();private Set<String> filePathSet = new HashSet<String>();private List<String> fileNameList = new ArrayList<String>();private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 123) {sbProgress.setProgress(mMediaPlayer.getCurrentPosition());tvCurrprogress.setText(formatDuring(mMediaPlayer.getCurrentPosition()));mHandler.sendEmptyMessage(123);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);mPath = null;try {requestPermissions();initMediaPlayer();initRecyclerView();initEvent();} catch (IOException e) {e.printStackTrace();}}/*** 动态申请权限  ----  API23后需要动态申请权限*/private void requestPermissions() {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {// 读写存储权限 和联网权限ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET}, 1);}}/*** 初始化RecyclerView*/private void initRecyclerView() {rvFileList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));myAdapter = new MyAdapter(fileList, this);rvFileList.setAdapter(myAdapter);}/*** 初始化MediaPlayer** @throws IOException*/private void initMediaPlayer() throws IOException {mMediaPlayer = new MediaPlayer();if (mPath != null && !mPath.equals("")) {mMediaPlayer.setLooping(false);  // 设置单曲循环mMediaPlayer.setDataSource(mPath);mMediaPlayer.prepareAsync();    // 异步加载音频mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// 异步加载结束后再回调isMusicLoadSuccess = true;mDuration = mMediaPlayer.getDuration();tvMaxprogress.setText(formatDuring(mDuration));sbProgress.setMax(mDuration);   // 设置进度条最大值}});}animator = ObjectAnimator.ofFloat(ivPic, "rotation", 360.0f);animator.setDuration(10000);    // 设置转速,越大越慢animator.setInterpolator(new LinearInterpolator());animator.setRepeatCount(-1);ivPic.setImageBitmap(null);if (getAudioPic() != null) {ivPic.setImageBitmap(getAudioPic());}ivPic.setBackgroundResource(R.drawable.shape_default_pic);}/*** 初始化事件*/private void initEvent() {tvPlay.setOnClickListener(this);tvResume.setOnClickListener(this);tvStop.setOnClickListener(this);tvFindFile.setOnClickListener(this);tvScanFile.setOnClickListener(this);tvLast.setOnClickListener(this);tvNext.setOnClickListener(this);mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {changeMusic(++mCurrPostion);}});sbProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {tvCurrprogress.setText(formatDuring(seekBar.getProgress()));}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {mMediaPlayer.seekTo(seekBar.getProgress());}});}/*** 获取音乐文件图片** @return*/public Bitmap getAudioPic() {try {Uri selectedAudio = Uri.parse(mPath);MediaMetadataRetriever myRetriever = new MediaMetadataRetriever();myRetriever.setDataSource(this, selectedAudio); // the URI of audio filebyte[] artwork;artwork = myRetriever.getEmbeddedPicture();if (artwork != null) {return getCircleBitmap(BitmapFactory.decodeByteArray(artwork, 0, artwork.length));} else {return null;}} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取圆形图片** @param bitmap* @return*/private Bitmap getCircleBitmap(Bitmap bitmap) {Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(output);Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());Paint paint = new Paint();paint.setAntiAlias(true);canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getHeight() / 2, paint);paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(bitmap, rect, rect, paint);//将图片画出来return output;}/*** 扫描音乐文件信息** @return*/public void scanAudioFile() {fileList.clear();filePathSet.clear();fileNameList.clear();Cursor cursor = this.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);for (int i = 0; i < cursor.getCount(); i++) {cursor.moveToNext();int id = (int) cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID));   //音乐idString title = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.Media.TITLE))); // 音乐标题String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); // 艺术家String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)); //专辑String displayName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));int albumId = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));int duration = (int) 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)); // 文件路径int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC)); // 是否为音乐if (1 == isMusic && duration > 1000 * 60 && !filePathSet.contains(url)) {    // 大于1min的音乐文件且未添加过ScanFile file = new ScanFile(id, title, artist, album, displayName, url, albumId, duration, size, isMusic);filePathSet.add(file.getUrl());fileNameList.add(file.getTitle());fileList.add(file);}}myAdapter.notifyDataSetChanged();Log.e("list", fileList.toString());}/*** 切换歌曲** @param postion*/private void changeMusic(int postion) {if (fileList.size() > 0) {mCurrPostion = 0;sbProgress.setProgress(0);mHandler.removeMessages(123);// 播放结束mMediaPlayer.stop();if (postion >= fileList.size()) {postion = 0;} else if (postion <= 0) {postion = fileList.size() - 1;}tvArtist.setText("演唱者: " + fileList.get(postion).getArtist());tvTitle.setText("歌曲名: " + fileList.get(postion).getTitle());ScanFile scanFile = fileList.get(mCurrPostion = postion);mPath = scanFile.getUrl();try {initMediaPlayer();} catch (IOException e) {e.printStackTrace();}} else {Toast.makeText(getApplicationContext(), "暂无音乐,请扫描...", Toast.LENGTH_SHORT).show();}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.tv_play:if (isMusicLoadSuccess) {if (mMediaPlayer.isPlaying()) {Toast.makeText(getApplicationContext(), "播放中...", Toast.LENGTH_SHORT).show();} else {if (fileList.size() == 0) {Toast.makeText(getApplicationContext(), "暂无音乐,请扫描...", Toast.LENGTH_SHORT).show();} else {mMediaPlayer.start();animator.start();mHandler.sendEmptyMessage(123);}}} else {Toast.makeText(getApplicationContext(), fileList.size() > 0 ? "加载中..." : "暂无音乐,请扫描...", Toast.LENGTH_SHORT).show();}break;case R.id.tv_resume:if (!isPlayPause) {if (mMediaPlayer.isPlaying()) {isPlayPause = true;mDuration = mMediaPlayer.getCurrentPosition();mMediaPlayer.pause();animator.pause();tvResume.setText("继续");} else {Toast.makeText(getApplicationContext(), "还未播放...", Toast.LENGTH_SHORT).show();}} else {isPlayPause = false;mMediaPlayer.start();animator.resume();tvResume.setText("暂停");}break;case R.id.tv_stop:mDuration = 0;sbProgress.setProgress(0);mMediaPlayer.reset();mMediaPlayer.stop();animator.cancel();mHandler.removeMessages(123);break;case R.id.tv_find_file:File parentFlie = new File(Environment.getExternalStorageDirectory().getAbsolutePath());Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);intent.setDataAndType(Uri.fromFile(parentFlie), "*/*");//   intent.setDataAndType(FileProvider.getUriForFile(getApplicationContext(), "com.example.qxb_810.fileprovider", parentFlie), "*/*");     // 设置打开文件类型,这么没设置类型//      intent.setType(“image/*”);//intent.setType(“audio/*”); //选择音频//intent.setType(“video/*”); //选择视频 (mp4 3gp 是android支持的视频格式)//intent.setType(“video/*;image/*”);//同时选择视频和图片intent.addCategory(Intent.CATEGORY_OPENABLE);startActivityForResult(intent, 111);break;case R.id.tv_scan_file:Toast.makeText(getApplicationContext(), "扫描中...", Toast.LENGTH_SHORT).show();scanAudioFile();Toast.makeText(getApplicationContext(), "扫描结束,共扫描到" + fileList.size() + "个音频...", Toast.LENGTH_SHORT).show();break;case R.id.tv_last:changeMusic(--mCurrPostion);break;case R.id.tv_next:changeMusic(++mCurrPostion);}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (resultCode == Activity.RESULT_OK) {//是否选择,没选择就不会继续Uri uri = data.getData();//得到uri,后面就是将uri转化成file的过程。mPath = GetPathFromUri4kitkat.getPath(this, uri);try {if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();animator.cancel();isMusicLoadSuccess = false;initMediaPlayer();}} catch (IOException e) {e.printStackTrace();}}}/*** 换算时间** @param time 时间* @return*/public static String formatDuring(long time) {long minutes = (time % (1000 * 60 * 60)) / (1000 * 60);long seconds = (time % (1000 * 60)) / 1000;String min = minutes < 10 ? "0" + minutes : minutes + "";String sec = seconds < 10 ? "0" + seconds : seconds + "";return min + ":" + sec;}@Overrideprotected void onDestroy() {super.onDestroy();mHandler.removeMessages(123);if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();}}public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {private List<ScanFile> list = new ArrayList<>();private Context context;public MyAdapter(List<ScanFile> list, Context context) {this.list = list;this.context = context;}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.simple_adapter_item, parent, false);MyViewHolder viewHolder = new MyViewHolder(view);return viewHolder;}@Overridepublic void onBindViewHolder(final MyViewHolder holder, final int position) {holder.tvTitle.setText(list.get(position).getTitle());holder.llContent.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {changeMusic(position);mHandler.sendEmptyMessage(123);mMediaPlayer.start();}});}@Overridepublic int getItemCount() {return list.size();}class MyViewHolder extends RecyclerView.ViewHolder {@BindView(R.id.tv_title)TextView tvTitle;@BindView(R.id.ll_content)LinearLayout llContent;public MyViewHolder(View itemView) {super(itemView);ButterKnife.bind(this, itemView);}}}
}

MainActivity布局,没错,如此丑陋的布局就是下面这个文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp"android:visibility="visible"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_file_list"android:layout_width="wrap_content"android:layout_height="300dp"></android.support.v7.widget.RecyclerView></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_pic"android:layout_width="300dp"android:layout_height="300dp"android:layout_marginBottom="20dp" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="暂无标题" /><TextViewandroid:id="@+id/tv_artist"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="20dp"android:layout_marginTop="20dp"android:text="演唱人" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_currprogress"android:layout_width="80dp"android:layout_height="wrap_content"android:layout_alignLeft="@+id/sb_progress"android:gravity="center"android:text="00:00" /><SeekBarandroid:id="@+id/sb_progress"android:layout_width="500dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical" /><TextViewandroid:id="@+id/tv_maxprogress"android:layout_width="80dp"android:layout_height="wrap_content"android:gravity="center"android:text="00:00" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:gravity="center"><TextViewandroid:id="@+id/tv_last"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="上一首" /><TextViewandroid:id="@+id/tv_play"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="播放" /><TextViewandroid:id="@+id/tv_resume"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="暂停" /><TextViewandroid:id="@+id/tv_stop"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="退出" /><TextViewandroid:id="@+id/tv_find_file"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="打开目录" /><TextViewandroid:id="@+id/tv_scan_file"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="文件扫描" /><TextViewandroid:id="@+id/tv_next"android:layout_width="80dp"android:layout_height="35dp"android:layout_margin="5dp"android:background="@color/colorAccent"android:gravity="center"android:text="下一首" /></LinearLayout></LinearLayout>
</FrameLayout>

GetPathFromUri4kitkat类,这个是从网上找的,因为需要转相应路径

package com.example.qxb_810.utils;import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;public class GetPathFromUri4kitkat {/*** 专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使*/@SuppressLint("NewApi")public static String getPath(final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;// DocumentProviderif (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {// ExternalStorageProviderif (isExternalStorageDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];if ("primary".equalsIgnoreCase(type)) {return Environment.getExternalStorageDirectory() + "/" + split[1];}// TODO handle non-primary volumes}// DownloadsProviderelse if (isDownloadsDocument(uri)) {final String id = DocumentsContract.getDocumentId(uri);final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));return getDataColumn(context, contentUri, null, null);}// MediaProviderelse if (isMediaDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];Uri contentUri = null;if ("image".equals(type)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;} else if ("video".equals(type)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;} else if ("audio".equals(type)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;}final String selection = "_id=?";final String[] selectionArgs = new String[]{split[1]};return getDataColumn(context, contentUri, selection, selectionArgs);}}// MediaStore (and general)else if ("content".equalsIgnoreCase(uri.getScheme())) {return getDataColumn(context, uri, null, null);}// Fileelse if ("file".equalsIgnoreCase(uri.getScheme())) {return uri.getPath();}return null;}/*** Get the value of the data column for this Uri. This is useful for* MediaStore Uris, and other file-based ContentProviders.** @param context       The context.* @param uri           The Uri to query.* @param selection     (Optional) Filter used in the query.* @param selectionArgs (Optional) Selection arguments used in the query.* @return The value of the _data column, which is typically a file path.*/public static String getDataColumn(Context context, Uri uri, String selection,String[] selectionArgs) {Cursor cursor = null;final String column = "_data";final String[] projection = {column};try {cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null);if (cursor != null && cursor.moveToFirst()) {final int column_index = cursor.getColumnIndexOrThrow(column);return cursor.getString(column_index);}} finally {if (cursor != null)cursor.close();}return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/public static boolean isExternalStorageDocument(Uri uri) {return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/public static boolean isDownloadsDocument(Uri uri) {return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/public static boolean isMediaDocument(Uri uri) {return "com.android.providers.media.documents".equals(uri.getAuthority());}
}

ScanFile — bean类

package com.example.qxb_810.bean;/**
* author XXX
* email XXX@XXXXXX.com
* create 2018/9/6 14:23
* desc 音乐bean
*/public class ScanFile {private int id;private String title;private String artist;private String album;private String displayName;private String url;private int albumId;private int duration;private long size;private int isMusic;public ScanFile() {}public ScanFile(int id, String title, String artist, String album, String displayName, String url, int albumId, int duration, long size, int isMusic) {this.id = id;this.title = title;this.artist = artist;this.album = album;this.displayName = displayName;this.url = url;this.albumId = albumId;this.duration = duration;this.size = size;this.isMusic = isMusic;}public int getId() {return id;}public void setId(int id) {this.id = 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 String getAlbum() {return album;}public void setAlbum(String album) {this.album = album;}public String getDisplayName() {return displayName;}public void setDisplayName(String displayName) {this.displayName = displayName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public int getAlbumId() {return albumId;}public void setAlbumId(int albumId) {this.albumId = albumId;}public int getDuration() {return duration;}public void setDuration(int duration) {this.duration = duration;}public long getSize() {return size;}public void setSize(long size) {this.size = size;}public int getIsMusic() {return isMusic;}public void setIsMusic(int isMusic) {this.isMusic = isMusic;}@Overridepublic String toString() {return "ScanFile{" +"id=" + id +", title='" + title + '\'' +", artist='" + artist + '\'' +", album='" + album + '\'' +", displayName='" + displayName + '\'' +", url='" + url + '\'' +", albumId=" + albumId +", duration=" + duration +", size=" + size +", isMusic=" + isMusic +'}';}
}

接下来就是一些简单的布局文件

shape_default_pic

<?xml version="1.0" encoding="UTF-8"?>
<shapexmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval"android:useLevel="false" ><solid android:color="@color/colorAccent" /><paddingandroid:left="2dp"android:top="1dp"android:right="2dp"android:bottom="1dp" /><solidandroid:color="@color/colorAccent" /><strokeandroid:width="10dp"android:color="@color/colorPrimary"/><size android:width="15dp"android:height="15dp" />
</shape>

simple_adapter_item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:id="@+id/ll_content"><TextViewandroid:id="@+id/tv_title"android:layout_width="200dp"android:layout_height="wrap_content"android:text="标题"android:singleLine="true"android:textSize="25dp"/><Viewandroid:background="@color/colorPrimary"android:layout_width="match_parent"android:layout_height="1dp"/>
</LinearLayout>

OK,项目大致就是这些文件。作为第一个做出来的稍微像样点的小Demo,代码里面仍有很多不足需要改进,等下周述职结束后再进行优化。

安卓MediaPlayer实现自定义音乐播放器相关推荐

  1. Android开发笔记(一百二十六)自定义音乐播放器

    MediaRecorder/MediaPlayer 在Android手机上面,音频的处理比视频还要复杂,这真是出人意料.在前面的博文< Android开发笔记(五十七)录像录音与播放>中, ...

  2. Android 自定义音乐播放器实现

    Android自定义音乐播放器 一:首先介绍用了哪些Android的知识点: 1 MediaPlayer工具来播放音乐 2 Handle.因为存在定时任务(歌词切换,动画,歌词进度条变换等)需要由Ha ...

  3. Vue 自定义音乐播放器组件为H5添加背景音乐

    自定义音乐播放器组件为H5添加背景音乐: 1.创建music.vue组件 <template><div><div @click="changeOn" ...

  4. 安卓使用服务完成音乐播放器

    安卓音乐播放器 使用Service后台服务实现播放器 自定义封装Music类 利用Fragment+RadioGroup实现页面 自定义Music类 适配器 音乐展示布局文件 Fragment音乐界面 ...

  5. 安卓Service实现通知栏音乐播放器,切换歌曲,类似QQ音乐

    引言: 这样的一个音乐播放器,用到了安卓四大组件的其中三个,等于说是一个比较综合性的小功能.实现方法其实有很多,我这里给出自己的方法,不喜勿喷. 需求分析 1.音乐播放器,那我们需要一个帮助类,来构建 ...

  6. 实现简单的自定义音乐播放器

    这篇博客只是记录自己写的js插件,着重点在于js,而不是css或者html.所以在js方面会比较详细,而其他的就只是简单提提. 刚学前端js那会,只是应付式的把书看完了,demo也没写几个.碰巧这学期 ...

  7. android音乐播放器上一首,安卓源码(音乐播放器,有播放和下一首,和进度条等功能)...

    [实例简介] 安卓音乐播放器,可以完美播放音乐,放下使用,在安卓eclipse下使用. [实例截图] [核心代码] Player └── Player ├── AndroidManifest.xml ...

  8. poweramp android,Poweramp - 安卓上的王牌音乐播放器 - Android 应用 - 【最美应用】

    智能手机上一个永恒不变的消遣方式就是听音乐,这也导致了 MP3 伴随智能手机的普及而逐渐消失.对于形形色色的安卓手机来说,系统自带的音乐播放器基本就是一个棒槌.如果找到一款好用的安卓音乐播放器对你来说 ...

  9. python开发安卓盒子_python实现音乐播放器 python实现花框音乐盒子

    本文实例为大家分享了python实现音乐播放器的具体代码,供大家参考,具体内容如下 """这是一个用海龟画图模块和pygame的混音模块制作的简易播放器. 作者:李兴球, ...

最新文章

  1. 回归架构本真:从规划、思维到设计,构建坚不可摧的架构根基
  2. ES6相关特性的整理(变量数据结构)
  3. where does watchers in scope come from
  4. css blink不闪烁_使它闪烁HTML教程–如何使用Blink标签以及代码示例
  5. stream实现list根据对象中多个属性分组,并取分组后最新数据
  6. Android开发笔记(八十四)使用Properties读写属性值
  7. 千兆云路由器Dlink850L10个0Day漏洞成筛子 PoC满天飞 随便拿Root权限
  8. python中oxf_python的strip()函数不工作
  9. 二维haar小波matlab_MATLAB实验之二维小波变换[附效果图]
  10. 求职、跳槽中英文简历模板下载集合
  11. 单片机USB2.0高速接口实现方案
  12. U8常用的二次开发方式
  13. html表单的put方法,form表单put、delete方式提交处理
  14. java.util.LinkedHashMap cannot be cast to 问题
  15. 【Pandas处理CSV】Error tokenizing data. C error: Expected 1 fields in line XX, saw XX
  16. 【学习记录贴】08:Arcgis Pro导入二维矢量拉伸,并用三维模型替换【未做完】
  17. 最近写了一个预测体彩3D的小软件
  18. A. BNU ACM校队时间安排表
  19. 【软件技术基础】国际语言代码LanguageCode
  20. 汽车驾驶盲区 无论新手老手都要看看

热门文章

  1. linux ps uptime,2、uptime命令
  2. sqlserver yyyyMMddHHmmss格式 转换为 日期格式
  3. 详解三次握手和四次挥手 — 遇到心动的女孩时,如何去把握?
  4. 实名认证,相关名称解释;信息比对;个人要素;运营商要素;银行卡要素;组织机构要素
  5. linux mkfs 命令 详解,mkfs命令详解
  6. 怎么样使用扫描文字识别软件
  7. iphone导出视频 无法连接到设备_Mac上iPhone数据恢复软件 Cisdem iPhone Recovery 破解版...
  8. python分布式框架有哪些_python分布式框架rq的使用
  9. Python实现淘宝京东秒杀!源码拿去吧!
  10. 2.5.13 动态内存扩展AME