文件管理细分为图片、视频、音乐、文件四类
目录
权限
静态声明权限
动态检查和声明权限方法
如何开始上述动态申请的流程
提示
图片
获取图片文件的对象列表
展示
删除
视频
获取视频文件的对象列表
获取视频file列表
按日期装载视频文件列表
展示
播放
删除
音乐
工具类
获取音乐视频文件的对象列表
播放
删除
文件
获取文件对象列表
展示
点击打开
删除
不管是哪种都需要权限,关于安卓的文件访问权限不同版本有不同的管理方式,下面的代码不一定通用
权限
静态声明权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
动态检查和声明权限方法
//判断是否拥有权限
private fun hasPermission() :Boolean {
return ContextCompat.checkSelfPermission(requireContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(requireContext(),Manifest.permission.READ_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED
}
//检查权限 23以前在app安装时静态声明的权限已被授予,不用动态授予
private fun checkHasPermission(jump:Runnable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!hasPermission()) requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) else jump.run()
} else {
jump.run()
}
}
//下面的两个方法是不同的版本申请权限的方法
//请求权限
private fun requestPermission(vararg requestPermission: String) =
ActivityCompat.requestPermissions(requireActivity(), requestPermission, 2024080202)
//到文件管理权限授予界面
private fun toFileManagerPage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()){
val appIntent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
appIntent.data = Uri.parse("package:" + AppUtils.getAppPackageName())
try {
requireActivity().startActivityForResult(appIntent,2024080201)
} catch (ex: ActivityNotFoundException) {
ex.printStackTrace()
val allFileIntent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
requireActivity().startActivityForResult(allFileIntent,2024080201)
}
}else{
LogUtils.d("RequestAccessAndroidDataUtils","已授予所有文件的权限")
}
}
}
//下面的两个方法,是上面两种申请的回调,且下面的两个方法得在activity中才有,如果别的几个方法在fragment中,就使用Livedatabus 将结果发送出来,之后在需要接收的地方接收就行
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//将权限申请结果广播出去
LiveDataBus.getInstance().with("requestCode").setValue(requestCode+","+resultCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//由于可能有多个权限申请,按照逻辑与处理
int resultCode = PackageManager.PERMISSION_GRANTED; //默认权限已授予
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED){
resultCode = PackageManager.PERMISSION_DENIED;
break;
}
}
//将权限申请结果广播出去
LiveDataBus.getInstance().with("requestCode").setValue(requestCode+","+resultCode);
}
//下面的代码就是接收上面发送的权限申请结果
//接受权限申请回调,数据来源于HomeActivity的onRequestPermissionsResult()和onActivityResult()
LiveDataBus.getInstance()
.with("requestCode", String::class.java)
.observe(requireActivity()) {
val result = it.split(",")
if (result[0].equals("2024080201") && result[1].toInt() == PackageManager.PERMISSION_GRANTED){
//文件管理权限回调
}else if(result[0].equals("2024080202") && result[1].toInt() ==PackageManager.PERMISSION_GRANTED){
//文件访问权限回调
}
}
//入口方法
private fun callPermission(runnable: Runnable) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Environment.isExternalStorageManager()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) checkHasPermission(runnable) else runnable.run()
} else {
val builder = AlertDialog.Builder(requireContext())
.setTitle(R.string.zfile_11_title)
.setMessage("管理文件需要您同意“允许所有文件管理权限”才能正常使用!")
.setCancelable(false)
.setPositiveButton(R.string.zfile_down) { d, _ ->
toFileManagerPage()
d.dismiss()
}
.setNegativeButton(R.string.zfile_cancel) { d, _ ->
d.dismiss()
}
builder.show()
}
}
注:没有LivedataBus的可以去看另一篇博客:
安卓LiveDataBus使用记录-CSDN博客
如何开始上述动态申请的流程
binding.noPermissionLayout.setOnClickListener {
callPermission{
//这里就是拥有权限后需要进行的操作
}
}
好了,搞完权限我们来具体到每个分类
由于项目java和kotlin混用,我就不自己转了,需要的自己转化一下吧
提示
下面分类中所有获取或者删除相关文件的代码都最好放到子线程或者协程的io线程中去,免得堵塞主线程
图片
先创建图片文件的bean对象来承载数据
data class FileEntity(
var size: String,
var path: String,
var isSelect: Boolean = false,
var displayName: String = "",
var time: String = ""
) : Serializable {
constructor(size: String, path: String) : this(size, path, false, "", "")
}
获取图片文件的对象列表
最好在子线程中
List<FileEntity> mediaBeen = new ArrayList<>();
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projImage = {MediaStore.Images.Media._ID
, MediaStore.Images.Media.DATA
, MediaStore.Images.Media.SIZE
, MediaStore.Images.Media.DISPLAY_NAME
, MediaStore.Images.Media.DATE_TAKEN};
Cursor mCursor = null;
try {
mCursor = mActivity.getContentResolver().query(mImageUri,
projImage,
MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?"+ "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
new String[]{"image/jpeg", "image/png","image/jpg"},
MediaStore.Images.Media.DATE_MODIFIED + " desc");
} catch (Throwable t) {
}
if (mCursor != null) {
while (mCursor.moveToNext()) {
// 获取图片的路径
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
int size = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE));
String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
Long date = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//用于展示相册初始化界面
mediaBeen.add(new FileEntity(size + "", path, displayName,dateFormat.format(new Date(date))));
}
mCursor.close();
}
//获取到所有图片的列表之后就是对于图片的展示和删除等
展示
//展示第一张图片到ImageView中
String path = listImage.get(0).getPath();
//使用Glide加载本地图片
Glide.with(mActivity)
.load(path)
.error(R.mipmap.error)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Log.e("ImagePositionPath","error: "+e.getMessage());
/* Toast.makeText(mActivity, e.getMessage(), Toast.LENGTH_SHORT).show();*/
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(((ImageViewHolder) holder).iv_photo_filelist_pic);
删除
最好也在子线程中删除
private void deletePicture(String localPath, Context context) {
if(!TextUtils.isEmpty(localPath)){
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = context.getContentResolver();
String url = MediaStore.Images.Media.DATA + "=?";
int deleteRows = contentResolver.delete(uri, url, new String[]{localPath});
if (deleteRows == 0) {//当生成图片时没有通知(插入到)媒体数据库,那么在图库里面看不到该图片,而且使用contentResolver.delete方法会返回0,此时使用file.delete方法删除文件
File file = new File(localPath);
if (file.exists()) {
file.delete();
}
}
}
}
//使用
deletePicture(listImage.get(0).getPath(),context);
视频
先创建视频文件的bean对象来承载数据
public class VideoInfoBean implements Serializable {
//日期
public String date;
//文件名
public String name;
//文件路径
public String path="";
//文件大小
public long packageSize;
//是否可选择
public boolean isSelect;
/**
* 标题为0,内容为1
*/
public int itemType;
}
某一日视频文件的bean对象
public class VideoFileCollenctionBean implements Serializable {
//保存的是年月日
public String date;
/**
* 本日对应的视频文件集合
*/
public List<VideoInfoBean> lists=new ArrayList<>();
}
获取视频文件的对象列表
获取视频file列表
下面的代码还是最好放在子线程中
String path = Environment.getExternalStorageDirectory().getPath();
private List<File> files = new ArrayList<>();
/**
* mp4 mov mkv avi wmv m4v mpg vob webm ogv 3gp flv f4v swf gif
* @param path
*/
private void scanViodeFile(String path){
File file = new File(path);
if (file.isDirectory()) {
File[] f = file.listFiles();
if (null != f) {
for (File file1 : f) {
String fileName=file1.getName().toLowerCase();
if (file1.isDirectory()) {
scanViodeFile(path + "/" + file1.getName());
} else if (fileName.endsWith(".mp4")
||fileName.equals(".mov")
|| fileName.equals(".mkv")
||fileName.equals(".avi")
||fileName.equals(".wmv")
||fileName.equals(".m4v")
||fileName.equals(".mpg")
||fileName.equals(".vob")
||fileName.equals(".webm")
||fileName.equals(".ogv")
||fileName.equals(".3gp")
||fileName.equals(".flv")
||fileName.equals(".f4v")
||fileName.equals(".swf")
&& file.length()!=0) {
files.add(file1);
}
}
}
}
}
按日期装载视频文件列表
处理上面的file列表,将信息装载为VideoFileCollenctionBean 列表,用于和系统相册一样按天展示所有的视频信息
//装载的列表
List<VideoFileCollenctionBean> lists=new ArrayList<>();
//使用集合的不可重复性,找出所有拥有视频的日期
Set<String> set=new TreeSet<String>((o1, o2) -> o2.compareTo(o1));
for (File file : files) {
//将date转换为年月日的字符串
String time= DateUtils.timestampToPatternTime(file.lastModified(), "yyyy-MM-dd");
set.add(time);
}
for (String l : set) {
VideoFileCollenctionBean videoFileCollenctionBean = new VideoFileCollenctionBean();
lists.add(videoFileCollenctionBean);
VideoInfoBean videoInfoBean=new VideoInfoBean();
videoInfoBean.date=l;
videoInfoBeans.add(videoInfoBean);
for (File file : files) {
String time = DateUtils.timestampToPatternTime(file.lastModified(), "yyyy-MM-dd");
if (time.equals(l)) {
VideoInfoBean bean = new VideoInfoBean();
bean.path = file.getPath();
bean.name = file.getName();
bean.date=time;
bean.packageSize = file.length();
bean.itemType=1;
videoFileCollenctionBean.lists.add(bean);
videoInfoBeans.add(bean);
}
}
}
展示
展示一般就是用adapter显示视频列表,按上图所示展示,红框就是一个VideoFileCollenctionBean对象,绿框就是VideoInfoBean对象,细节不说,仅说明一下关键部分
下面的部分应该在adapter的onBindViewHolder()中
//展示视频第一帧图片图片
private void setImgFrame(ImageView imgFrame, String path) {
RequestOptions options = new RequestOptions()
.placeholder(R.color.color_666666)// 正在加载中的图片
.diskCacheStrategy(DiskCacheStrategy.ALL); // 磁盘缓存策略
Glide.with(mContext).load(path).apply(options).into(imgFrame);
}
//使用
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
VideoInfoBean appInfoBean = getLists().get(position);
setImgFrame(viewHolder.mImgFrame, appInfoBean.path);
}
播放
public void play(VideoInfoBean appInfoBean) {
try {
if (appInfoBean.path == null){
return;
}
String filePath = appInfoBean.path;
if (filePath.isEmpty()) return;
File file = new File(filePath);
if (!file.exists()) {
// 文件不存在,可以在这里处理错误
return;
}
// 获取文件 MIME 类型
String mimeType = getMimeType(file);
// 检查 MIME 类型是否为视频类型
if (!isVideoType(mimeType)) {
// 不是视频文件,可以在这里处理错误
return;
}
// 创建 Uri
Uri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);
// 创建 Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 检查是否有应用可以处理此意图
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "当前系统无可播放视频的软件!", Toast.LENGTH_SHORT );
}
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private boolean isVideoType(String mimeType) {
return mimeType.startsWith("video/");
}
private String getMimeType(File file) {
String extension = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();
switch (extension) {
case "mp4":
case "mov":
case "mkv":
case "avi":
case "wmv":
case "m4v":
case "mpg":
case "vob":
case "webm":
case "ogv":
case "3gp":
case "flv":
case "f4v":
case "swf":
return "video/" + extension;
default:
return "video/x-generic"; // 通用视频类型,可能不是最优解
}
}
删除
//子线程中使用
public void delFile(List<VideoInfoBean> list) {
List<VideoInfoBean> files = list;
for (VideoInfoBean appInfoBean : files) {
File file = new File(appInfoBean.path);
if (null != file) {
file.delete();
}
}
}
音乐
先创建音乐文件的bean对象来承载数据
public class MusciInfoBean implements Serializable {
public int id;
//文件名
public String name;
//播放时长
public String time;
//文件路径
public String path;
//文件大小
public long packageSize;
//是否可选择
public boolean isSelect;
@Override
public String toString() {
return super.toString();
}
}
工具类
包名
import android.media.MediaPlayer;
import java.io.IOException;
public class MusicFileUtils {
/**
* 获取音频播放时长
* @param path
* @return
*/
public static String getPlayDuration(String path) {
MediaPlayer player = new MediaPlayer();
String duration="";
try {
//recordingFilePath()为音频文件的路径
player.setDataSource(path);
player.prepare();
//获取音频的时间
duration= timeParse(player.getDuration());
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
player.release();
}
return duration;
}
/**
* 获取音频播放时长
* @param path
* @return
*/
public static String getPlayDuration2(String path) {
MediaPlayer player = new MediaPlayer();
String duration="";
try {
//recordingFilePath()为音频文件的路径
player.setDataSource(path);
player.prepare();
//获取音频的时间
duration= timeParse2(player.getDuration());
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
player.release();
}
return duration;
}
/**
*
* @param duration 音乐时长
* @return
*/
public static String timeParse(long duration) {
String time = "" ;
long minute = duration / 60000 ;
long seconds = duration % 60000 ;
long second = Math.round((float)seconds/1000) ;
if( minute < 10 ){
time += "0" ;
}
time += minute+"''" ;
if( second < 10 ){
time += "0" ;
}
time += second+"'" ;
return time ;
}
/**
*
* @param duration 音乐时长
* @return
*/
public static String timeParse2(long duration) {
String time = "" ;
long minute = duration / 60000 ;
long seconds = duration % 60000 ;
long second = Math.round((float)seconds/1000) ;
if( minute < 10 ){
time += "0" ;
}
time += minute+"分" ;
if( second < 10 ){
time += "0" ;
}
time += second+"秒" ;
return time ;
}
}
获取音乐视频文件的对象列表
private List<MusciInfoBean> musciInfoBeans = new ArrayList<MusciInfoBean>();
private void queryAllMusic(){
Cursor cursor = activity.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.YEAR,
MediaStore.Audio.Media.MIME_TYPE,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.DATA },null
,null,null);
while (cursor.moveToNext()){
//if (cursor.moveToFirst()) {
int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
// String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
File file=new File(url);
if(null!=file){
files.add(file);
MusciInfoBean musciInfoBean = new MusciInfoBean();
musciInfoBean.id=id;
musciInfoBean.name = file.getName();
musciInfoBean.packageSize = file.length();
musciInfoBean.path = file.getPath();
//musciInfoBean.time = MusicFileUtils.getPlayDuration(file.getPath());
musciInfoBean.time=MusicFileUtils.timeParse(duration);
musciInfoBeans.add(musciInfoBean);
}
}
try {
cursor.close();
}catch (Exception e){
e.printStackTrace();
}
}
//按时间升序排列
Collections.sort(musciInfoBeans, ((o1, o2) -> o2.time.compareTo(o1.time)));
展示的就不说了,就把上面的信息展示出来就行
播放
public void play(MusciInfoBean musciInfoBean) {
try {
if (musciInfoBean.path == null){
return;
}
String filePath = musciInfoBean.path;
if (filePath.isEmpty()) return;
File file = new File(filePath);
if (!file.exists()) {
// 文件不存在,可以在这里处理错误
return;
}
// 获取文件 MIME 类型
String mimeType = getMimeType(file);
// 检查 MIME 类型是否为视频类型
if (!isAudioType(mimeType)) {
// 不是音乐文件,可以在这里处理错误
return;
}
// 创建 Uri
Uri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);
// 创建 Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 检查是否有应用可以处理此意图
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "当前系统无可播放音乐的软件!", Toast.LENGTH_SHORT );
}
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private boolean isAudioType(String mimeType) {
return mimeType.startsWith("audio/");
}
private String getMimeType(File file) {
String extension = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();
if ("mp3".equals(extension) || "wav".equals(extension) || "aac".equals(extension) ||
"flac".equals(extension) || "ogg".equals(extension) || "m4a".equals(extension) ||
"wma".equals(extension) || "aiff".equals(extension) || "amr".equals(extension)) {
return "audio/" + extension;
} else {
return "audio/x-generic"; // 通用音频类型,可能不是最优解
}
}
删除
//子线程中使用
public void delFile(List<MusciInfoBean> list) {
List<MusciInfoBean> files = list;
for (MusciInfoBean appInfoBean : files) {
File file = new File(appInfoBean.path);
if (null != file) {
file.delete();
}
}
}
文件
文件的含义就多了,像pdf,word,xml,json,txt,docx等等都可以算文件
先创建文件的bean对象来承载数据
/**
* 文件 实体类
* @property fileName String 文件名
* @property isFile Boolean true---文件;false---文件夹
* @property filePath String 文件路径
* @property date String 格式化后的时间
* @property originalDate String 原始时间(时间戳)
* @property size String 格式化后的大小
* @property originaSize Long 原始大小(byte)
* @property folderLength Int 文件夹包含的文件个数
* @property parent String? 父级所包含的选择文件个数(自定义文件获取不需要给该字段赋值)
*/
//有些字段无实际意义,按需获取就行
data class ZFileBean(
var fileName: String = "",
var isFile: Boolean = true,
var filePath: String = "",
var date: String = "",
var originalDate: String = "",
var size: String = "",
var originaSize: Long = 0L,
var folderLength: Int = 0,
var parent: String? = "",
var folderName: String = "",
var thumbPath: String = "",
var fullPath: String = "",
var imageCount: Int = 0,
var isDelete:Boolean? = false, //是否选中删除
var originalduration:Long =0, //原始视频的时长
) : Serializable ,Parcelable
获取文件对象列表
//这个方法其实可以获取到所有本地的文件,如视频、图片、音乐、压缩包、安装包等,只要传入不同的后缀数组就行
val filefilterArray = arrayOf(DOC, DOCX, XLS, XLSX, PPT, PPTX, PDF, TXT, ZIP, JSON, XML) //还有补充的可以再加
private fun getLocalData(filterArray: Array<String>): MutableList<ZFileBean> {
val list = arrayListOf<ZFileBean>()
var cursor: Cursor? = null
try {
val fileUri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.TITLE,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_MODIFIED
)
val sb = StringBuilder()
filterArray.forEach {
if (it == filterArray.last()) {
sb.append(MediaStore.Files.FileColumns.DATA).append(" LIKE '%.$it'")
} else {
sb.append(MediaStore.Files.FileColumns.DATA).append(" LIKE '%.$it' OR ")
}
}
val selection = sb.toString()
val sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED
val resolver = getContext()?.contentResolver
cursor = resolver?.query(fileUri, projection, selection, null, sortOrder)
if (cursor?.moveToLast() == true) {
do {
val path =
cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA))
val size =
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE))
val date =
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATE_MODIFIED))
val fileSize = ZFileOtherUtil.getFileSize(size)
val lastModified = ZFileOtherUtil.getFormatFileDate(date * 1000)
if (size > 0.0) {
val name = path.substring(path.lastIndexOf("/") + 1, path.length)
list.add(
ZFileBean(
name,
true,
path,
lastModified,
date.toString(),
fileSize,
size
)
)
}
} while (cursor.moveToPrevious())
}
} catch (e: Exception) {
ZFileLog.e("根据特定条件 获取文件 ERROR ...")
e.printStackTrace()
} finally {
cursor?.close()
return list
}
}
展示
展示没啥说的,顶多根据文件的后缀名显示不同的图片就行
点击打开
在res中添加一个xml的文件夹,在xml文件夹中创建dn_file_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="files_path"
path="." />
<cache-path
name="cache_path"
path="." />
<external-path
name="external_path"
path="." />
<external-files-path
name="external_files_path"
path="." />
<external-cache-path
name="external_cache_path"
path="." />
<external-media-path
name="external_media_path"
path="." />
<!-- 添加共享目录-->
<external-path
name="external_files"
path="." />
</paths>
在AndroidManifast.xml中注册内容提供商
<application
android:allowBackup="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="包名.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/dn_file_paths"/>
</provider>
</application>
打开方法
private fun openFile(filePath: String) {
if (filePath.isEmpty()) return
val file = File(filePath)
if (!file.exists()) {
// 文件不存在,可以在这里处理错误
return
}
val mimeType = getMimeType(file)
val uri = FileProvider.getUriForFile(this, "${this.packageName}.provider", file)
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, mimeType)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
// 检查是否有应用可以处理此意图
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
// 没有应用可以处理此文件类型
// 可以提示用户安装支持的应用程序
}
}
private fun getMimeType(file: File): String {
val extension = file.extension.toLowerCase()
return when (extension) {
"doc", "docx" -> "application/msword"
"xls", "xlsx" -> "application/vnd.ms-excel"
"ppt", "pptx" -> "application/vnd.ms-powerpoint"
"pdf" -> "application/pdf"
"txt" -> "text/plain"
"json" -> "application/json"
else -> "*/*" // 通用类型,可能不是最优解
}
}
删除
//子线程中使用
public void delFile(List<ZFileBean> list) {
List<ZFileBean> files = list;
for (ZFileBean appInfoBean : files) {
File file = new File(appInfoBean.filePath);
if (null != file) {
file.delete();
}
}
}