Android -- Demo(四) 自定义弹框式文件选择器

Android – demo (四) 自定义弹框式文件选择器

简介:本文件选择器继承Dialog, 调用时如弹框一样;可筛选文件类型或仅文件夹,返回选中的文件或文件夹列表

项目链接:https://blog.csdn.net/weixin_43738911/article/details/109449345

效果演示:在这里插入图片描述

使用方法:

public class xxx implements VirgoFileSelectorDialog.SelectFileCallback{
    private VirgoFileSelectorDialog dialog;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dialog = new VirgoFileSelectorDialog(context);
        
        dialog.setDialogHeight(ScreenUtil.getScreenHeight(context)/2);//设置弹框高度(显示屏高度一半)
        dialog.setFlag(VirgoFileSelectorDialog.FLAG_FILE);//设置要选择的是文件还是文件夹
        dialog.setFileType(VirgoFileSelectorDialog.TYPE_IMAGE);//选择dir后,无效
        dialog.setRootPath("/storage/emulated/0");//设置根目录,若无效调用默认值
        dialog.setCallback(this);//设置回调,必选,若要返回值
        
        dialog.show();//显示
    }
    
    //从文件选择框返回的数据
    @Override
    public void onResult(List<File> list) {
        Log.i(TAG, "onResult: ");
        //deal with the result
    }
}

源代码:

  1. VirgoFileSelectorDialog.java

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iudvK0k2-1604916243827)(E:\ZSLWORK\media\fileSelector.gif)]/**
     * @author nepalese on 2020/10/30 15:20
     * @usage 弹框式文件选择器,可筛选文件类型或仅文件夹,返回选中的文件或文件夹
     *
     */
    public class VirgoFileSelectorDialog extends Dialog implements ListView_FileSelector_Adapter.FileInterListener {
        private static final String TAG = "FileSelectorDialog";
    
        //选择文件或文件夹
        public static final int FLAG_DIR = 0;
        public static final int FLAG_FILE = 1;
    
        //仅显示某种特定文件
        public static final String TYPE_ALL = "all";
        public static final String TYPE_IMAGE = "image";
        public static final String TYPE_TEXT = "txt";
        public static final String TYPE_VIDEO = "video";
        public static final String TYPE_AUDIO = "audio";
    
        //选择特定文件类型后将显示的文件拓展名
        public static final String[] IMAGE_EXTENSION = {"jpg", "jpeg", "png", "svg", "bmp", "tiff"};
        public static final String[] AUDIO_EXTENSION = {"mp3", "wav", "wma", "aac", "flac"};
        public static final String[] VIDEO_EXTENSION = {"mp4", "flv", "avi", "wmv", "mpeg", "mov", "rm", "swf"};
        public static final String[] TEXT_EXTENSION = {"txt", "java", "html", "xml", "php"};
    
        private static final String DEFAULT_ROOT_PATH = "/storage/emulated/0";//默认初始位置
    
        private Context context;
        private SelectFileCallback callback;
    
        private TextView tvCurPath, tvConfirm;
        private LinearLayout layoutRoot, layoutLast;
        private ListView listView;
        private ListView_FileSelector_Adapter adapter;
    
        private String curPath;//当前路径
        private List<File> files;//返回值
        private List<Integer> index;//选中文件、夹索引
    
        //默认设置
        private int flag = FLAG_FILE;//默认选择文件
        private String rootPath = DEFAULT_ROOT_PATH;
        private String fileType = TYPE_ALL;
        private int dialogHeight = 500;//默认弹框高度
    
        public VirgoFileSelectorDialog(@NonNull Context context) {
            //默认自定义弹框样式,使用下面的构造函数可另设样式
            this(context, R.style.File_Dialog);
        }
    
        public VirgoFileSelectorDialog(@NonNull Context context, int themeResId) {
            super(context, themeResId);
            setCancelable(false);
            init(context);
        }
    
        private void init(Context context) {
            this.context = context;
    
            LayoutInflater mLayoutInflater = LayoutInflater.from(context);
            View view = mLayoutInflater.inflate(R.layout.layout_file_selector, null);
    
            tvCurPath = view.findViewById(R.id.tvCurPath);
            tvConfirm = view.findViewById(R.id.tvConfirmChoose);
    
            layoutRoot = view.findViewById(R.id.layoutToRoot);
            layoutLast = view.findViewById(R.id.layoutToLast);
    
            listView = view.findViewById(R.id.listViewFile);
            index = new ArrayList<>();
    
            setContentView(view);
            setListener();
        }
    
        //==============================================================================================
        //初始化数据
        private void setData() {
            setLayout();//设置弹框布局
            curPath = rootPath;
            tvCurPath.setText(curPath);
            files  = new ArrayList(getFiles(curPath));
    
            adapter = new ListView_FileSelector_Adapter(context, files, this);//指向的是最开始的list
    //        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
            listView.setAdapter(adapter);
        }
    
        private void setLayout() {
            Window dialogWindow = this.getWindow();
            WindowManager.LayoutParams lp = dialogWindow.getAttributes();
            dialogWindow.setGravity(Gravity.CENTER);
    
            /*
             * lp.x与lp.y表示相对于原始位置的偏移.
             * 当参数值包含Gravity.LEFT时,对话框出现在左边,所以lp.x就表示相对左边的偏移,负值忽略.
             * 当参数值包含Gravity.RIGHT时,对话框出现在右边,所以lp.x就表示相对右边的偏移,负值忽略.
             * 当参数值包含Gravity.TOP时,对话框出现在上边,所以lp.y就表示相对上边的偏移,负值忽略.
             * 当参数值包含Gravity.BOTTOM时,对话框出现在下边,所以lp.y就表示相对下边的偏移,负值忽略.
             * 当参数值包含Gravity.CENTER_HORIZONTAL时
             * ,对话框水平居中,所以lp.x就表示在水平居中的位置移动lp.x像素,正值向右移动,负值向左移动.
             * 当参数值包含Gravity.CENTER_VERTICAL时
             * ,对话框垂直居中,所以lp.y就表示在垂直居中的位置移动lp.y像素,正值向右移动,负值向左移动.
             * gravity的默认值为Gravity.CENTER,即Gravity.CENTER_HORIZONTAL |
             * Gravity.CENTER_VERTICAL.
             */
    
    //        lp.width = 300; // 宽度
            lp.height = dialogHeight; // 高度
            lp.alpha = 0.85f; // 透明度
    
            Log.i(TAG, "setLayout: height= " + lp.height);
            dialogWindow.setAttributes(lp);
        }
    
        //进入新的路径或返回上一层,刷新数据
        private void resetData(String path){
            //若为空文件夹,则不进入
            File file = new File(path);
            if(file.listFiles()==null || file.listFiles().length==0){
                SystemUtil.showToast(context, "空文件夹!");
                return;
            }
            curPath = path;
            tvCurPath.setText(curPath);
            files.clear();
            index.clear();//重置选中
    
            //note: 若直接使用 files = getFiles(curPath);listView 将无变化
            List<File> temp = getFiles(curPath);
            for(File f : temp){
                files.add(f);
            }
            adapter.notifyDataSetChanged();
        }
    
        //根据条件筛选显示文件
        private List<File> getFiles(String path){
            if(flag==FLAG_DIR){
                FileFilter filter = File::isDirectory;
                return Arrays.asList(new File(path).listFiles(filter));
            }else if(flag==FLAG_FILE){//显示所有文件夹及选择的类型的文件
                switch (fileType){
                    case TYPE_ALL:
                        File[] fs = new File(path).listFiles();
                        Arrays.sort(fs, new Comparator<File>() {
                            @Override
                            public int compare(File o1, File o2) {
                                return o1.getName().compareTo(o2.getName());
                            }
                        });
                        return Arrays.asList(fs);
                    case TYPE_AUDIO:
                        return getCertainFile(path, AUDIO_EXTENSION);
                    case TYPE_IMAGE:
                        return getCertainFile(path, IMAGE_EXTENSION);
                    case TYPE_TEXT:
                        return getCertainFile(path, TEXT_EXTENSION);
                    case TYPE_VIDEO:
                        return getCertainFile(path, VIDEO_EXTENSION);
                }
            }
            return new ArrayList<>();
        }
    
        private List<File> getCertainFile(String path, String[] extension){
            List<File> list = new ArrayList<>();
            File[] files = new File(path).listFiles();;
    
            //排序
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });
    
            List extList = Arrays.asList(extension);
            for (File file:files){
                if (file.isDirectory()){
                    list.add(file);
                    continue;
                }
                if (extList.contains(getExtensionName(file.getName()))){
                    list.add(file);
                }
            }
            return list;
        }
    
        private String getExtensionName(String filename) {
            if ((filename != null) && (filename.length() > 0)) {
                int dot = filename.lastIndexOf('.');
                if ((dot > -1) && (dot < (filename.length() - 1))) {
                    return filename.substring(dot + 1).toLowerCase();
                }
            }
            return "";
        }
    
        //清空数据
        private void release() {
            files.clear();
            index.clear();
            adapter = null;
        }
    
        //===============================================外部调用api=====================================
        @Override
        public void show() {
            super.show();
            setData();
        }
    
        @Override
        public void dismiss() {
            super.dismiss();
            release();
        }
    
        //设置根目录
        public void setRootPath(String rootPath) {
            File file = new File(rootPath);
            if(file.exists() && file.isDirectory()){
                this.rootPath = rootPath;
            }
        }
    
        //设置选择文件或文件夹
        public void setFlag(int flag) {
            this.flag = flag;
        }
    
        //设置要筛选文件类型
        public void setFileType(String fileType) {
            this.fileType = fileType;
        }
    
        public void setCallback(SelectFileCallback callback) {
            this.callback = callback;
        }
    
        public void setDialogHeight(int dialogHeight) {
            this.dialogHeight = dialogHeight;
        }
    
        
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event){
            if(keyCode == KeyEvent.KEYCODE_BACK){
                this.cancel();
            }
            return super.onKeyDown(keyCode, event);
        }
    
        //添加或移除选择对象
        @Override
        public void itemClick(View v, boolean isChecked) {
            Integer position = (Integer) v.getTag();
            switch (v.getId()){
                case R.id.cbChoose:
                    if(isChecked){
                        index.add(position);
                        Log.i(TAG, "add: " + position);
                    }else{
                        index.remove(position);
                        Log.i(TAG, "remove: " + position);
                    }
                    break;
            }
        }
    
        private void setListener() {
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Log.d("click", String.valueOf(position+1));
                    //judge file/dir
                    if(files.get(position).isFile()){
                        //do nothing
                        //可增加本地打开查看
                    }else{
                        //点击,进入文件夹
                        resetData(files.get(position).getPath());
                    }
                }
            });
    
            tvConfirm.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(index.size()<1) {
                        //未选择, 退出
                        dismiss();
                        return;
                    }
    
                    List<File> result = new ArrayList<>();
                    switch (flag){
                        case FLAG_DIR://return dirs
                            for(int i=0; i<index.size(); i++){
                                //双重保险
                                if(files.get(index.get(i)).isDirectory()){
                                    result.add(files.get(index.get(i)));
                                }
                            }
                            break;
                        case FLAG_FILE://return files
                            for(int i=0; i<index.size(); i++){
                                if(files.get(index.get(i)).isFile()){
                                    result.add(files.get(index.get(i)));
                                }
                            }
                            break;
                    }
                    //通过回调函数返回结果
                    if(callback!=null){
                        callback.onResult(result);
                    }
                    //退出
                    dismiss();
                }
            });
    
            //返回上一级
            layoutLast.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //judge curPath is root or not
                    if (curPath.equals(rootPath)) {
                        //do nothing
                        SystemUtil.showToast(context, "已是根目录");
                    }else{
                        //back to last layer
                        resetData(curPath.substring(0, curPath.lastIndexOf("/")));
                    }
                }
            });
    
            //返回根目录
            layoutRoot.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(curPath.equals(rootPath)){
                        //do nothing
                        SystemUtil.showToast(context, "已是根目录");
                    }else{
                        //back to root
                        resetData(rootPath);
                    }
                }
            });
        }
    
        //自定义回调接口:
        public interface SelectFileCallback{
            void onResult(List<File> list);
        }
    }
    
  2. ListView_FileSelector_Adapter.java

    public class ListView_FileSelector_Adapter extends BaseAdapter {
        private Context context;
        private LayoutInflater inflater;
        private List<File> data;
        private List<CheckBean> beans = new ArrayList<>();//记录checkbox的选中情况
        private FileInterListener interListener;//供外部引用接口
    
        public ListView_FileSelector_Adapter(Context context, List<File> data, FileInterListener interListener){
            this.context = context;
            inflater = LayoutInflater.from(context);
            this.data = data;
            this.interListener = interListener;
            for (int i = 0; i < data.size(); i++) {
                CheckBean bean = new CheckBean(i, false);
                beans.add(bean);
            }
        }
        @Override
        public int getCount() {
            return data==null ? 0:data.size();
        }
    
        @Override
        public Object getItem(int position) {
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            return 0;
        }
    
        static class ViewHolder {
            public LinearLayout layout;
            public TextView tvData;
            public ImageView imageView;
            public CheckBox checkBox;
        }
    
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
    
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.layout_file_selector_list, null);
                holder = new ViewHolder();
    
                holder.layout = convertView.findViewById(R.id.layout_all);
                holder.tvData = convertView.findViewById(R.id.tvFilePath);
                holder.imageView = convertView.findViewById(R.id.imgFileDir);
                holder.checkBox = convertView.findViewById(R.id.cbChoose);
    
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.layout.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    
            String path = data.get(position).getPath();
            holder.tvData.setText(path.substring(path.lastIndexOf("/")+1));//show the last layer
            if(data.get(position).isDirectory()){
                holder.imageView.setImageResource(R.drawable.icon_dir);
            }else{
                //file
                String tail = path.substring(path.lastIndexOf(".")+1);
                switch(tail.toLowerCase()){
                    case "mp3":
                    case "wav":
                    case "mp4":
                        holder.imageView.setImageResource(R.mipmap.icon_selector_media);
                        break;
                    case "jpg":
                    case "png":
                        Glide.with(context).load(path).into(holder.imageView);
                        break;
                    default:
                        holder.imageView.setImageResource(R.mipmap.icon_selector_file);
                        break;
                }
            }
    
            //make component be able click from outside
            //防止CheckBox因滚动ListView时混乱
            holder.checkBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    beans.get(position).setChecked(!beans.get(position).isChecked());
                    interListener.itemClick(view, beans.get(position).isChecked());
                    holder.checkBox.setChecked(beans.get(position).isChecked());
                }
            });
    
            holder.checkBox.setChecked(beans.get(position).isChecked());
            holder.checkBox.setTag(position);
            return convertView;
        }
    
        public interface FileInterListener {
            void itemClick(View v, boolean isChecked);
        }
    }
    
  3. CheckBean.java

    public class CheckBean {
        private int id;
        private boolean isChecked;
    
        public CheckBean(int id, boolean isChecked) {
            this.id = id;
            this.isChecked = isChecked;
        }
    
        public int getId() {
            return id;
        }
    
        public boolean isChecked() {
            return isChecked;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public void setChecked(boolean checked) {
            isChecked = checked;
        }
    }
    

布局:

  1. layout_file_selector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="@dimen/padding_5"
            android:orientation="horizontal">
    
            <TextView
                android:id="@+id/tvCurPath"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="curPath"
                android:textSize="@dimen/text_size_18"
                android:textColor="@color/black"
                android:singleLine="true"
                android:ellipsize="middle"/>
    
            <TextView
                android:id="@+id/tvConfirmChoose"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="@dimen/padding_5"
                android:text="确定"
                android:textColor="@color/colorRed"
                android:background="@drawable/selector_button_transparent"/>
        </LinearLayout>
    
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/colorGery"/>
    
        <LinearLayout
            android:id="@+id/layoutBack"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
            <LinearLayout
                android:id="@+id/layoutToRoot"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:background="@drawable/selector_button_transparent">
    
                <ImageView
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:padding="@dimen/padding_3"
                    app:srcCompat="@drawable/ic_file_root"
                    tools:ignore="VectorDrawableCompat" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:layout_gravity="center_vertical"
                    android:text="返回根目录"
                    android:textSize="@dimen/text_size_16"
                    android:textColor="@color/colorBlack" />
            </LinearLayout>
    
            <View
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="@color/colorGery"/>
    
            <LinearLayout
                android:id="@+id/layoutToLast"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:background="@drawable/selector_button_transparent">
    
                <ImageView
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:padding="@dimen/padding_3"
                    app:srcCompat="@drawable/ic_file_back"
                    tools:ignore="VectorDrawableCompat" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:layout_gravity="center_vertical"
                    android:text="返回上一层"
                    android:textSize="@dimen/text_size_16"
                    android:textColor="@color/colorBlack" />
            </LinearLayout>
    
            <View
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="@color/colorGery"/>
        </LinearLayout>
    
        <ListView
            android:id="@+id/listViewFile"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
    </LinearLayout>
    
  2. layout_file_selector_list.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="36dp"
        android:padding="@dimen/padding_3"
        android:id="@+id/layout_all"
        android:orientation="horizontal">
    
        <ImageView
            android:id="@+id/imgFileDir"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:padding="@dimen/padding_3" />
    
        <TextView
            android:id="@+id/tvFilePath"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="@dimen/margin_5"
            android:layout_weight="1"
            android:ellipsize="middle"
            android:singleLine="true"
            android:text="***"
            android:textColor="@color/colorBlack"
            android:textSize="@dimen/text_size_16" />
    
        <CheckBox
            android:id="@+id/cbChoose"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:focusable="false" />
    </LinearLayout>
    
  3. style.xml

    <style name="File_Dialog" parent="@android:style/Theme.Dialog">
            <item name="android:windowIsFloating">true</item>
            <item name="android:windowBackground">@android:color/transparent</item>
            <item name="android:windowNoTitle">true</item>
            <item name="android:background">#ffffff</item>
            <item name="android:backgroundDimEnabled">true</item>
        </style>
    
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值