安卓 文件管理相关功能记录

news2024/12/18 5:05:22

文件管理细分为图片、视频、音乐、文件四类

目录

权限

静态声明权限

动态检查和声明权限方法

如何开始上述动态申请的流程

提示

图片

获取图片文件的对象列表

展示

删除

视频

获取视频文件的对象列表

获取视频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();
         }
     }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261393.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CHIMA网络安全攻防大赛经验分享

比赛模式 第一轮&#xff1a;20分钟基础知识赛&#xff08;50道题&#xff09; 安全运维&#xff0c;法律法规&#xff0c;linux操作系统等 第二轮&#xff1a;50分钟CTF夺旗&#xff08;5道题&#xff09; 题目涵盖 密码学 运用多种工具&#xff0c;如ASCII对照&#xff0c…

QT 国际化(翻译)

QT国际化&#xff08;Internationalization&#xff0c;简称I18N&#xff09;是指将一个软件应用程序的界面、文本、日期、数字等元素转化为不同的语言和文化习惯的过程。这使得软件能够在不同的国家和地区使用&#xff0c;并且可以根据用户的语言和地区提供本地化的使用体验。…

[Java] 使用 VSCode 来开发 Java

目录 前言Java 环境怎么看自己是否已经配置完成&#xff1f;安装 JDK安装 Maven 环境修改 Maven 依赖源 完善 VS Code配置插件配置 Maven配置 Maven Settings配置 Maven 可执行文件地址 前言 由于使用 VSCode 编码已经成为习惯&#xff0c;并且它确实相对其他的 IDE 较为轻量化…

如何高效获取Twitter数据:Apify平台上的推特数据采集解决方案

引言 在数据分析和市场研究领域&#xff0c;Twitter&#xff08;现在的X&#xff09;数据一直是重要的信息来源。但是&#xff0c;自从Twitter更改API定价策略后&#xff0c;获取数据的成本大幅提升。本文将介绍一个经济实惠的替代方案。 为什么需要Twitter数据&#xff1f; …

vue3+ant design vue实现日期选择器不展示清除按钮

1、代码&#xff1a;只需设置:allowClear"false"即可 <a-date-pickerv-model:value"value1":disabledDate"disabledDate"change"queryRate":allowClear"false" />const disabledDate (current: Dayjs) > {// 获取…

S2CRNet 图像测评笔记 图像融合

空间分离曲线渲染网络用于高效高分辨率图像协调 开源地址&#xff1a; https://github.com/stefanLeong/S2CRNet 效果图&#xff1a; 左边是输入&#xff0c;最右边是效果&#xff1a;效果不是很理想&#xff0c;色差问题还在 本地代码&#xff1a; S2CRNet-demos-main

【计算机网络】Layer4-Transport layer

目录 传输层协议How demultiplexing works in transport layer&#xff08;传输层如何进行分用&#xff09;分用&#xff08;Demultiplexing&#xff09;的定义&#xff1a;TCP/UDP段格式&#xff1a; UDPUDP的特点&#xff1a;UDP Format端口号Trivial File Transfer Protocol…

【Excel】单元格分列

目录 分列&#xff08;新手友好&#xff09; 1. 选中需要分列的单元格后&#xff0c;选择 【数据】选项卡下的【分列】功能。 2. 按照分列向导提示选择适合的分列方式。 3. 分好就是这个样子 智能分列&#xff08;进阶&#xff09; 高级分列 Tips&#xff1a; 新手推荐基…

易语言鼠标轨迹算法(游戏防检测算法)

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

.net winform 实现CSS3.0 泼墨画效果

效果图 代码 private unsafe void BlendImages1(Bitmap img1, Bitmap img2) {// 确定两个图像的重叠区域Rectangle rect new Rectangle(0, 0,Math.Min(img1.Width, img2.Width),Math.Min(img1.Height, img2.Height));// 创建输出图像&#xff0c;尺寸为重叠区域大小Bitmap b…

Https身份鉴权(小迪网络安全笔记~

附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;若有错误欢迎指正&#xff01; 5.2 Https&身份鉴权 引子&#xff1a;上一篇主要对Http数据包结构、内容做了介绍&#xff0c;本篇则聊聊Https、身份鉴权等技术。 …

7.OPEN SQL

总学习目录请点击下面连接 SAP ABAP开发从0到入职&#xff0c;冷冬备战-CSDN博客 目录 ​编辑 1.OPEN-SQL 简单回顾 R3体系 OEPN-SQL 2.OPEN-SQL 读取数据 2.1Select 语句 select 1条数据 多条数据与into AS别名 2.2INTO 结构体 内表 例子 2.3FROM 选择动态表…

PLC网关,plc远程通信 —— 跨越距离远程控制运维升级

在日新月异的工业4.0时代&#xff0c;智能化、网络化已成为制造业转型升级的关键词。其中&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为工业自动化控制的核心设备&#xff0c;其远程通信技术的突破&#xff0c;正引领着一场前所未有的工业变革。今天&#xff0…

Python-基于Pygame的小游戏(天空之战)(一)

前言:不久前接触了Python的游戏制作的相关第三方库&#xff0c;于是学习了pygame的相关内容&#xff0c;想制作一款基于pygame的小游戏。因为还不太熟悉游戏制作和pygame&#xff0c;部分内容我参考了《Python-从入门到精通》这本书。那么好&#xff0c;话不多说&#xff0c;我…

CV(4)--边缘提取和相机模型

前言 仅记录学习过程&#xff0c;有问题欢迎讨论 边缘提取&#xff08;涉及语义分割&#xff09;&#xff1a; 图象的边缘是指图象局部区域亮度变化显著的部分,也有正负之分&#xff0c;暗到亮为正 求边缘的幅度&#xff1a;sobel&#xff0c;Canny算子 图像分高频分量和低…

cocos creator 的 widget组件的使用及踩坑

以下的内容基于cocos creator 3.8版本&#xff0c;如有错误&#xff0c;恳请指出。 &#x1f449;官方文档的指引 应用&#xff1a;以上官方指引有非常清晰的使用方式&#xff0c;接下来说明一些注意事项&#xff1a; 1、与canvas搭配的使用&#xff0c;解决多分别率适配问题。…

九个任务调度框架

一、背景介绍 说到定时任务&#xff0c;相信大家都不陌生&#xff0c;在我们实际的工作中&#xff0c;用到定时任务的场景可以说非常的多&#xff0c;例如&#xff1a; 双 11 的 0 点&#xff0c;定时开启秒杀每月1号&#xff0c;财务系统自动拉取每个人的绩效工资&#xff0…

Qt6开发自签名证书的https代理服务器

目标&#xff1a;制作一个具备类似Fiddler、Burpsuit、Wireshark的https协议代理抓包功能&#xff0c;但是集成到自己的app内&#xff0c;这样无需修改系统代理设置&#xff0c;使用QWebengineview通过自建的代理服务器&#xff0c;即可实现https包的实时监测、注入等自定义功能…

【深度学习项目】目标检测之YOLO系列详解(一)

介绍 YOLO&#xff08;You Only Look Once&#xff09;是一种实时目标检测算法&#xff0c;由Joseph Redmon等人提出。与传统的基于滑动窗口和区域提案的目标检测方法不同&#xff0c;YOLO将目标检测问题框架化为一个单一的回归问题&#xff0c;直接从图像像素预测边界框和类别…

SpringBoot + minio + kkfile 实现文件预览

1、容器安装kkfileviewer 1.1 下载文件 这里以kkfile 4.4.0-beta版本为例 下载kkfile安装包及Dockerfile&#xff1a; https://codeup.aliyun.com/6254dee9a923b68581caaf50/kkfileviewer.git 1.2、构建镜像 git clone https://codeup.aliyun.com/6254dee9a923b68581caaf50…