AndroidStudio 简单却功能完备的音乐播放器

音乐播放器

  • 前言
  • 总体设计
  • 详细设计
    • 扫描功能
    • 歌曲列表功能
    • 播放控制功能
    • 播放历史功能
    • 歌单操作功能
    • 搜索功能
    • 精化设计
    • 侧边栏功能
  • 界面展示
  • 参考资料
  • 项目链接

前言

课设设计作品,参考了网上很多资料和实例,整了一个功能齐全的播放器
发上来供大家借鉴学习,如果大家能继续改进就再好不过了。
实验目的:

  1. 学会如何使用 MediaPlayer 等媒体播放组件。
  2. 学会如何进行数据保存。

总体设计

设计介绍:
综合运用UI界面设计、数据存储、Activity(活动)、Service(服务)、MediaPlayer、ListView、RecyclerView等知识,设计开发一款具有音乐列表的音乐播放器。
设计步骤:
AndroidStudio 简单却功能完备的音乐播放器_第1张图片

音乐播放器功能模块:

  1. 播放列表:能够读取手机中的音频文件,建立播放列表。
  2. 播放控制:能够对播放进行控制,能够开始/继续播放、暂停播放、上一
    首、下一首、单曲循环、列表播放,并能根据进度条自由选择播放进度。
  3. 播放历史:能够存储已播放过的历史,并能够清空历史。
  4. 歌单操作:能够在歌单页面建立额外的自定义歌单,并能够选择歌曲添加其中,并能够移除歌曲,删除歌单。
  5. 侧边栏:作为拓展辅助功能,方便操作,方便以后扩充。
  6. 搜索栏:根据所在页面,能够在搜索栏搜索需要的对象。

详细设计

程序流程:
AndroidStudio 简单却功能完备的音乐播放器_第2张图片

扫描功能

歌曲JavaBean数据库文件Song.java,主要存储数据如下

public class Song implements Serializable {//javabean数据库
    public String id;      //歌曲id
    public String song;    //歌曲名称
    public String singer;  //歌手名称
    public String album;   //专辑名称
    public long duration;  //歌曲时长
    public String path;    //歌曲路径
    public long size;//大小

扫描本地歌曲对应文件:MusicUtils.java

public class MusicUtils {
    // 扫描系统里面的音频文件,返回一个list集合
    public static List<Song> getMusicData(Context context) {
        List<Song> list = new ArrayList<Song>();
        // 查询曲库
        //Cursor 提供遍历查询结果的方法//getcontentresolver访问数据//
        Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null,null, MediaStore.Audio.AudioColumns.IS_MUSIC);
        int id=-1;
            if (cursor != null) {//查询结果非空 {
                while (cursor.moveToNext()) {//循环查询数据库
                    Song song = new Song();//获取相应的属性值
                    song.duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
                    song.size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));
                    if (song.duration>30*1000&&song.size > 1000 * 800) {
                    song.song = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
 //获取列,getColumnIndexOrThrow如果没找到该列名,会抛出IllegalArgumentExceptio异常
                    song.singer = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
                    song.path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
                     song.album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
//获取相应的的属性值
                        if (song.song.contains(".mp3")) {  //分离文件格式后缀
                            String[] str = song.song.split(".mp3");
                            song.song = str[0];
                        }
                        if (song.song.contains(".ogg")) {//分离文件格式后缀
                            String[] str = song.song.split(".ogg");
                            song.song = str[0];
                        }
                        if (song.song.contains(".flav")) {//分离文件格式后缀
                            String[] str = song.song.split(".flav");
                            song.song = str[0];
                        }  //分离后缀
                        if (song.song.contains("-")) {//用-分离歌名和歌手
                            String[] str = song.song.split("-");
                            song.singer = str[0];
                            song.song = str[1];
                        }
                        ++id;
                        String sid = String.valueOf(id+1);
                        song.id=sid;
                        list.add(song);//添加到表中
                    }
                }
                cursor.close();// 释放资源
            }
            return list;
        }

歌曲列表功能

音频数据适配器MyAdapter.java,将扫描到的音频文件进行数据适配,关键代码:

public void onBindViewHolder(@NonNull LocalMusicViewHolder holder, final int position) {
    Song musicBean = mDatas.get(position);
    if (musicBean != null) {//非空判断,防止报错
        holder.idTv.setText(musicBean.getId());
        holder.songTv.setText(musicBean.getSong());
        holder.singerTv.setText(musicBean.getSinger());
        holder.albumTv.setText(musicBean.getAlbum());
        //转换时间格式
        SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
        String time = sdf.format(new Date(musicBean.getDuration()));
        holder.timeTv.setText(time);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onItemClickListener.OnItemClick(v, position);
            }
        });
    }
}
class LocalMusicViewHolder extends RecyclerView.ViewHolder{
    TextView idTv,songTv,singerTv,albumTv,timeTv;
    public LocalMusicViewHolder(View itemView) {
        super(itemView);
        idTv = itemView.findViewById(R.id.music_num);//id
        songTv = itemView.findViewById(R.id.music_song);//歌名
        singerTv = itemView.findViewById(R.id.music_singer);//歌手
        albumTv = itemView.findViewById(R.id.music_album);//专辑
        timeTv = itemView.findViewById(R.id.music_duration);//时长
    }
}

其中的id对应布局文件item_song.xml中的控件,布局效果如下
在这里插入图片描述
在MusicListActivity(歌曲列表Activity)初始化控件后再初始化数据。
初始化数据函数initData():

public static List<Song> list;
list = new ArrayList<>();//歌单数组
list.addAll(sheet.getList());
adapter = new MyAdapter(this,list);// 创建适配器对象
musicRv.setAdapter(adapter);
//musicRV为对应布局文件activity_music_list.xml中的RecyclerView控件的id
//设置布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
musicRv.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();

播放控制功能

服务类:MusicService.java 包含与播放有关的函数,关键代码:

public class MyBinder extends Binder {
        public boolean setDatas(List<Song> musicBeans){//获取歌曲列表list
            Log.d("MusicService----0----", "setDatas");
            mDatas = musicBeans;
            musicDataSize = mDatas.size();
                      return true;
        }
                  public void setMusic(int Id){
            Log.d("MusicService----1----", "setMusic");
            //重置播放
            mediaPlayer.reset();
            //进度记录清零
            pausePosition=0;
            //设置新的音乐
            music_Id=Id;
            Song musicBean = mDatas.get(music_Id);
            try {
                //设置信息
                path = musicBean.getPath();
                mediaPlayer.setDataSource(path);
                music_song = musicBean.getSong();
                music_singer = musicBean.getSinger();
                music_duration = (int) musicBean.getDuration();
                // music_num=musicBean.getNum();
                //调用意图服务,更新activity内容
                Intent intentInfo = new Intent("com.example.musicapplication.intentService");
                intentInfo.setPackage(getPackageName());
                intentInfo.putExtra("music_id", music_Id);
                intentInfo.putExtra("music_song", music_song);
                intentInfo.putExtra("music_singer", music_singer);
                intentInfo.putExtra("music_duration", music_duration);
                // intentInfo.putExtra("music_duration", music_num);
                startService(intentInfo);
                myBinder.playMusic();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        public void playMusic() {
            /* 播放音乐的函数*/
            Log.d("MusicService----2----", "playMusic");
            if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
                if (pausePosition == 0) {
                    try {
                        mediaPlayer.prepare();
                        mediaPlayer.start();
                        playState=false;
                    } catch (IOException e) {
                        e.printStackTrace()}
                } else {
                    //从暂停到播放
                    mediaPlayer.seekTo(pausePosition);
                    mediaPlayer.start();
                    playState=false;
                }
            }
        }
        public void pauseMusic() {
            /* 暂停音乐的函数*/
            Log.d("MusicService----3----", "pauseMusic");
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                pausePosition = mediaPlayer.getCurrentPosition();
                mediaPlayer.pause();
            }
        }
        public void play_next() {
            //判断是否为单曲循环
            if(!playModule) {
                //判断是否是最后一首
                if (music_Id >= musicDataSize - 1) {
                    //从第一首开始播放
                    music_Id = -1;
                }
                music_Id = music_Id + 1;
            }
            setMusic(music_Id);
            playMusic();
            playState=false;
        }
        public void play_last() {
            music_Id = music_Id-1;
            if(music_Id==-1) return;
            setMusic(music_Id);
            playMusic();
        }
        public void stopMusic() {
            /* 停止音乐的函数*/
            Log.d("MusicService----4----", "stopMusic");
            if (mediaPlayer != null) {
                pausePosition = 0;
                mediaPlayer.pause();
                mediaPlayer.seekTo(0);
                mediaPlayer.stop();
            }
        }
        public int getPlayPosition() {
            /*返回播放进度*/
            Log.d("MusicService----5----", "getPlayPosition");
            if(mediaPlayer!=null) {
                return mediaPlayer.getCurrentPosition();
            }
            return 0;
        }
        public void seekToPosition(int msec) {
            /*设置播放进度*/
            Log.d("MusicService----6----", "seekToPosition");
            if(mediaPlayer!=null) {
                if(!mediaPlayer.isPlaying()) myBinder.playMusic();
                playState=false;
                mediaPlayer.seekTo(msec);
            }
        }
        public int getMediaPlayState() {
            /*返回播放状态*/
            Log.d("MusicService----7----", "getMediaPlayState");
            if(mediaPlayer!=null) {
                if(mediaPlayer.isPlaying()) return 1;
                else if(playState) return 2;
                else return 0;
            }
            else return 0;
        }
        public String getMusicSinger(){
            Log.d("MusicService----8----", "getMusicSinger");
            if(mediaPlayer!=null) return music_singer;
            else return "";
        }
        public String getMusicSong() {
            Log.d("MusicService----9----", "getMusicSong");
            if(mediaPlayer!=null) return music_song;
            else return "";
        }
        public int getMusicId(){
            Log.d("MusicService----10----", "getMusicId");
            if(mediaPlayer!=null) return music_Id;
            else return -1;
        }
        public int getMusicDuration(){
            Log.d("MusicService----11----", "getMusicDuration");
            if(mediaPlayer!=null) return mediaPlayer.getDuration();
            else return -1;
        }
        public void setPlayModule(){
            /*设置播放模式*/
            if(playModule) playModule=false;
            else playModule=true;
        }
        public boolean getPlayModule(){
            return playModule;
        }
    }

MusicListActivity.java中与播放控制有关的关键代码

 private void serviceConn() {
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mMyBinder = (MusicService.MyBinder) iBinder;
                //如果启动时正在播放
                if(mMyBinder.getMediaPlayState()==1) {
                    playIv.setImageResource(R.mipmap.icon_pause);
                    songTv.setText(mMyBinder.getMusicSong());
                    singerTv.setText(mMyBinder.getMusicSinger());
                    currentId=mMyBinder.getMusicId();
                    //设置进度条大小
                    seekBar.setMax(mMyBinder.getMusicDuration());
                    Log.d("currentId", Integer.toString(currentId));
                }
                //传递播放列表
                mMyBinder.setDatas(list);
                //初始化进度条
                seekBar.setProgress(0);
                seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {                      //响应用户点击设置进度条
                        if(b && currentId!=-1 &&currentId!=-2) {
                            mMyBinder.seekToPosition(seekBar.getProgress());
                            playIv.setImageResource(R.drawable.ripple_pause);
                        } else if(b){
                            Toast.makeText(MusicListActivity.this, "请选择播放音乐", Toast.LENGTH_SHORT).show();
                            seekBar.setProgress(0);
                        }
                    }
                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                    }
                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                        //mMyBinder.seekToPosition(seekBar.getProgress());
                    }
                });
                //设置进度条控制线程
                handler = new Handler();
                runnable = new Runnable() {
                    private int pre=-1, pos;
                    @Override
                    public void run() {
                        pos = mMyBinder.getPlayPosition();
                        if(currentId!=-1 && currentId!=-2)
                            seekBar.setProgress(pos);
                        Log.d("RunnablePos", String.valueOf(pos));
                        if(pre!=pos) handler.postDelayed(runnable, 1000);
                        else handler.postDelayed(runnable, 2000);
                        //修复最后一首时播放图标bug
                        pre = pos;
                    }
                };
                handler.post(runnable);
            }
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
            }
        };
    }
    private void setEventListener() {
        /* 设置点击事件*/
        //设置单曲循环/列表循环
        albumIv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mMyBinder.setPlayModule();
                if(mMyBinder.getPlayModule()){
                    Toast.makeText(MusicListActivity.this, "单曲循环", Toast.LENGTH_SHORT).show();
                    albumIv.setImageResource(R.mipmap.icon_song2_loop_blue);
                } else {
                    Toast.makeText(MusicListActivity.this, "列表循环", Toast.LENGTH_SHORT).show();
                    albumIv.setImageResource(R.mipmap.icon_song2);
                }
            }
        });
    }

MusicIntentService.java处理异步请求,点击歌曲后可使相应相应歌曲信息显示在下方播放栏中

private Intent intent;
    private MusicIntentService(Context context){
        super(null);
        intent=new Intent(context ,MusicService.class);
    }
    public MusicIntentService() {
        super(null);
    }

    Intent getIntent(){
        return intent;
    }
    @Override
    public void onCreate() {
        super.onCreate();
    }
    static MusicIntentService get(Context context){
        if(musicIntent==null){
            musicIntent=new MusicIntentService(context);
        }
        return musicIntent;
    }
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            intent.setAction("UI_info");
            sendBroadcast(intent);
        }
    }
}

MusicListActivity中对应的函数

private void setReceiver() {
    myReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            songTv.setText(intent.getStringExtra("music_song"));
            singerTv.setText(intent.getStringExtra("music_singer"));
            currentId = intent.getIntExtra("music_id", -1);
            seekBar.setMax(intent.getIntExtra("music_duration", 0));
        }
    };
    IntentFilter intentFilter = new IntentFilter("UI_info");
    registerReceiver(myReceiver, intentFilter);
}

播放历史功能

MusicService.java中对应的更新历史记录函数

public class MyBinder extends Binder {
       public void updateHistory(int position){
        if(position!=-1) {//歌曲非空
            song= mDatas.get(position);//获取歌曲的id
        }
        if(song!=null){//歌曲非空
            if(listHistory.contains(song)) {
                listHistory.remove(song);//存在歌曲则移除,防止重复
            }
        }
        listHistory.addFirst(song);//添加元素至首部,使新点歌曲永远位于顶部    }
}

MusicListActivity.java中相应点击事件,点击歌曲触发事件

adapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
    @Override   //点击歌曲后即可存入播放历史
    public void OnItemClick(View view, int position) {
        Song bean = list.get(position);//position为歌曲id,这是为了获取歌曲的位置
        mMyBinder.updateHistory(position);//更新历史记录
                song=bean;
        playMusicOnService(position);
    }
});

MusicListActivity.java中清空历史记录的函数

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_delete:
if(positionList==1){//如果当前为历史记录歌单
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("是否清空历史记录?")
            .setIcon(android.R.drawable.ic_dialog_info)
            .setNegativeButton("取消", null);
    builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
           list.clear();//实时更新数据源
            listSheet=MusicListSheet.get(getApplicationContext());
            listHistory=listSheet.getSheet().get(1);
            listHistory.getList().clear();//
            adapter.notifyDataSetChanged();//实时更新数据源
            list.addAll(sheet.getList());//实时更新数据源
            Toast.makeText(MusicListActivity.this, "清除成功", Toast.LENGTH_SHORT).show();
        }
    });
    builder.show();
    adapter.notifyDataSetChanged();//更新显示
}

歌单操作功能

歌单JavaBean数据库文件Sheet.java,主要存储数据如下

public class Sheet implements Serializable {
    private String name;//歌单名
    private List<Song> list;//歌曲列表
public String getNumString(){
    if(list!=null) {
        return " " + list.size() + " 首歌曲";
    }//显示歌曲数目
    return " " + 0 + " 首歌曲";
}

歌单适配器SheetAdapter.java关键代码

    protected void convert(BaseViewHolder viewHolder, Sheet item) {
        viewHolder.setText(R.id.name_text_view, item.getName());
        viewHolder.setText(R.id.number_text_view,item.getNumString());
    }

MusicListSheet.java对歌单进行初始化

private MusicListSheet(Context context){
    sheet=new ArrayList<>();
    Sheet listAllMusic=new Sheet("全部音乐");//默认歌单
    listAllMusic.setList(MusicUtils.getMusicData(context));
    Sheet listHistory=new Sheet("播放历史",1);//默认歌单
    //Sheet listRecommendation=new Sheet("推荐歌单",2);
    sheet.add(0,listAllMusic);
    sheet.add(1,listHistory);   //  sheet.add(2,listRecommendation);
}

歌单对应布局文件item_sheet.xml,效果如下
在这里插入图片描述
MainActivity.java中相应点击事件代码
点击歌单跳转事件

public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
    Intent intent = new Intent(MainActivity.this, MusicListActivity.class);
//点击跳转至歌单
    intent.putExtra("CLICK_POSITION",position);//点击的歌单位置,相应跳转
    startActivity(intent);
}

新建歌单事件
MusicListActivity.java相应点击函数


public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.action_add) {//nameNewList 弹窗输入
        final EditText inputServer = new EditText(this);
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("输入歌单名称")
                .setView(inputServer)
                .setNegativeButton("取消", null);
        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                String nameNewList = inputServer.getText().toString();
                listSheet.add(new Sheet(nameNewList));
                //实时更新数据源
                setSheet.clear();
                setSheet.addAll(listSheet.getSheet());
//先清空歌单,再加入新歌单列表,实时更新列表
                sheetAdapter.notifyDataSetChanged();
            }
        });
        builder.show();
        sheetAdapter.notifyDataSetChanged();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

MusicListActivity.java中相应点击事件代码
删除歌单事件

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_delete:
            if(positionList!=0&&positionList!=1) {//非默认歌单时才可删除
            //历史歌单点击按钮对应为清空播放历史,全部音乐歌单对应按钮则提示不可删除
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("是否删除此列表?")
                        .setIcon(android.R.drawable.ic_dialog_info)
                        .setNegativeButton("取消", null);
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int which) {
           //setSheet.clear(); //setSheet.addAll(listSheet.getSheet());
             //           sheetAdapter.notifyDataSetChanged();
                        listSheet.getSheet().remove(positionList);//移除歌单
                        setSheet.clear();//清空列表
                        setSheet.addAll(listSheet.getSheet()); sheetAdapter.notifyDataSetChanged();//添加更新后列表,更新数据
                        Toast.makeText(MusicListActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                        finish();   sheetAdapter.notifyDataSetChanged();
                    }
                });
                builder.show();
                adapter.notifyDataSetChanged();
            }else if(positionList==0){//positionList==1时再上面
    Toast.makeText(this, "此列表不可删除", Toast.LENGTH_SHORT).show();	}
}

添加歌曲事件
点击要添加的歌曲
MusicListActivity.java对应点击事件函数:


addImageView.setOnClickListener(new View.OnClickListener() {
    @Override//点击进入添加歌曲界面
    public void onClick(View view) {
        Intent addIntent = new Intent(MusicListActivity.this, SheetAdd.class);
        startActivityForResult(addIntent,0);
    }
});

在这里插入图片描述

SheetAdd.java对应关键代码,点击要加入的歌单

public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
    Intent intent=new Intent();
    intent.putExtra("ADD_LIST",position);
    setResult(1, intent);
    finish();
}

删除歌曲事件
MusicListActivity.java对应点击事件函数,点击修改按钮进入修改界面


public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
            case R.id.action_settings:
            if(positionList!=0&&positionList!=1) {//默认列表不可修改
                Intent intentDrag = new Intent(MusicListActivity.this, ItemDragActivity.class);
                intentDrag.putExtra("POSITION",positionList);//传入歌单位置
                startActivity(intentDrag);
            }else{//默认列表不可修改
                Toast.makeText(this, "此列表不可修改", Toast.LENGTH_SHORT).show();
            }
            adapter.notifyDataSetChanged();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

ItemDragActivity.java对应函数,通过滑动歌曲进行删除

OnItemDragListener onItemDragListener = new OnItemDragListener() {
    @Override
    public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos){   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
    @Override
    public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
    @Override
    public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
};

OnItemSwipeListener onItemSwipeListener = new OnItemSwipeListener() {
    @Override
    public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {  list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
    @Override
    public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
    @Override
    public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}

    @Override
    public void onItemSwipeMoving(Canvas canvas, RecyclerView.ViewHolder viewHolder, float dX, float dY, boolean isCurrentlyActive) {   list.clear();//实时更新数据源
        list.addAll(sheet.getList());//实时更新数据源
        adapter.notifyDataSetChanged();}
};

相应适配器代码

    protected void convert(BaseViewHolder holder, Song item) {
if(item!=null) {
    holder.setText(R.id.music_num, item.getId());
    holder.setText(R.id.music_song, item.getSong());
    holder.setText(R.id.music_singer, item.getSinger());
    holder.setText(R.id.music_album, item.getAlbum());
    SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
    String time = sdf.format(new Date(item.getDuration()));
    holder.setText(R.id.music_duration, time);
}
        // (String)item.getDuration()
    }

搜索功能

搜索功能分为两部分,在歌单页面则搜索相应歌单,在歌曲页面则搜索相应歌曲
两个判定函数,中文歌曲可直接进行字符匹配,英文歌曲一般每个单词首字母大写
所以判定条件为,无论大小写输入,都可以按 每个单词首字母大写,其余小写搜索

public String capitalize(String string) {//搜索 首字母大写
    String finalstr="";
    String[] a=   string.split(" ");
    for(int i=0;i<a.length;i++){
        a[i]=a[i].substring(0, 1).toUpperCase()+a[i].substring(1);
        finalstr=finalstr+a[i]+" ";
    }
    finalstr=finalstr.substring(0, finalstr.length()-1);
    return finalstr;
}
public String capitalize2(String string) {//搜索 和上面反过来
    String finalstr="";
    String[] a=   string.split(" ");
    for(int i=0;i<a.length;i++){
        a[i]=a[i].substring(0, 1)+a[i].substring(1).toLowerCase();
        finalstr=finalstr+a[i]+" ";
    }
    finalstr=finalstr.substring(0, finalstr.length()-1);
    return finalstr;
}

MusicListActivity.java搜索歌曲:

private void setSearchList() {
    //设置SearchView默认是否自动缩小为图标
    musicSearch.setIconifiedByDefault(true);
    musicSearch.setFocusable(false);
    //设置搜索框监听器
    musicSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String s) {
            //点击搜索按钮时激发
            return false;
        }
        @Override
        public boolean onQueryTextChange(String s) {
            //输入时激发
            if(TextUtils.isEmpty(s)){
                //没有过滤条件内容
                list.clear();
                list.addAll(sheet.getList());
                adapter.notifyDataSetChanged();
            } else {
                //根据输入内容对RecycleView搜索
                list.clear();
                for (Song bean:sheet.getList()){
if(bean.getSong().contains( capitalize(s))|bean.getSong().contains( capitalize2(s))|bean.getSong().contains(s.toLowerCase()) | bean.getSong().contains(s.toUpperCase()) |bean.getSong().contains(s)) {
                        list.add(bean);
                    }
                }
                adapter.notifyDataSetChanged();
            }
            return true;
        }
    });
}

MainActivity.java搜索歌单:

private void setSearchList() {
    //设置SearchView默认是否自动缩小为图标
    musicSearch.setIconifiedByDefault(true);
    musicSearch.setFocusable(false);
    //设置搜索框监听器
    musicSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String s) {
            //点击搜索按钮时激发
            return false;
        }
        @Override
        public boolean onQueryTextChange(String s) {
            //输入时激发
            if(TextUtils.isEmpty(s)){
                //没有过滤条件内容
                setSheet.clear();
                setSheet.addAll(listSheet.getSheet());
                sheetAdapter.notifyDataSetChanged();
            } else {
                //根据输入内容对RecycleView搜索
                setSheet.clear();
                for (Sheet bean: listSheet.getSheet()){
if(bean.getName().contains( capitalize(s))|bean.getName().contains( capitalize2(s))|bean.getName().contains(s.toLowerCase()) | bean.getName().contains(s.toUpperCase()) |bean.getName().contains(s)  ) {
                        setSheet.add(bean);
                    }
                }
                sheetAdapter.notifyDataSetChanged();
            }
            return true;
        }
    });
}
private void setEventListener() {
//点击弹出侧边栏
    menuIv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            drawerLayout.openDrawer(GravityCompat.START);
        }
    });
}
//点击相应侧边栏页面
  private void setDrawer() {
        navigationView.setItemIconTintList(null);
        navigationView.getChildAt(0).setVerticalScrollBarEnabled(false);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                switch (menuItem.getItemId()){
                    case R.id.item_0:
                        Log.d("ItemSelectedListener", "item0");
                        //fragmentManager.popBackStackImmediate(null, 1);
                        transaction = fragmentManager.beginTransaction();
                        transaction.remove(fragment_about).commit();
                        transaction = fragmentManager.beginTransaction();
                        transaction.remove(fragment_introduction).commit();
                        transaction = fragmentManager.beginTransaction();
                        transaction.remove(fragment_message).commit();
//                        Toast.makeText(MainActivity.this, "!!!!", Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.item_2:
                        //弹出功能介绍
                        transaction = fragmentManager.beginTransaction();
                        transaction.replace(R.id.fragment, fragment_introduction).commit();
                        Log.d("ItemSelectedListener", "item2");
//                        Toast.makeText(MainActivity.this, "2", Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.item_3:
                        //退出
                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                        builder.setTitle("确定要退出吗?")
                                .setIcon(android.R.drawable.ic_dialog_info)
                                .setNegativeButton("取消", null);
                        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                Toast.makeText(MainActivity.this, "退出成功", Toast.LENGTH_SHORT).show();
                                finish();   sheetAdapter.notifyDataSetChanged();
                            }
                        });
                        builder.show();
                        break;
                }
                sheetAdapter.notifyDataSetChanged();
                drawerLayout.closeDrawer(GravityCompat.START);
                transaction.addToBackStack(null);
                return true;
            }
        });
        //设置滑动主activity跟随
        drawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {
            @Override
            public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
                //获取高度宽度
                DisplayMetrics metrics = getResources().getDisplayMetrics();
                Display display = MainActivity.this.getWindowManager().getDefaultDisplay();
                display.getMetrics(metrics);
                //设置activity高度,注意要加上状态栏高度
                RelativeLayout relativeLayout = findViewById(R.id.main_activity);
                relativeLayout.layout(drawerView.getRight(), Notification_height, drawerView.getRight()+metrics.widthPixels, metrics.heightPixels);

            }
            @Override
            public void onDrawerOpened(@NonNull View drawerView) {
            }
            @Override
            public void onDrawerClosed(@NonNull View drawerView) {
            }
            @Override
            public void onDrawerStateChanged(int newState) {
            }
        });
    }

精化设计

SplashActivity,动态获取权限

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
//        setContentView(R.layout.activity_splash);
        initView();
        final RxPermissions rxPermissions = new RxPermissions(this);
        rxPermissions
                .request(Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.INTERNET,
                        Manifest.permission.ACCESS_NETWORK_STATE)
                .subscribe(granded -> {
                    if (granded) {
                        //获取权限后
                        new Handler().postDelayed(new Runnable() {
                            public void run() {
                                Intent intent = new Intent(SplashActivity.this, MainActivity.class);   //第二个参数即为执行完跳转后的Activity
                                startActivity(intent);
                                SplashActivity.this.finish();   //关闭splashActivity,将其回收,否则按返回键会返回此界面
                            }
                        }, SPLASH_DISPLAY_LENGHT);
                        Log.d("rxRermision", "get");
                    } else {
                        Toast.makeText(this, "未获取到存储权限", Toast.LENGTH_LONG).show();
                        Log.d("rxRermision", "denied");
                    }
                });

设置启动时动画,初始化控件,关键代码:

private void initView() {
    //加载视频资源控件
    videoview = (CustomVideoView) findViewById(R.id.videoview);
    //设置播放加载路径
    //videoview.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.video));
    videoview.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.splash));
    //播放
    videoview.start();
    //循环播放
    videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) {
            videoview.start();
        }  });}

侧边栏功能

包含3个侧边栏activity,分别为Frag_introduction.java, Frag_message.java, Frag_quit.java
分别为介绍,返回首页,退出功能
MainActivity.java中的监听事件关键代码:

界面展示

主界面
AndroidStudio 简单却功能完备的音乐播放器_第3张图片
AndroidStudio 简单却功能完备的音乐播放器_第4张图片

启动加载页面
AndroidStudio 简单却功能完备的音乐播放器_第5张图片
询问授权
AndroidStudio 简单却功能完备的音乐播放器_第6张图片
主页面(歌单页面)
AndroidStudio 简单却功能完备的音乐播放器_第7张图片
添加歌单
AndroidStudio 简单却功能完备的音乐播放器_第8张图片

搜索功能演示:
在这里插入图片描述
在这里插入图片描述
歌单页面(以全部音乐为例)
AndroidStudio 简单却功能完备的音乐播放器_第9张图片
侧边栏界面
AndroidStudio 简单却功能完备的音乐播放器_第10张图片

参考资料

https://www.bilibili.com/video/BV1oJ41197fi?from=search&seid=570955563504969972

https://blog.csdn.net/dl10210950/article/details/52248893

项目链接

链接:https://github.com/FallingForYouForever/MusicPlayer.git

你可能感兴趣的:(笔记,android,android,studio,java)