最近我在学习在学习RecyclerView,跟着老司机玩转App,《Android App 开发入门与项目实战》很友好,比较适合新手入门,通过基础知识和案例相结合,慢慢掌握其中要点。
书中的音乐案例在我的Android Studio模拟器中运行,从媒体库MediaStore.Audio.Media.EXTERNAL_CONTENT_URI加载音频文件列表,显示只有两首歌曲,无论我怎么重启模拟器,结果还是如此。于是我选择从sdcard/Music下遍历文件,通过适配器加载到RecyclerView中。

 [案例代码仓库](https://codechina.csdn.net/mirrors/aqi00/myapp) 推荐购买书籍来上手。

运行书中的案例,结果如下,显示模拟器中的两首歌曲,实际导入了9首。

一番操作之后,可以显示完整的音乐列表,除了最佳损友不能播放,或许是MediaPlayer不喜欢损友爱基友。

以下是具体的步骤:
1、AndroidManifest.xml加载存储卡读写权限:

    <!-- 存储卡读写 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

运行在Android 10及更高版本手机,配置暂时停用分区存储。
<application android:requestLegacyExternalStorage=“true”

2、申请动态权限:方式一、用代码来申请权限;方式二、可以在手机设置中手动打开如下的权限。

3、写出每个音乐条目布局,没有点击播放时隐藏进度条,点击之后显示它。因为是progressBar,所以无法拖动播放进度。
item_audio.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/ll_audio"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_name"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="4"android:gravity="left|center"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:id="@+id/tv_duration"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><LinearLayoutandroid:id="@+id/ll_progress"android:layout_width="match_parent"android:layout_height="30dp"android:orientation="horizontal"android:visibility="gone"><ProgressBarandroid:id="@+id/pb_audio"style="?android:attr/progressBarStyleHorizontal"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="4" /><TextViewandroid:id="@+id/tv_progress"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout></LinearLayout>

4、主界面显示RecyclerView布局。
activity_audio_play.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="5dp"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="点击音频列表开始播放"android:textColor="@color/black"android:textSize="17sp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:layout_margin="2dp"android:orientation="horizontal"><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:gravity="left|center"android:text="音频名称"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:text="总时长"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_audio"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>

5、创建bean的音乐信息类文件,提供信息给适配器,适配器作为沟通的桥梁,显示到RecyclerView。

public class AudioInfo {private String title; // 标题private int duration; // 播放时长private String path; // 文件路径private int progress = -1; // 播放进度public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public int getDuration() {return duration;}public void setDuration(int duration) {this.duration = duration;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public int getProgress() {return progress;}public void setProgress(int progress) {this.progress = progress;}}

6、创建点击监听器接口

public class RecyclerExtras {// 定义一个循环视图列表项的点击监听器接口public interface OnItemClickListener {void onItemClick(View view, int position);}}

7、创建适配器
循环视图有专门的循环适配器RecyclerView.Adapter,在setAdapter 方法之前,得先实现一个从
RecyclerView.Adapter 派生而来的适配器,用来定义列表项的界面布局及其控件操作。下面是实现循环适配器时有待重写的方法说明。
●getItemCount: 获得列表项的数目。
●onCreateViewHolder: 创建整个布局的视图持有者,可在该方法中指定列表项的布局文件。第二个输入参数为视图类型viewType,根据视图类型加载不同的布局,从而实现带头部的列表布局。
●onBindViewHolder: 绑定列表项的视图持有者。可在该方法中操纵列表项的控件。
以上3种方法是必需的,每个自定义的循环适配器都要重写这3种方法。

●getltemViewType: 返回每项的视图类型。这里的类型与onCreateViewHolder 方法的viewType参数保持一致。
●getItemld: 获得每个列表项的编号。
以上两种方法不是必需的,可以重写也可以不重写。
在我看来循环视图里的视图持有者是容纳视图,RecyclerView和adapter 在相互对话交流中创建了视图。先是RecyclerView调用getItemCount()询问adapter有多少条列表项的数目要显示,adapter告之具体的数目。接着RecyclerView开始创建ViewHolder,加载列表项的布局。然后adapter返回ViewHolder,每个ViewHolder容纳一个带item_audio.xml的布局。RecyclerView用onBindViewHolder: 绑定列表项的视图持有者对应的第一条列表项上,将数据绑定显示在ViewHolder上。之后同样的方式再绑定到第二条列表项上。…
RecyclerView可以循环利用,超出屏幕的ViewHolder将会被回收利用,不会一直onCreateViewHolder(),但是会onBindViewHolder()直到你不滚动显示页面。如此可以节约资源,提高性能。

书中的内容:工作的步骤如下:
步骤1、在构造方法中传入消息列表。
步骤2、重写getItemCount方法,返回列表项的个数。
步骤3、定义一个由RecyclerView. ViewHolder派生而来的内部类,用作列表项的视图持有者。
步骤4、重写onCreateViewHolder 方法,根据指定的布局文件生成视图对象,并返回该视图对象对应的视图持有者。
步骤5、onBindViewHolder方法,从输入参数中的视图持有者获取各个控件实例,再操纵这些控件(设置文字、设置图片、设置点击监听器等)。

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;import androidx.recyclerview.widget.RecyclerView;import com.example.chapter13.R;
import com.example.chapter13.bean.AudioInfo;
import com.example.chapter13.util.MediaUtil;
import com.example.chapter13.widget.RecyclerExtras;import java.util.List;public class AudioRecycler extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private Context mContext; // 声明一个上下文对象private List<AudioInfo> audio_list ; // 声明一个音频信息列表public AudioRecycler(Context context, List<AudioInfo> mp3audio_list) {mContext = context;audio_list = mp3audio_list;}// 获取列表项的个数public int getItemCount() {return audio_list.size();}// 创建列表项的视图持有者public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {// 根据布局文件item_audio.xml生成视图对象View v = LayoutInflater.from(mContext).inflate(R.layout.item_audio, vg, false);return new ItemHolder(v);}// 绑定列表项的视图持有者public void onBindViewHolder(RecyclerView.ViewHolder vh, final int position) {ItemHolder holder = (ItemHolder) vh;AudioInfo audio = audio_list.get(position);holder.tv_name.setText(audio.getTitle()); // 显示音频名称holder.tv_duration.setText(MediaUtil.formatDuration(audio.getDuration())); // 显示音频时长if (audio.getProgress() >= 0) { // 正在播放holder.ll_progress.setVisibility(View.VISIBLE);holder.pb_audio.setMax(audio.getDuration()); // 设置进度条的最大值,也就是媒体的播放时长holder.pb_audio.setProgress(audio.getProgress()); // 设置进度条的播放进度,也就是已播放的进度holder.tv_progress.setText(MediaUtil.formatDuration(audio.getProgress())); // 显示已播放时长} else { // 没在播放holder.ll_progress.setVisibility(View.GONE);}// 列表项的点击事件需要自己实现holder.ll_audio.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mOnItemClickListener != null) {mOnItemClickListener.onItemClick(v, position);}}});}//    // 获取列表项的类型
//    public int getItemViewType(int position) {//        return 3;
//    }
//
//    // 获取列表项的编号
//    public long getItemId(int position) {//        return position;
//    }// 定义列表项的视图持有者public class ItemHolder extends RecyclerView.ViewHolder {public LinearLayout ll_audio; // 声明音频列表的线性布局对象public TextView tv_name; // 声明音频名称的文本视图对象public TextView tv_duration; // 声明总时长的文本视图对象public LinearLayout ll_progress; // 声明进度区域的线性布局对象public ProgressBar pb_audio; // 声明音频播放的进度条对象public TextView tv_progress; // 声明已播放时长的文本视图对象public ItemHolder(View v) {super(v);ll_audio = v.findViewById(R.id.ll_audio);tv_name = v.findViewById(R.id.tv_name);tv_duration = v.findViewById(R.id.tv_duration);ll_progress = v.findViewById(R.id.ll_progress);pb_audio = v.findViewById(R.id.pb_audio);tv_progress = v.findViewById(R.id.tv_progress);}}// 声明列表项的点击监听器对象private RecyclerExtras.OnItemClickListener mOnItemClickListener;public void setOnItemClickListener(RecyclerExtras.OnItemClickListener listener) {this.mOnItemClickListener = listener;}}

8、主程序AudioPlayer,MediaPlayer提供的方法虽多,基本的应用场景只有两个,一个是播放指定音频文件,另一个是退出页面时释放媒体资源。其中播放音频的场景需要历经下列步骤:重置播放器→设置媒体文件的路径→准备播放→开始播放。

import com.example.chapter13.widget.RecyclerExtras;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;import com.example.chapter13.adapter.AudioRecycler;
import com.example.chapter13.bean.AudioInfo;import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;public  class AudioPlayer extends AppCompatActivity implements RecyclerExtras.OnItemClickListener {private RecyclerView rv_audio; // 音频列表的循环视图private List<AudioInfo> aAudio_list = new ArrayList<AudioInfo>(); // 音频列表private AudioRecycler mAdapter; // 音频列表的适配器private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器private int mLastPosition = -1; // 上次播放的音频序号private Timer mTimer = new Timer(); // 计时器@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_audio_play);rv_audio = findViewById(R.id.rv_audio);rv_audio.addItemDecoration(new RecycleViewDivider(this, LinearLayoutManager.VERTICAL, R.drawable.divider_mileage));//RecycleView加入分割线///mnt/sdcard/MusicFile file=new File("/sdcard/Music");//手机可以用如下的路径加载//File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_MUSIC).getAbsolutePath());final File[] files=file.listFiles();if (files == null){Log.e("Error","这是一个空目录");}for(int i =0;i<files.length;i++) {AudioInfo audioInfo = new AudioInfo();if (files[i].getName().endsWith(".mp3")) {//根据文件后缀类型加载到列表audioInfo.setTitle(files[i].getName().split(".mp3")[0]);//文件名中获取.mp3后缀前面的歌名audioInfo.setPath(files[i].getAbsolutePath());//获取音频文件的绝对路径,在MediaPlayer中加载源文件aAudio_list.add(audioInfo);}}showAudioList();}// 显示音频列表private void showAudioList() {// 创建一个水平方向的线性布局管理器LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);rv_audio.setLayoutManager(manager); // 设置循环视图的布局管理器mAdapter = new AudioRecycler(this, aAudio_list); // 创建音频列表的线性适配器mAdapter.setOnItemClickListener((RecyclerExtras.OnItemClickListener) this); // 设置线性列表的点击监听器rv_audio.setAdapter(mAdapter); // 设置循环视图的列表适配器}@Overridepublic void onItemClick(View view, final int position) {if (mLastPosition!=-1 && mLastPosition!=position) {//点击切歌之后,播放重置AudioInfo last_audio = aAudio_list.get(mLastPosition);last_audio.setProgress(-1); // 当前进度设为-1表示没在播放aAudio_list.set(mLastPosition, last_audio);mAdapter.notifyItemChanged(mLastPosition); // 刷新此处的列表项}mLastPosition = position;final AudioInfo audio = aAudio_list.get(position);mTimer.cancel(); // 取消计时器mMediaPlayer.reset(); // 重置媒体播放器// mMediaPlayer.setVolume(0.5f, 0.5f); // 设置音量,可选mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐try {mMediaPlayer.setDataSource(audio.getPath()); // 设置媒体数据的文件路径mMediaPlayer.prepare(); // 媒体播放器准备就绪audio.setDuration(mMediaPlayer.getDuration());// 读取音频的总时长后设置为音频播放时长的属性,不完美的地方就是要播放之后才显示mMediaPlayer.start(); // 媒体播放器开始播放mMediaPlayer.setLooping(true);} catch (Exception e) {e.printStackTrace();}mTimer = new Timer(); // 创建一个计时器mTimer.schedule(new TimerTask() {@Overridepublic void run() {audio.setProgress(mMediaPlayer.getCurrentPosition()); // 设置进度条的当前进度aAudio_list.set(position, audio);// 界面刷新操作需要在主线程执行,故而向处理器发送消息,由处理器在主线程更新界面mHandler.sendEmptyMessage(position);}}, 0, 1000); // 计时器每隔一秒就更新进度条上的播放进度}private Handler mHandler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);mAdapter.notifyItemChanged(msg.what); // 刷新此处的列表项}};}

RecyclerView加载sdcard/Music下的音乐列表相关推荐

  1. Android使用RecyclerView实现上拉加载更多,下拉刷新,分组显示

    项目地址:点击打开链接(https://github.com/MrGaoGang/luckly_recyclerview) 使用RecyclerView封装headerview,footerView, ...

  2. Android RecyclerView加载复杂布局

    Android RecyclerView加载复杂布局 用一个RecyclerView实现多种复杂布局,复用机制要保存 简书:Android RecyclerView加载复杂布局 demo源码挂载在码云 ...

  3. el-image中src加载assets路径下图片使用require避免加载不到

    场景 el-image中图片的数据源加载assets路径下的照片. 正确加载方式. <el-imagestyle="width: 732px; height: 48px":s ...

  4. Android中WebView加载sdcard中的html时提示:ERR_FILE_NOT_FOUND和ERR_ACCESS_DENIED

    场景 Android中WebView加载sdcard中的html显示: Android中WebView加载sdcard中的html显示_BADAO_LIUMANG_QIZHI的博客-CSDN博客 在实 ...

  5. RecyclerView加载不同view实现效果--IT蓝豹

    本项目由开发者:黄洞洞精心为初学者编辑RecyclerView的使用方法. RecyclerView加载不同view实现效果,支持加载多个view,并且支持用volley获取数据, 项目主要介绍: 初 ...

  6. 加载指定路径下所有文件

    工作中经常遇到加载某个文件夹下的所有文件,然后对文件遍历,今天写代码时遇到一个好的类ArrayDeque,整理一下,做一下笔记 下面写了一个测试类 package com.hpzx.test;impo ...

  7. html 下拉滚动加载,原生js滚动到底部加载数据和下拉刷新 Scrollload

    初衷 如今移动端站点越来越多,滚动到底部加载数据和下拉刷新的需求非常的常见,即使现在很多pc站点也会有这样的需求,比如百度首页就有.虽然简单的完成这么一个功能非常方便,但是滚动往往会成为性能的瓶颈,处 ...

  8. 原生js滚动到底部加载数据和下拉刷新 Scrollload

    原文地址 https://github.com/fa-ge/Scrollload/blob/master/README.md 初衷 如今移动端站点越来越多,滚动到底部加载数据和下拉刷新的需求非常的常见 ...

  9. RecyclerView加载瀑布流,图片自适应问题

    在使用recyclerView加载瀑布流时,由于图片加载慢于item绘制,从而导致item中的图片无法正常显示,不是被压缩就是拉伸. 解决方案: 1.后台给一个图片的原始宽高,在recyclerVie ...

最新文章

  1. P1155 双栈排序(二分图的染色判断+链式前向星)
  2. Python跨平台文件夹分割方法os.sep
  3. 关于EXPORT_SYMBOL
  4. hihoCoder #1639 图书馆
  5. 操作系统学习(三)-- CPU调度
  6. python求圆柱体的体积_Python 常见优化技巧,让你的程序溜溜的跑起来!
  7. 变换例题_小学语文学习攻略9:句式变换知识点概述+例题讲练
  8. 基于设备树的TQ2440 DMA学习(3)—— DMA控制器驱动
  9. TSX指令集中的 RTM 受限的事务内存 来实现并发无锁编程
  10. imageJ下载链接
  11. 怎么将计算机的网络恢复连接,win7怎么还原网络设置 win7本地连接不上的解决方法...
  12. 一个简单的跑酷引擎-------bibibibi
  13. UGUI——RectTransform详解
  14. 尚硅谷实战项目---手机影音APP
  15. r510服务器开机无显示,联想B320i开机无显示维修案例
  16. 微信小程序实现长按复制和点击复制
  17. 485通讯协议在java中的应用
  18. FFmpeg音视频核心技术精讲与实战-李超-ffmpeg开发使用笔记2
  19. CSRF跨站点伪造请求攻击之——CSRF钓鱼添加管理员账号及安全防范
  20. E3游戏展十大观察:游戏行业缺乏惊喜1A1M

热门文章

  1. java基于springboot+vue的学生考勤请假打卡管理系统 elementui
  2. qt 主窗口关闭时关闭子窗口
  3. 柬埔寨最新咨讯及吴哥遗迹全接触
  4. 2018北邮网研院机试真题
  5. 解决 .webp 格式图片在 ios 设备上无法正常显示的问题
  6. 物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块
  7. 计算机硬件工艺心得,电子工艺实训心得体会
  8. 信息系统项目管理师考试论文写作复习笔记(6)-成本管理论文指导
  9. 一套完整的汽车服务终端管理系统,源代码分享
  10. Float.compare()和Double.compare()的使用