IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> RecyclerView加载sdcard/Music下的音乐列表 -> 正文阅读

[移动开发]RecyclerView加载sdcard/Music下的音乐列表

最近我在学习在学习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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal">

        <TextView
            android: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" />

        <TextView
            android: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>

    <LinearLayout
        android:id="@+id/ll_progress"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:orientation="horizontal"
        android:visibility="gone">

        <ProgressBar
            android:id="@+id/pb_audio"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="4" />

        <TextView
            android: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">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="点击音频列表开始播放"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="2dp"
        android:orientation="horizontal">

        <TextView
            android: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" />

        <TextView
            android: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.RecyclerView
        android: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() {
            @Override
            public 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(); // 计时器

    @Override
    protected 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/Music
        File 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); // 设置循环视图的列表适配器
    }


    @Override
    public 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() {
            @Override
            public void run() {
                audio.setProgress(mMediaPlayer.getCurrentPosition()); // 设置进度条的当前进度
                aAudio_list.set(position, audio);
                // 界面刷新操作需要在主线程执行,故而向处理器发送消息,由处理器在主线程更新界面
                mHandler.sendEmptyMessage(position);

            }
        }, 0, 1000); // 计时器每隔一秒就更新进度条上的播放进度
    }

    private Handler mHandler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mAdapter.notifyItemChanged(msg.what); // 刷新此处的列表项
        }
    };

}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-22 13:39:10  更:2021-08-22 13:40:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:16:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码