Android入门之本地音乐播放器

article/2025/10/24 1:53:13

功能简介

  • 读取模拟器中音乐文件;
  • 列表展示,获取歌曲详细信息:歌名、歌手名、专辑名、专辑封面、播放时间等;
  • 基本功能:上下切歌,播放暂停、进度条显示与点击跳转。
  • 成品效果图:
    Android音乐播放器

Mediaplayer基本状态

Mediaplayer播放器主要涉及以下状态,通过调用图中函数进行状态跳转。

  • Idle //mPlayer刚被创建或者重置之后还未被初始化时的状态;
  • Initialized //setDataSource()加载资源后进入初始化状态;
  • Preparing和Prepared //Preparing是准备中状态, Prepared是就绪状态,可进行播放属性的设置。
  • Started和Paused //播放和暂停状态,调用start()和pause()进行跳转
  • Stop //停止状态,当在某个时刻调用了stop()之后,就需要再次调用prepare()使mPlayer转到就绪状态。
  • PlaybackCompleted //播放完毕状态,如果设置了循环播放模式,当播放完毕的时候,mPlayer会重新进入到Started状态
  • End //End是结束状态,当调用release()时,会释放播放引擎中与mPlayer相关的资源,这时就无法再转换到其他状态了。
  • Error //当mPlayer出现一些错误(例如,格式错误),就会调用OnErrorListener.OnError()方法进入到Error状态。
    在这里插入图片描述

项目难点详解

添加资源文件

  • 工程项目添加
    项目res资源文件夹 -> 新建raw资源文件夹 -> 添加资源
    raw资源加载
  • 模拟器添加音乐
    1.打开模拟器存储目录,运行模拟器 -> View -> Tools Windows -> Device File Explorer
    2.存储音乐至sdcard/Music/storage/emulated/0/Music/
    3.右键点击Upload,选择音乐资源加载。
    在这里插入图片描述

权限

  • 静态权限
    读权限&写权限,直接声明使用
    [AndroidManifest.xml]
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  • 动态权限
    因为涉及SD卡读取,需要使用动态权限,Android6.0后权限分为静态权限与动态权限,需要分开定义。如果读取工程项目中指定资源资源文件,使用静态权限即可。
    [MainActivity.java] 中声明使用,分为权限获取函数及其回调函数
 //动态权限申请函数if (ContextCompat.checkSelfPermission( MainActivity.this,Manifest.permission.WRITE_APN_SETTINGS ) != PackageManager.PERMISSION_GRANTED)    //判断是否已经授权{//申请权限 三个参数:1.Activity实例;2.String数组,存放权限;3.唯一请求码ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}, 1 );}else{//填写功能函数loadLocalMusicData();          //权限请求失败,则加载本地数据源,请求成功则调用回调函数中的加载,不管成功与否都会调用}//动态权限获取函数的回调函数@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult( requestCode, permissions, grantResults );switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {//填写功能函数loadLocalMusicData();    //申请权限成功,扫描模拟器存储,加载资源} else {finish();Toast.makeText( this, "denied!", Toast.LENGTH_SHORT ).show();}}}

媒体库资源更新

在模拟器中添加资源,并通过动态权限进行读取的过程中,Android系统并不会时时全局扫描资源,通常开关机模拟器时才扫描,所以需要进行媒体库的主动扫描资源

private void loadLocalMusicData() {/* 更新媒体库mp3文件*/String file_path = "storage/emulated/0/Music";MediaScannerConnection.scanFile( this,new String[]{file_path.toString()},
//                null,new String[]{"mp3"}, //指定扫描mp3文件,缩短时间new MediaScannerConnection.OnScanCompletedListener() {@Overridepublic void onScanCompleted(String path, Uri uri) {Log.i( "ExternalStorage", "Scanned" + path + ":" );Log.i( "ExternalStorage", "->" + uri);}} );

读取Android多媒体资源文件

这里使用MediaStore库,其是android系统提供的一个多媒体数据库,专门用于存放多媒体信息的,通过ContentResolver即可对数据库进行操作。

  • MediaStore.Files: 共享的文件,包括多媒体和非多媒体信息
  • MediaStore.Audio: 存放音频信息
  • MediaStore.Image: 存放图片信息
  • MediaStore.Vedio: 存放视频信息

模块化读取资源信息,如mp3文件中的歌曲名、歌手名、专辑名、时长、存放路径等信息。

  • MediaStore.Audio.Media.TITLE //歌曲标题
  • MediaStore.Audio.Media.ARTIST //歌手名
  • MediaStore.Audio.Media.ALBUM //专辑名
  • MediaStore.Audio.Media.DATA //路径
  • MediaStore.Audio.Media.DURATION //视频时长
  • MediaStore.Audio.Media.ALBUM_ID //相册名
    [MainActivity.java]
 /* 加载本地存储当中的音乐mp3文件到集合当中*/
//        1.获取ContentResolver对象ContentResolver resolver = getContentResolver();
//        2.获取本地音乐存储的Uri地址Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
//        3 开始查询地址 查询接口Cursor cursor = resolver.query(uri, null, null, null, null);
//        4.遍历Cursorint id = 0;while (cursor.moveToNext()) {String song = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));    //歌曲标题String singer = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); //歌手名String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));   //专辑名id++;String sid = String.valueOf(id);                                                        //序列号String path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));     //路径long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)); //视频时长SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");String time = sdf.format(new Date(duration));
//          获取专辑图片主要是通过album_id进行查询String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));//相册名String albumArt = getAlbumArt(album_id);
//            将一行当中的数据封装到对象当中LocalMusicBean bean = new LocalMusicBean(sid, song, singer, album, time, path, albumArt); //每一首歌的信息封装mDatas.add(bean);}
//        数据源变化,提示适配器更新adapter.notifyDataSetChanged();

获取歌曲封面

//获取歌曲的封面private void loadCover(String path){MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();mediaMetadataRetriever.setDataSource( path );byte[] cover = mediaMetadataRetriever.getEmbeddedPicture();Bitmap bitmap = BitmapFactory.decodeByteArray( cover, 0, cover.length );albumIv.setImageBitmap( bitmap );}

获取歌曲的时间以及进度条跳转

  • 两个TextView:tv_star, tv_end分别时时显示当前时间,时间总长;
  • 一个SeekBar实现进度动态显示与跳转;
  • 延时50ms更新时间进度条;
  • SeekBar回调函数,点击时、移动时、确定时。

private void SeekBarSet(){/*新增seekbar进度条*/int duration = mediaPlayer.getDuration();   //获取视频总时间seekBar.setMax( duration );     //将音乐总时间设置为Seekbar的最大值Timer timer = new Timer(  );timer.schedule( new TimerTask() {@Overridepublic void run() {
//                    if (!currentPausePositionInSong){seekBar.setProgress( mediaPlayer.getCurrentPosition() );
//                    }}},0,50);seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int i, boolean b) {int time_end = mediaPlayer.getDuration() / 1000 ;   //获取视频时长tv_end.setText(calaculateTime( time_end ));int time_now = mediaPlayer.getCurrentPosition() / 1000;tv_star.setText(calaculateTime( time_now ) );}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {mediaPlayer.seekTo( seekBar.getProgress() );
//                    tv_star.setText(formattime( mediaPlayer.getCurrentPosition() ));}} );
}

项目部分代码

使用控件列表

控件类型功能详细控件
ImageView图形按键上一首、下一首、播放、暂停
TextView文本栏歌曲名、歌手名、专辑名、单前播放时间、总时长
RecyclerView滑动列表加载各歌曲信息的列表
LocalMusicAdapter横条信息展示栏加载每一首歌曲信息的基本信息
SeekBar进度条显示当前歌曲进度信息

基本控件为以上所述,前端代码就不贴了

项目详细代码

[MainActivity.java] 主类,包含各控件定义与各监听器定义及功能

package com.example.musicplayer_2;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import android.Manifest;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private MediaPlayer mediaPlayer;private ImageView nextIv,playIv,lastIv,albumIv; //图像控件:private TextView singerTv,songTv;               //文本信息:歌手名、歌名private RecyclerView musicRv;                   //类似ListView 可横向滚动private SeekBar seekBar;private TextView tv_star, tv_end;private List<LocalMusicBean> mDatas;            //数据源 自定义类数组 存放各歌曲信息private LocalMusicAdapter adapter;              //歌曲横条信息控件设置private int currnetPlayPosition = -1;           //记录当前正在播放的音乐的位置private int currentPausePositionInSong = 0;     //记录暂停音乐时进度条的位置@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate( savedInstanceState );setContentView( R.layout.activity_main );initView();                                 //控件定义与绑定 减少代码量mediaPlayer = new MediaPlayer();mDatas = new ArrayList<>();                 //任意类型数组//创建适配器对象adapter = new LocalMusicAdapter(this, mDatas); //传参context,mDatasmusicRv.setAdapter(adapter);//设置布局管理器LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);musicRv.setLayoutManager(layoutManager);//动态权限申请if (ContextCompat.checkSelfPermission( MainActivity.this,Manifest.permission.WRITE_APN_SETTINGS ) != PackageManager.PERMISSION_GRANTED)    //判断是否已经授权{//申请权限 三个参数:1.Activity实例;2.String数组,存放权限;3.唯一请求码ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}, 1 );}else{loadLocalMusicData();          //权限请求失败,则加载本地数据源,请求成功则调用回调函数中的加载,不管成功与否都会调用}//        loadLocalMusicData();     //加载本地数据源setEventListener();         //设置每一项的点击事件}//动态权限获取函数的回调函数@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult( requestCode, permissions, grantResults );switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {loadLocalMusicData();    //申请权限成功,扫描模拟器存储,加载资源} else {finish();Toast.makeText( this, "denied!", Toast.LENGTH_SHORT ).show();}}}private void loadLocalMusicData() {/* 更新媒体库mp3文件*/String file_path = "storage/emulated/0/Music";MediaScannerConnection.scanFile( this,new String[]{file_path.toString()},
//                null,new String[]{"mp3"},new MediaScannerConnection.OnScanCompletedListener() {@Overridepublic void onScanCompleted(String path, Uri uri) {Log.i( "ExternalStorage", "Scanned" + path + ":" );Log.i( "ExternalStorage", "->" + uri);}} );/* 加载本地存储当中的音乐mp3文件到集合当中*/
//        1.获取ContentResolver对象ContentResolver resolver = getContentResolver();
//        2.获取本地音乐存储的Uri地址Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
//        3 开始查询地址 查询接口Cursor cursor = resolver.query(uri, null, null, null, null);
//        4.遍历Cursorint id = 0;while (cursor.moveToNext()) {String song = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));    //歌曲标题String singer = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); //歌手名String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));   //专辑名id++;String sid = String.valueOf(id);                                                        //序列号String path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));     //路径long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)); //视频时长SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");String time = sdf.format(new Date(duration));
//          获取专辑图片主要是通过album_id进行查询String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));//相册名String albumArt = getAlbumArt(album_id);
//            将一行当中的数据封装到对象当中LocalMusicBean bean = new LocalMusicBean(sid, song, singer, album, time, path, albumArt); //每一首歌的信息封装mDatas.add(bean);}
//        数据源变化,提示适配器更新adapter.notifyDataSetChanged();}private void initView() {/* 初始化控件的函数*/nextIv = findViewById(R.id.local_music_bottom_iv_next);playIv = findViewById(R.id.local_music_bottom_iv_play);lastIv = findViewById(R.id.local_music_bottom_iv_last);albumIv = findViewById(R.id.local_music_bottom_iv_icon);singerTv = findViewById(R.id.local_music_bottom_tv_singer);songTv = findViewById(R.id.local_music_bottom_tv_song);musicRv = findViewById(R.id.local_music_rv);nextIv.setOnClickListener(this);lastIv.setOnClickListener(this);playIv.setOnClickListener(this);seekBar = findViewById( R.id.local_music_bottom_tv_time ); //进度条定义tv_star = findViewById( R.id.local_music_bottom_tv_time_star );tv_end = findViewById( R.id.local_music_bottom_tv_time_end );}private void setEventListener() {/* 设置每一项的点击事件*/adapter.setOnItemClickListener(new LocalMusicAdapter.OnItemClickListener() {@Overridepublic void OnItemClick(View view, int position) {currnetPlayPosition = position;LocalMusicBean musicBean = mDatas.get(position);playMusicInMusicBean(musicBean);}});}public void playMusicInMusicBean(LocalMusicBean musicBean) { //传入一首歌 信息/*根据传入对象播放音乐*///设置底部显示的歌手名称和歌曲名singerTv.setText(musicBean.getSinger());    //获取当前信息并设置songTv.setText(musicBean.getSong());stopMusic();
//                重置多媒体播放器mediaPlayer.reset();
//                设置新的播放路径try {mediaPlayer.setDataSource(musicBean.getPath());String albumArt = musicBean.getAlbumArt();Log.i("lsh123", "playMusicInMusicBean: albumpath=="+albumArt);Bitmap bm = BitmapFactory.decodeFile(albumArt);Log.i("lsh123", "playMusicInMusicBean: bm=="+bm);albumIv.setImageBitmap(bm);playMusic();SeekBarSet();   //设置进度条及其时间显示loadCover(musicBean.getPath());} catch (Exception e) {e.printStackTrace();}}private void SeekBarSet(){/*新增seekbar进度条*/int duration = mediaPlayer.getDuration();   //获取视频总时间seekBar.setMax( duration );     //将音乐总时间设置为Seekbar的最大值Timer timer = new Timer(  );timer.schedule( new TimerTask() {@Overridepublic void run() {
//                    if (!currentPausePositionInSong){seekBar.setProgress( mediaPlayer.getCurrentPosition() );
//                    }}},0,50);seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int i, boolean b) {int time_end = mediaPlayer.getDuration() / 1000 ;   //获取视频时长tv_end.setText(calaculateTime( time_end ));int time_now = mediaPlayer.getCurrentPosition() / 1000;tv_star.setText(calaculateTime( time_now ) );}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {mediaPlayer.seekTo( seekBar.getProgress() );
//                    tv_star.setText(formattime( mediaPlayer.getCurrentPosition() ));}} );}/** 点击播放按钮播放音乐,或者暂停从新播放* 播放音乐有两种情况:* 1.从暂停到播放* 2.从停止到播放* */private void playMusic() {/* 播放音乐的函数*/if (mediaPlayer!=null&&!mediaPlayer.isPlaying()) {if (currentPausePositionInSong == 0) { //如果已经停止播放try {mediaPlayer.prepare();          //重新准备播放mediaPlayer.start();} catch (Exception e) {e.printStackTrace();}}else{
//                从暂停到播放mediaPlayer.seekTo(currentPausePositionInSong);mediaPlayer.start();}
//            playIv.setImageResource(R.mipmap.icon_pause);}}private void pauseMusic() {/* 暂停音乐的函数*/if (mediaPlayer!=null&&mediaPlayer.isPlaying()) {currentPausePositionInSong = mediaPlayer.getCurrentPosition(); //获取进度条mediaPlayer.pause();playIv.setImageResource(R.mipmap.icon_play);}}private void stopMusic() {/* 停止音乐的函数*/if (mediaPlayer!=null) {currentPausePositionInSong = 0;mediaPlayer.pause();            //播放器暂停mediaPlayer.seekTo(0);    //调至0mediaPlayer.stop();             //停止playIv.setImageResource(R.mipmap.icon_play); //显示图标}}@Overrideprotected void onDestroy() {super.onDestroy();stopMusic();}/*进度条时间换算与格式转变*///时间格式换算1public  String formattime(long time){String min=  (time/(1000*60))+"";String second= (time%(1000*60)/1000)+"";if(min.length()<2){min = 0 + min;}if(second.length()<2){second = 0 + second;}return min +":"+ second;}//时间格式换算2public String calaculateTime(int time){int minute, second;if(time > 60){minute = time / 60; //分钟取整second = time % 60; //秒钟取余//分钟再0~9if(minute >= 0 && minute < 10){ //0x:0x//判断秒if(second >= 0 && second < 10){return "0"+minute+":"+"0"+second;}else {return "0"+minute+":"+second;}}else {//分钟大于10再判断秒if(second >= 0 && second < 10){return minute+":"+"0"+second;}else {return minute+":"+second;}}}else if(time < 60){second = time;if(second >= 0 && second < 10){return "00:"+"0"+second;}else {return "00:"+ second;}}return null;}private String getAlbumArt(String album_id) {String mUriAlbums = "content://media/external/audio/albums";String[] projection = new String[]{"album_art"};Cursor cur = this.getContentResolver().query(Uri.parse(mUriAlbums + "/" + album_id),projection, null, null, null);String album_art = null;if (cur.getCount() > 0 && cur.getColumnCount() > 0) {cur.moveToNext();album_art = cur.getString(0);}cur.close();cur = null;return album_art;}private void loadCover(String path){MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();mediaMetadataRetriever.setDataSource( path );byte[] cover = mediaMetadataRetriever.getEmbeddedPicture();Bitmap bitmap = BitmapFactory.decodeByteArray( cover, 0, cover.length );albumIv.setImageBitmap( bitmap );}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.local_music_bottom_iv_last: //上一首if (currnetPlayPosition ==0) {Toast.makeText(this,"已经是第一首了,没有上一曲!",Toast.LENGTH_SHORT).show();return;}currnetPlayPosition = currnetPlayPosition-1;                    //序号索引-1LocalMusicBean lastBean = mDatas.get(currnetPlayPosition);      //从数组中获取上一首信息playMusicInMusicBean(lastBean);break;case R.id.local_music_bottom_iv_next:  //下一首if (currnetPlayPosition ==mDatas.size()-1) {Toast.makeText(this,"已经是最后一首了,没有下一曲!",Toast.LENGTH_SHORT).show();return;}currnetPlayPosition = currnetPlayPosition+1;LocalMusicBean nextBean = mDatas.get(currnetPlayPosition);playMusicInMusicBean(nextBean);break;case R.id.local_music_bottom_iv_play:if (currnetPlayPosition == -1) {
//                    并没有选中要播放的音乐Toast.makeText(this,"请选择想要播放的音乐",Toast.LENGTH_SHORT).show();return;}if (mediaPlayer.isPlaying()) {
//                    此时处于播放状态,需要暂停音乐pauseMusic();}else {
//                    此时没有播放音乐,点击开始播放音乐playMusic();}break;}}
}

[LocalMusicBean.java] 歌曲信息封装类,包含歌曲各信息

package com.example.musicplayer_2;/*** author : harrison* e-mail : harrison.zfx@gmail.com* data   : zfx上午10:29* version: 1.0*/
public class LocalMusicBean {private String id; //歌曲idprivate String song; //歌曲名称private String singer; //歌手名称private String album; //专辑名称private String duration; //歌曲时长private String path; //歌曲路径private String albumArt;  //专辑地址public LocalMusicBean() {}public LocalMusicBean(String id, String song, String singer, String album, String duration, String path, String albumArt) {this.id = id;this.song = song;this.singer = singer;this.album = album;this.duration = duration;this.path = path;this.albumArt = albumArt;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getSong() {return song;}public void setSong(String song) {this.song = song;}public String getSinger() {return singer;}public void setSinger(String singer) {this.singer = singer;}public String getAlbum() {return album;}public void setAlbum(String album) {this.album = album;}public String getDuration() {return duration;}public void setDuration(String duration) {this.duration = duration;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getAlbumArt() {return albumArt;}public void setAlbumArt(String albumArt) {this.albumArt = albumArt;}
}

[LocalMusicAdapter.java] 歌曲横向展示类

package com.example.musicplayer_2;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;import java.util.List;/*** author : harrison* e-mail : harrison.zfx@gmail.com* data   : zfx上午10:49* version: 1.0*/
public class LocalMusicAdapter extends RecyclerView.Adapter<LocalMusicAdapter.LocalMusicViewHolder>{private Context context;private List<LocalMusicBean>mDatas;private OnItemClickListener onItemClickListener;public void setOnItemClickListener(OnItemClickListener onItemClickListener) {  //监听器参数传入this.onItemClickListener = onItemClickListener;}public interface OnItemClickListener{public void OnItemClick(View view,int position);}public LocalMusicAdapter(Context context, List<LocalMusicBean> mDatas) {  //构造函数传入参数 context上下文,数据集this.context = context;this.mDatas = mDatas;}@NonNull@Overridepublic LocalMusicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_local_music,parent,false);LocalMusicViewHolder holder = new LocalMusicViewHolder(view);return holder;}@Overridepublic void onBindViewHolder(@NonNull LocalMusicViewHolder holder, final int position) {LocalMusicBean musicBean = mDatas.get(position);holder.idTv.setText(musicBean.getId());holder.songTv.setText(musicBean.getSong());holder.singerTv.setText(musicBean.getSinger());holder.albumTv.setText(musicBean.getAlbum());holder.timeTv.setText(musicBean.getDuration());holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {onItemClickListener.OnItemClick(v,position);}});}@Overridepublic int getItemCount() {return mDatas.size();}class LocalMusicViewHolder extends RecyclerView.ViewHolder{TextView idTv,songTv,singerTv,albumTv,timeTv;public LocalMusicViewHolder(View itemView) {super(itemView);idTv = itemView.findViewById(R.id.item_local_music_num);        //序号songTv = itemView.findViewById(R.id.item_local_music_song);     //歌名singerTv = itemView.findViewById(R.id.item_local_music_singer); //歌手名albumTv = itemView.findViewById(R.id.item_local_music_album);   //专辑名timeTv = itemView.findViewById(R.id.item_local_music_durtion);  //时间}}
}

前端页面代码较长就不贴了,上表中已列清基本控件使用。
挣点积分,后续会上传GitHub,完整工程地址:https://download.csdn.net/download/Harrison509/12894922


http://chatgpt.dhexx.cn/article/WIxcBWmT.shtml

相关文章

大学生安卓期末设计之本地音乐播放器

作为一个热爱设计交互性产品的男大学生&#xff0c;我真的不太爱听课&#xff0c;所以很多时候&#xff0c;需要一个人学会阅读搜索资料并不停尝试&#xff0c;我希望大家也多多热爱这样持之以恒的尝试。 先看产品的一个模拟机测试效果&#xff08;b站南阳洛信也有视频) 安卓A…

Android本地音乐播放器

UI界面模仿QQ音乐 实现一个简单的本地播放器&#xff0c;功能包括&#xff1a;播放&#xff0c;暂停&#xff0c;上一曲&#xff0c;下一曲&#xff0c;进度条。 功能实现 读取本地音乐 1.创建一个Song类 public class Song {public String song;//歌曲名public String sing…

毕业设计- 基于Android的本地音乐播放器

—— 木叶飞舞之处&#xff0c;火亦生生不息。 项目介绍 本系统支持扫瞄本地音乐播放、暂停、上一首、下一首&#xff0c;点击播放栏跳转到播放页面&#xff0c;可查看当前播放列表&#xff0c;设置播放模式如顺序播放、随机播放&#xff0c;标记为我的喜欢&#xff0c;可以创…

Selenium自动化测试设计模式-PO模式

前言&#xff1a; 在python自动化过程中&#xff0c;Selenium自动化测试中有一个名字常常被提及PageObject&#xff08;思想与面向对象的特性相同&#xff09;&#xff0c;通过PO模式可以大大提高测试用例的维护效率。 不了解po设计模式的可自行百度 面向对象的特性&#xf…

电商项目测试实战(十一)后台业务场景测试设计

后台订单处理业务场景测试设计 流程步骤&#xff1a; 设计测试用例&#xff1a; 第一步&#xff1a;绘制流程图 1、确认业务中的操作 2、分析执行的顺序 3、按照业务方向进行连线 收到前台订单&#xff08;商城->订单->订单列表&#xff09;订单确认发货&#xff0…

电商项目测试实战(十)前台下单业务场景测试设计

前台下单业务场景测试设计 流程步骤&#xff1a; 设计测试用例&#xff1a; 第一步&#xff1a;需求分析 第二步&#xff1a;绘制流程图 1、确定业务中的操作 2、分析执行的顺序 3、按照业务方向进行连线 登录->选购商品->加入购物车->支付->确认订单->等待…

空调测试用例设计

** 空调测试用例设计 ** 界面测试 功能测试 可靠性测试 易用性测试 可维护性测试 兼容性测试 竞品测试 负载压力测试 稳定性测试 文档测试 界面测试&#xff1a; 空调外观的美观性空调外观尺寸是否和设计尺寸一致遥控按钮是否清晰和易懂显示温度及模式的显示屏是否显示设备…

测试场景设计-登录设计

来一波广告&#xff1a;欢迎关注测者说&#xff0c;测试理论知识尽在此处 面试中经常被问到有一个登录页面&#xff0c;你怎么设计测试场景&#xff0c;原来可以做到这么细致。 具体需求&#xff1a; 有一个登陆页面&#xff0c;&#xff08;假如上面有2个textbox,一个提交按钮…

基于微信小程序的大学生心理健康测试设计与实现 .docx

目录 1 绪论 3 1.1 项目开发背景 3 1.2 项目开发意义 3 1.3 项目主要的内容 4 2 相关技术介绍及系统环境开发条件 5 2.1相关技术介绍 5 2.2系统环境开发条件 6 3 系统的需求分析与设计 6 3.1可行性分析 7 3.2需求分析 7 3.2.1系统总体概述 8 3.2.2功能性需求 8 3.2.3非功能性需…

测试设计技术

本文是根据测试架构师修炼之道&#xff08;第二部分 突破&#xff1a;向软件测试架构师的目标迈进&#xff09;整理的&#xff0c;主要分为5个小部分&#xff1a;测试设计四步走、测试设计软技能、设计技术之控制用例粒度、设计技术之自动化测试、设计技术之探索式测试。学习的…

数字系统的测试与可测试设计(DFT)

数字系统的测试与可测试设计&#xff08;DFT&#xff09; 背景介绍1 Defects1.1 名词解释1.2 缺陷种类1.2.1 Physical Defects物理缺陷1.2.2 Shorting Defects1.2.2.1 Gate-Oxide-Shorts1.2.2.2 Bridge1.2.2.1 Open1.2.2.1 Post-fabrication failures 2 Fault Modelling&#x…

2. 测试分析与测试设计

1. 为什么要做好测试分析和测试设计 以业务驱动测试&#xff1a;当下的测试圈子内&#xff0c;大家一直在强调自动化技术、DevOps等&#xff0c;这些是提高效率和质量的利器&#xff0c;但是所有有效的测试行为&#xff0c;都是建立在对业务需求有正确的理解和分析的基础上的。…

软件测试之---测试设计方法

二、测试设计方法&#xff08;黑盒测试设计方法&#xff1b;白盒测试设计方法&#xff09; 1、等价类划分法 1.1等价类划分法概念 将输入&#xff08;输出&#xff09;域划分成若干个子集合&#xff0c;从划分的子集合中选取代表数据&#xff0c;如果选取的数据测试没有问题&…

测试用例:四步测试设计法

读者提问&#xff1a; 阿常&#xff0c;上节我们讲到测试点不等同于测试用例&#xff0c;想请教你一下&#xff0c;如何把测试点加工成测试用例呢&#xff1f; 目前团队中测试人员针对同样的测试点&#xff0c;编写出来的测试用例各不相同&#xff0c;一千个人就有一千个哈姆雷…

常用测试设计方法

目录 1、按照开发阶段划分&#xff08;1&#xff09;单元测试(模块测试)&#xff08;2&#xff09;集成测试(组装测试)&#xff08;3&#xff09;确认测试(有效性测试)&#xff08;4&#xff09;系统测试&#xff08;5&#xff09;验收测试 2、按照代码运行划分&#xff08;1&a…

大数据基础概述

大数据基础概述 1.前言2.大数据技术发展的三个阶段3.大数据技术面临的问题4.大数据技术数据的特点5.大数据对各方面的影响6.大数据技术的应用领域7.大数据技术的相关概念8.大数据技术的相关产业9.云计算和物联网 1.前言 三次信息浪潮 信息化浪潮发生时间标志解决问题代表企业第…

大数据概论

大数据big data指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新 处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。大量数据资源为解决以前不可能解决的问题带来了可能性。1大数据不一定…

大数据技术概论

大数据技术概论 大数据技术的产生 大数据的基本概念 ●大数据简介: 大数据是规模非常巨大和复杂的数据集,传统数据库管理工具处理起来面临很多问题,比如说获取、存储、检索、共享、分析和可视化,数据量达到PB、EB或ZB的级别。 大数据有三个V: 一是数据量(Volume),数据量是持续…

2【源码】数据可视化:基于 Echarts + Java SpringBoot 实现的动态实时大屏范例-物流大数据

数据可视化大屏的出现&#xff0c;掀起一番又一番的浪潮&#xff0c;众多企业纷纷想要打造属于自己的“酷炫吊炸天”的霸道总裁大屏驾驶舱。 之前小伙伴们建议我出一些视频课程来学习Echarts&#xff0c;这样可以更快上手&#xff0c;所以我就追星赶月的录制了《Echarts - 0基…

客快物流大数据项目(九十一):ClickHouse的数据库引擎

​​​​​​​ 文章目录 ClickHouse的数据库引擎 一、MySQL引擎 ClickHouse的数据库引擎 ClickHouse提供了本机、M