Simple music player for Android , the use of MediaPlayer
The functions of the currently implemented music player include: acquiring local music, playing music, pausing, switching songs, displaying the progress bar, controlling the playback position of the progress bar and other basic functions
Mainly used: RecyclerView display music list
MediaPlayer to play music
SeekBar shows progress bar and controls music progress
ContentProvider to get local music
After completion, the effect is as follows:
MainActivity.class
public class MainActivity extends AppCompatActivity implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaPlayer.OnCompletionListener { private MediaPlayer mediaPlayer; private MusicAdapter musicAdapter;//The adapter of the recyclerView to display the music list private List<MyMusic> musicList; private AppCompatSeekBar seekBar; private TextView timeStart, timeEnd; private int mPosition = -1;//Position the currently playing music private Button playB;//Play, pause Button @SuppressLint("HandlerLeak") private Handler handler = new Handler(new Handler.Callback() { //Dynamic update of seekBar is implemented here @Override public boolean handleMessage(Message message) { seekBar.setProgress(mediaPlayer.getCurrentPosition()); timeStart.setText(parseDate(mediaPlayer.getCurrentPosition())); updateProgress();//Send a message to update seekBar return true; } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView(R.layout.activity_main); requestPermission();//Get permission. After 6.0, reading files is set as a dangerous permission, and a runtime request is required initView(); queryMusic(); } private void requestPermission() { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == 1) { if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "The program will not work properly if you deny permission!", Toast.LENGTH_SHORT).show(); finish(); } } } @Override//Destroy mediaPlayer when exiting the program protected void onDestroy() { super.onDestroy (); if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); } } private void initView() { timeStart = findViewById(R.id.time_start); timeEnd = findViewById (R.id.time_end); seekBar = findViewById(R.id.seek_bar); seekBar.setOnSeekBarChangeListener(this); playB = findViewById(R.id.music_play); Button lastB = findViewById(R.id.last_music); Button nextB = findViewById(R.id.next_music); playB.setOnClickListener(this); lastB.setOnClickListener(this); nextB.setOnClickListener(this); //Initialize RecyclerView final RecyclerView musicListView = findViewById(R.id.music_list); musicList = new ArrayList<>(); musicListView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); musicAdapter = new MusicAdapter(musicList); musicListView.setAdapter(musicAdapter); musicAdapter.setSelected(-1); musicAdapter.setOnItemClickListener(new MusicAdapter.OnItemClickListener() { @Override public void onItemClick(View v, int position) { changeMusic(position); mPosition = position; } }); musicListView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); } private void queryMusic() { //Find out local music files (MP3) through Cursor Cursor cursor = getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); if (cursor != null) { musicList.clear(); while (cursor.moveToNext()) { //It is easy to see the attributes of the music files represented by the attribute names, so the attributes will not be explained. String id = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); String singer = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); int time = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); int size = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); //myMusic is a javaBean defined by myself to store music files MyMusic myMusic = new MyMusic(id, title, singer, path, size, time, album); musicList.add(myMusic); } musicAdapter.notifyDataSetChanged(); } } //Send an empty message every second, prompting the handler to update private void updateProgress() { handler.sendMessageDelayed(Message.obtain(), 1000); } //play music private void playMusic(MyMusic myMusic) { try { if (mediaPlayer == null) { //Determine whether it is empty to avoid repeated creation mediaPlayer = new MediaPlayer(); mediaPlayer.setOnCompletionListener(this); } mediaPlayer.reset();//Reset the player before playing. In fact, you don't need to do this operation for the first time, but for the reusability of this method, I choose to use it here mediaPlayer.setDataSource(myMusic.getPath());//Set the playback source mediaPlayer.prepare();//Prepare, this step is very important, it is essential when playing a new song mediaPlayer.start();//Start playing timeEnd.setText(parseDate(mediaPlayer.getDuration()));//Used to display the music duration seekBar.setMax(mediaPlayer.getDuration());//Set the duration of seekBar to be the same as the music file updateProgress();//Open the update of seekBar } catch (IOException e) { e.printStackTrace (); } } private String parseDate(int time) {//The time obtained by the cursor is milliseconds, here it is converted into a common time format time = time / 1000; int min = time / 60; int second = time % 60; return min + ":" + second; } private void changeMusic(int position) { //Implement song switching if (position < 0) { mPosition = musicList.size() - 1; playMusic(musicList.get(mPosition)); } else if (position > musicList.size() - 1) { mPosition = 0; playMusic (musicList.get (0)); } else { playMusic(musicList.get(position)); } musicAdapter.setSelected(mPosition); //Set the selected music //Update RecyclerView, the reason for this step is that I set two layouts, and the layout of the music row being played changes musicAdapter.notifyDataSetChanged(); playB.setBackgroundResource(R.drawable.ic_playing); //Update the icons of play and pause buttons } private void startOrPause() { //Play or pause logic implementation if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); playB.setBackgroundResource(R.drawable.ic_pause); } else { mediaPlayer.start(); playB.setBackgroundResource(R.drawable.ic_playing); } } @Override public void onClick(View view) { switch (view.getId()) { case R.id.last_music: //Last song changeMusic(--mPosition); break; case R.id.music_play: //Play/Pause if (mediaPlayer == null) { changeMusic(0); } else { startOrPause (); } break; case R.id.next_music://next song changeMusic(++mPosition); break; } } //The following three methods are the methods that OnSeekBarChangeListener needs to rewrite, here only the third one needs to be rewritten @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { mediaPlayer.seekTo(seekBar.getProgress());//Locate the music to the position specified by seekBar updateProgress(); } @Override public void onCompletion(MediaPlayer mediaPlayer) { //OnCompletionListener override method to achieve carousel effect changeMusic(++mPosition); } }
MusicAdapter.class
public class MusicAdapter extends RecyclerView.Adapter<MusicAdapter.ViewHolder> implements View.OnClickListener { private int selected; private List<MyMusic> musicList; private OnItemClickListener onItemClickListener; private Context mContext; @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { mContext=parent.getContext(); View view = LayoutInflater.from(mContext).inflate(R.layout.item_music_content, parent, false); ViewHolder holder = new ViewHolder(view); view.setOnClickListener(this); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { MyMusic music = musicList.get(position); holder.content.removeAllViews(); if (position==selected){ holder.content.addView(addFocusView()); TextView textView=holder.content.findViewById(R.id.music_playing); textView.setText("Playing:"+music.getName()); }else{ holder.content.addView(addNormalView()); TextView musicName=holder.content.findViewById(R.id.music_name); TextView musicSinger=holder.content.findViewById(R.id.music_singer); musicName.setText(music.getName()); musicSinger.setText(music.getSinger()); } holder.itemView.setTag(position); } @Override public int getItemCount() { return musicList.size(); } @Override public void onClick(View view) { if (onItemClickListener != null) { onItemClickListener.onItemClick(view, (Integer) view.getTag()); } } class ViewHolder extends RecyclerView.ViewHolder { RelativeLayout content; ViewHolder(View itemView) { super(itemView); content=itemView.findViewById(R.id.content); } } private View addFocusView(){ return LayoutInflater.from(mContext).inflate(R.layout.item_music_focus,null,false); } private View addNormalView(){ return LayoutInflater.from(mContext).inflate(R.layout.item_music_list,null,false); } public void setSelected(int selected) { this.selected = selected; } public MusicAdapter(List<MyMusic> musicList) { this.musicList = musicList; } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public interface OnItemClickListener { void onItemClick(View v, int position); } }