Android 简易文件管理模块

最近在做一个将word文档导入到SQLite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件

先看下效果图:
这里写图片描述

思路:

获取存储器接口
遍历当前目录
利用ListView显示文件文件夹

先是布局

<?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"
    android:orientation="vertical">

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:id="@+id/lyPath">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:textAppearance"
                android:text="@string/txt_path_now"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:textAppearance"
                android:text="mnt/sdcard"
                android:id="@+id/txtPath"/>

        </LinearLayout>
    </HorizontalScrollView>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@android:color/darker_gray"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/folderList"/>

    </LinearLayout>
</LinearLayout>

用于加载文件的Item布局
list_file_style.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:scaleY="0.9">

        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable = "false"
            android:focusableInTouchMode="false"
            android:id="@+id/cbSelect"/>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/img"
            android:src="@mipmap/other"
            tools:ignore="ContentDescription" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:textAppearance"
                android:id="@+id/name"
                android:text="setting"/>

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

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/time"
                    android:text="2017-3-30 21:40:02"/>

                <View
                    android:layout_width="20dp"
                    android:layout_height="match_parent"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/size"
                    android:text="1.2Mb"/>

            </LinearLayout>

        </LinearLayout>

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@android:color/darker_gray"/>

</LinearLayout>

自定义类

为了更好的将数据绑定到ListView上我选择自定义BaseAdapter类

package czhy.grey.sun.exam.bin.adapter_;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;

import java.io.File;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;

import czhy.grey.sun.exam.R;
import czhy.grey.sun.exam.bin.holder_.FileHolder;

public class FileAdapter extends BaseAdapter {
    private ArrayList<File> list;
    private LayoutInflater inflater;
    private boolean isRoot;
    // 用来控制CheckBox的选中状况
    private HashMap<Integer, Boolean> isSelected;
    private int selectNum;

    public FileAdapter(Context context, ArrayList<File> list,boolean isRoot) {
        this.list = list;
        this.isRoot = isRoot;
        inflater = LayoutInflater.from(context);
        isSelected = new HashMap<>();
        // 初始化数据
        initDate();
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public File getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        FileHolder holder;
        File file = getItem(position);

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.list_file_style, parent, false);
            holder = new FileHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (FileHolder) convertView.getTag();
        }
        // TODO: 2017/4/1 根目录UI优化
        if (!isRoot && position == 0) {
            holder.setName("返回上一层", file.isDirectory(),isSelectedFor(position));
            holder.setId(position,isSelectedFor(position),new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (int)  v.getTag();
                    boolean b = !isSelected.get(position);

                    isSelected.put(position, b);
                    ((CheckBox) v).setChecked(b);
                    //全选或全取消操作
                    for(int i=0;i< getCount();i++){
                        setChecked(i,b);
                    }
                    selectNum = b?getCount():0;

                    notifyDataSetChanged();
                }
            });
            holder.setTime("全选");
            holder.setSize("已选择"+selectNum+"项");
        } else {
            holder.setName(file.getName(), file.isDirectory(),isSelectedFor(position));
            holder.setId(position,isSelectedFor(position),new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (int)  v.getTag();
                    boolean b = !isSelectedFor(position);
                    isSelected.put(position, b);
                    ((CheckBox) v).setChecked(b);
                    //是否已经全选
                    if(isSelectedAll()) {
                        isSelected.put(0, true);
                        selectNum = getCount();
                    }else {
                        isSelected.put(0, false);
                        selectNum = b?selectNum+1:selectNum-1;
                    }

                    notifyDataSetChanged();
                }
            });
            holder.setTime(new SimpleDateFormat("yyyy/mm/hh/dd hh:mm:ss").format(file.lastModified()));

            if (file.isFile())
                holder.setSize(fileLength(file.length()));
            else {
                holder.setSize("");
            }
        }

        return convertView;
    }

    public boolean isSelectedFor(int id) {
        return isSelected.get(id);
    }

    public int getSelectNum() {
        return selectNum;
    }

    //私有函数
    /** 初始化isSelected的数据
     */
    private void initDate() {
        selectNum = 0;
        for (int i = 0; i < list.size(); i++) {
            isSelected.put(i, false);
        }
    }

    private void setChecked(int id,boolean isChecked) {
        isSelected.put(id,isChecked);
    }

    private String fileLength(long length) {
        String size;

        if (length > 1024 * 1024)
            size = new DecimalFormat("#.00").format(length / (1024.0 * 1024.0)) + "MB";
        else if (length > 1024)
            size = new DecimalFormat("#.00").format(length / 1024.0) + "KB";
        else
            size = length + "B";

        return size;
    }

    private boolean isSelectedAll(){
        for(int i=1;i< getCount();i++){
            if(!isSelectedFor(i))
                return false;
        }

        return true;
    }

}

以及用于布局导入的Holder

package czhy.grey.sun.exam.bin.holder_;


import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;

import czhy.grey.sun.exam.R;

public class FileHolder{

    private CheckBox cbSelect;
    private TextView name;
    private TextView time;
    private TextView size;
    private ImageView img;

    public FileHolder(View convertView) {
        cbSelect = (CheckBox)convertView.findViewById(R.id.cbSelect);
        name = (TextView)convertView.findViewById(R.id.name);
        time = (TextView)convertView.findViewById(R.id.time);
        img = (ImageView)convertView.findViewById(R.id.img);
        size = (TextView)convertView.findViewById(R.id.size);
    }

    public void setTime(String time) {
        this.time.setText(time);
    }

    public void setId(int id,boolean isSelected, View.OnClickListener listener) {
        cbSelect.setTag(id);
        cbSelect.setChecked(isSelected);
        cbSelect.setOnClickListener(listener);
    }

    public void setName(String name,boolean isDirectory,boolean isChecked) {
        this.name.setText(name);
        cbSelect.setChecked(isChecked);
        if (isDirectory) {
            if(name.equalsIgnoreCase("返回上一层")){
                img.setImageResource(R.mipmap.back);
            }else
                img.setImageResource(R.mipmap.folder);
        }else {
            String type = name.substring(name.lastIndexOf(".")+1,name.length());

            if ((type.equalsIgnoreCase("doc") || type.equalsIgnoreCase("docx") || type.equalsIgnoreCase("txt"))) {
                img.setImageResource(R.mipmap.doc_text);
            } else {
                img.setImageResource(R.mipmap.other);
            }
        }
    }

    public void setSize(String size) {
        this.size.setText(size);
    }
}

控制文件

全局变量

    private ListView folderList;
    private TextView txtPath;
    private FileAdapter fileAdapter;
    private ArrayList<File> rootFileList;
    private int folder_level;

首先是获取存储器列表


    private void getRootFile(){
        rootFileList = new ArrayList<>();
        StorageManager storageManager = (StorageManager) this.getSystemService(STORAGE_SERVICE);
        try {
            //内部存储器
            File inside = null;
            //可移除存储器集合
            ArrayList<File> outside = new ArrayList<>();

            //获取存储器接口 API-24以下不支持StorageVolume接口
            //API-24开始可直接 List<StorageVolume> svList = storageManager.getStorageVolumes();
            Method getVolumeList = StorageManager.class.getMethod("getVolumeList");
            getVolumeList.setAccessible(true);
            //获取存储器列表
            Object[] invokes = (Object[]) getVolumeList.invoke(storageManager);
            if (invokes != null) {
                for (Object obj:invokes) {
                    //获取存储器地址接口
                    Method getPath = obj.getClass().getMethod("getPath");
                    //获取存储器地址
                    String path = (String) getPath.invoke(obj);
                    File file = new File(path);
                    if (file.canWrite()) {
                        //获取存储器是否可移除接口
                        Method isRemovable = obj.getClass().getMethod("isRemovable");
                        //存储器是否可移除
                        if((isRemovable.invoke(obj)).equals(true)){
                            outside.add(file);
                        }else {
                            inside =  file;
                        }
                    }
                }
                //按0-内部存储器 >0外部存储器 顺序 添加到根目录列表
                rootFileList.add(inside);
                rootFileList.addAll(outside);
            }
        } catch (NoSuchMethodException |
                IllegalArgumentException |
                IllegalAccessException |
                InvocationTargetException e1) {
            e1.printStackTrace();
        }
    }

当我们点击一个文件夹时,打开文件夹以及更新UI

    //获取目录数据
    private void refurbish(File folder,boolean isBack) {
        if (isBack)
            folder_level--;
        else
            folder_level++;

        txtPath.setText(folder.getPath());
        ArrayList<File> files = new ArrayList<>();

        File[] folderFile = folder.listFiles();
        if (null != folderFile && folderFile.length > 0) {
            Collections.addAll(files, folderFile);
        }

        //排序 文件夹在前,然后按文件名排序
        Collections.sort(files, new Comparator<File>() {
            @Override
            public int compare(File f1, File f2) {
                if (f1.isDirectory()) {
                    if (f2.isDirectory()) {
                        return f1.getName().compareToIgnoreCase(f2.getName());
                    } else {
                        return -1;
                    }
                } else if (f2.isDirectory()) {
                    return 1;
                } else {
                    return f1.getName().compareToIgnoreCase(f2.getName());
                }
            }
        });

        //新建集合用做打开文件夹
        ArrayList<File> openedFolder = new ArrayList<>();
        //将上层目录保存到第一项
        openedFolder.add(folder.getParentFile());
        //将排序完毕的内容添加到目标集合 目的:解决第一个文件夹不是上一层地址问题
        openedFolder.addAll(files);
        fileAdapter = new FileAdapter(this, openedFolder, false);
        folderList.setAdapter(fileAdapter);
    }

程序刚运行时需要加载存储器列表,而不是某一文件夹,所有需要额外定义一个函数

    //获取根目录数据
    private void rootFile() {
        txtPath.setText("/");
        fileAdapter = new FileAdapter(this, rootFileList,true);
        folderList.setAdapter(fileAdapter);
        folder_level= 0;
    }

有了以上这些,在控制文件创建是直接调用相应的函数即可

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_import);

        txtPath = (TextView) findViewById(R.id.txtPath);
        folderList = (ListView) findViewById(R.id.folderList);
        folderList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                File file = fileAdapter.getItem(position);
                //因为为的程序中不需要对文件进行其他操作,所有不做处理
                //有需求的在这里添加else即可
                if (file.isDirectory()) {
                    if(folder_level == 0)//判断是否在根目录
                        refurbish(file,false);
                    else if (folder_level == 1 && position == 0) //判断当前位置是否在一级目录 并且 是否点击第一项
                        rootFile();
                    else
                        refurbish(file,position == 0);
                }
            }
        });
        getRootFile();
        rootFile();
        setSubject();
        importManger = new ImportManger();
    }

弄了这么多基本算是完成了,为了用户友好,我对返回键进行了重载

    /**
     * 监听物理按键
     * 需要注意的是这个函数是有返回值的
     * 返回值可以理解为
     * true表示做了处理,就不提交给处理系统的back按键事件
     * false则是提交给系统处理
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)) {

            if (folder_level != 0) {
                    if (folder_level != 1)
                        refurbish(fileAdapter.getItem(0),true);
                    else
                        rootFile();

                    return true;          
            } else {
                finish();
            }
        }

        return super.onKeyDown(keyCode, event);
    }

参考文献:
http://www.cnblogs.com/shang53880/archive/2012/07/03/2574806.html
http://blog.sina.com.cn/s/blog_5da93c8f0102vcam.html
http://vjson.com/wordpress/%E8%8E%B7%E5%8F%96android%E8%AE%BE%E5%A4%87%E6%8C%82%E8%BD%BD%E7%9A%84%E6%89%80%E6%9C%89%E5%AD%98%E5%82%A8%E5%99%A8.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Grey Sun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值