Android常用的工具类汇总(方便日后使用)

平时进行android开发时候,可能需要使用各种各样的工具类,每次总要去上网搜索,费时费力,因此特此将其整理发出来,小伙伴可自行收藏.

1.LogUtils工具类

/**
 * 控制Log开关的工具类
 */
public class LogUtils {
    private LogUtils() {}

    // 如果想屏蔽所有log,可以设置为0
    public static final int LOG_LEVEL = 6;

    public static final int VERBOSE = 5;
    public static final int DEBUG = 4;
    public static final int INFO = 3;
    public static final int WARN = 2;
    public static final int ERROR = 1;

    public static void v(String tag, String msg) {
        if (LOG_LEVEL > VERBOSE) {
            Log.v(tag, msg);
        }
    }

    public static void d(String tag, String msg) {
        if (LOG_LEVEL > DEBUG) {
            Log.d(tag, msg);
        }
    }

    public static void i(String tag, String msg) {
        if (LOG_LEVEL > INFO) {
            Log.i(tag, msg);
        }
    }

    public static void w(String tag, String msg) {
        if (LOG_LEVEL > WARN) {
            Log.i(tag, msg);
        }
    }

    public static void e(String tag, String msg) {
        if (LOG_LEVEL > ERROR) {
            Log.e(tag, msg);
        }
    }

    public static void v(String msg) {
        if (LOG_LEVEL > VERBOSE) {
            Log.v(getCallerName(), msg);
        }
    }

    public static void d(String msg) {
        if (LOG_LEVEL > DEBUG) {
            Log.d(getCallerName(), msg);
        }
    }

    public static void i(String msg) {
        if (LOG_LEVEL > INFO) {
            Log.i(getCallerName(), msg);
        }
    }

    public static void w(String msg) {
        if (LOG_LEVEL > WARN) {
            Log.w(getCallerName(), msg);
        }
    }

    public static void e(String msg) {
        if (LOG_LEVEL > ERROR) {
            Log.e(getCallerName(), msg);
        }
    }

    /**
     * 获取调用者的类名
     */
    public static String getCallerName() {
        StackTraceElement caller = Thread.currentThread().getStackTrace()[4];
        String className = caller.getClassName();// 带有包名信息
        className = className.substring(className.lastIndexOf(".") + 1);
        return className;
    }

    /**
     *  描述:日志内容多的时候(超过4k)需要打印全时.
     */
    public static void showLog(String str) {
        str = str.trim();
        int index = 0;
        int maxLength = 4000;
        String finalString;
        while (index < str.length()) {
            if (str.length() <= index + maxLength) {
                finalString = str.substring(index);
            } else {
                finalString = str.substring(index, maxLength);
            }
            index += maxLength;
            i(getCallerName(), finalString.trim());
        }
    }
}

针对上述代码块中的showLog()方法的代码描述,大家可能不太清楚.按我的理解,特此举例说明下, 相信进行android 开发的小伙伴都用过Android Studio,比如有时候我打印log输入一个2048长度的short[]数组时候,你会发现,数组的后半部分没有输出,这是为什么呢?因为一个short类型占2个字节,所以2048个short型的数据就占用4k(超出了Log所规定的单个打印上限),自动被忽略了,当然这种情况毕竟比较少见,大家了解一下.

2.通用的自定义toast工具类
说它通用是因为满足以下需求:
(1).能自定义toast的弹出样式(包括设置toast弹出时的标题和背景等)
(2).既能在主线程中又能在子线程中使用
直接点击链接查看:Android通用自定义toast工具类(可在主线程和子线程中使用)

3.单位转换的辅助类DensityUtil

/**
 * 常用单位转换的辅助类
 */
public class DensityUtil {

    private DensityUtil() {
        throw new UnsupportedOperationException("cannot be instantiated");
    }

    /**
     * dp转px
     */
    public static int dp2px(Context context, float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, context.getResources().getDisplayMetrics());
    }

    /**
     * sp转px
     */
    public static int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());
    }

    /**
     * px转dp
     */
    public static float px2dp(Context context, float pxVal) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (pxVal / scale);
    }

    /**
     * px转sp
     */
    public static float px2sp(Context context, float pxVal) {
        return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
    }
}

2.史上最全FileUtil工具类

/**
 * 1、文件的新建、删除;
 * 2、文件的复制;
 * 3、获取文件扩展名;
 * 4、文件的重命名;
 * 5、获取某个文件的详细信息;
 * 6、计算某个文件的大小;
 * 7、文件大小的格式化;
 * 8、获取某个目录下的文件列表;
 * 9、目录的新建、删除; 11、目录的复制;
 * 10、计算某个目录包含的文件数量;
 * 11、计算某个目录包含的文件大小;
 */
public class FileUtil {
    private static final String TAG = "FileUtil";
    private static final String[][] MIME_MapTable =
            {
                    // {后缀名, MIME类型}
                    {".3gp", "video/3gpp"}, {".apk", "application/vnd.android.package-archive"},
                    {".asf", "video/x-ms-asf"}, {".avi", "video/x-msvideo"},
                    {".bin", "application/octet-stream"}, {".bmp", "image/bmp"}, {".c", "text/plain"},
                    {".class", "application/octet-stream"}, {".conf", "text/plain"}, {".cpp", "text/plain"},
                    {".doc", "application/msword"},
                    {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
                    {".xls", "application/vnd.ms-excel"},
                    {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
                    {".exe", "application/octet-stream"}, {".gif", "image/gif"},
                    {".gtar", "application/x-gtar"}, {".gz", "application/x-gzip"}, {".h", "text/plain"},
                    {".htm", "text/html"}, {".html", "text/html"}, {".jar", "application/java-archive"},
                    {".java", "text/plain"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"},
                    {".js", "application/x-javascript"}, {".log", "text/plain"}, {".m3u", "audio/x-mpegurl"},
                    {".m4a", "audio/mp4a-latm"}, {".m4b", "audio/mp4a-latm"}, {".m4p", "audio/mp4a-latm"},
                    {".m4u", "video/vnd.mpegurl"}, {".m4v", "video/x-m4v"}, {".mov", "video/quicktime"},
                    {".mp2", "audio/x-mpeg"}, {".mp3", "audio/x-mpeg"}, {".mp4", "video/mp4"},
                    {".mpc", "application/vnd.mpohun.certificate"}, {".mpe", "video/mpeg"},
                    {".mpeg", "video/mpeg"}, {".mpg", "video/mpeg"}, {".mpg4", "video/mp4"},
                    {".mpga", "audio/mpeg"}, {".msg", "application/vnd.ms-outlook"}, {".ogg", "audio/ogg"},
                    {".pdf", "application/pdf"}, {".png", "image/png"},
                    {".pps", "application/vnd.ms-powerpoint"}, {".ppt", "application/vnd.ms-powerpoint"},
                    {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
                    {".prop", "text/plain"}, {".rc", "text/plain"}, {".rmvb", "audio/x-pn-realaudio"},
                    {".rtf", "application/rtf"}, {".sh", "text/plain"}, {".tar", "application/x-tar"},
                    {".tgz", "application/x-compressed"}, {".txt", "text/plain"}, {".wav", "audio/x-wav"},
                    {".wma", "audio/x-ms-wma"}, {".wmv", "audio/x-ms-wmv"},
                    {".wps", "application/vnd.ms-works"}, {".xml", "text/plain"},
                    {".z", "application/x-compress"}, {".zip", "application/x-zip-compressed"}, {"", "*/*"}
            };

    /**
     * 根据文件后缀名获得对应的MIME类型
     *
     * @param filePath 文件路径
     */
    public static String getMIMEType(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            Log.e(TAG, "getMIMEType: 文件不存在");
            return "";
        }

        if (!file.isFile()) {
            Log.e(TAG, "getMIMEType: 当前文件类型是目录");
            return "";
        }
        String type = "*/*";
        String fileName = file.getName();
        int dotIndex = fileName.lastIndexOf("."); // 获取后缀名前的分隔符"."在fileName中的位置
        if (dotIndex < 0) {
            return type;
        }

        String end = fileName.substring(dotIndex, fileName.length()).toLowerCase(Locale.getDefault()); // 获取文件的后缀名
        if (end.length() == 0) {
            return type;
        }

        // 在MIME和文件类型的匹配表中找到对应的MIME类型
        for (String[] aMIME_MapTable : MIME_MapTable) {
            if (end.equals(aMIME_MapTable[0])) {
                type = aMIME_MapTable[1];
            }
        }
        return type;
    }

    /**
     * 获取设备的sd根路径
     */
    public static String getSDPath() {
        File sdDir = null;
        String sdPath;
        boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);//判断sd卡是否存在
        if (sdCardExist) {
            sdDir = Environment.getExternalStorageDirectory();
        }
        assert sdDir != null;
        sdPath = sdDir.toString();
        Log.w(TAG, "getSDPath:" + sdPath);
        return sdPath;
    }

    /**
     * 根据文件名获得文件的扩展名
     *
     * @param fileName 文件名
     * @return 文件扩展名(不带点)
     */
    public static String getFileSuffix(String fileName) {
        Log.w(TAG, "getFileSuffix: fileName::" + fileName);
        int index = fileName.lastIndexOf(".");
        return fileName.substring(index + 1, fileName.length());
    }

    //**************************************************创建文件操作*****************************************************************

    /**
     * 创建文件
     *
     * @param dirPath  文件所在目录的目录名,如/java/test/1.txt,要在当前目录下创建一个文件名为1.txt的文件
     *                 则path为/java/test,fileName为1.txt(也可以封装成直接传递文件的绝对路径)
     * @param fileName 文件名
     * @return 文件新建成功则返回true
     */
    public static boolean createFile(String dirPath, String fileName) {
        String filePath = dirPath + File.separator + fileName;
        Log.w(TAG, "createFile: filePath::" + filePath + "  File.separator ::" + File.separator);
        File file = new File(filePath);
        File fileParent = file.getParentFile();
        if (!fileParent.exists()) {
            fileParent.mkdirs();
            Log.w(TAG, "createFile: 文件所在目录不存在,创建目录成功");
        }

        if (file.exists()) {
            Log.e(TAG, "新建文件失败:file.exist()=" + file.exists());
            return false;
        } else {
            try {
                return file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 新建目录
     *
     * @param dir 目录的绝对路径
     * @return 创建成功则返回true
     */
    public static boolean createFolder(String dir) {
        File file = new File(dir);
        return file.mkdir();
    }

    //*******************************************删除文件操作************************************************************************

    /**
     * 删除单个文件
     *
     * @param filePath 要删除的文件路径
     * @return 文件删除成功则返回true
     */
    public static boolean deleteFile(String filePath) {

        File file = new File(filePath);
        if (file.exists()) {
            boolean isDeleted = file.delete();
            Log.w(TAG, file.getName() + "删除结果:" + isDeleted);
            return isDeleted;
        } else {
            Log.w(TAG, "文件删除失败:文件不存在!");
            return false;
        }
    }

    /**
     * 删除单个文件
     *
     * @param file 要删除的文件对象
     * @return 文件删除成功则返回true
     */
    private static boolean deleteFile(File file) {
        if (file.exists()) {
            boolean isDeleted = file.delete();
            Log.w(TAG, file.getName() + "删除结果:" + isDeleted);
            return isDeleted;
        } else {
            Log.w(TAG, "文件删除失败:文件不存在!");
            return false;
        }
    }

    /**
     * 删除文件夹及其包含的所有文件(会自身循环调用)
     *
     * @param file 要删除的文件对象
     * @return 文件删除成功则返回true
     */
    public static boolean deleteFolder(File file) {
        boolean flag;
        File files[] = file.listFiles();
        if (files != null) // 目录下存在文件列表
        {
            for (File f : files) {
                if (f.isFile()) {
                    // 删除子文件
                    flag = deleteFile(f);
                    if (!flag) {
                        return false;
                    }
                } else {
                    // 删除子目录
                    flag = deleteFolder(f);
                    if (!flag) {
                        return false;
                    }
                }
            }
        }
        //能成功走到这,说明当前目录下的所有子文件和子目录都已经删除完毕
        flag = file.delete();//将此空目录也进行删除
        return flag;
    }

    //*************************************复制/重命名操作********************************************

    /**
     * 复制文件
     *
     * @param srcPath 源文件绝对路径
     * @param destDir 目标文件所在目录
     * @return boolean 复制成功则返回true
     */
    public static boolean copyFile(String srcPath, String destDir) {
        boolean flag = false;
        File srcFile = new File(srcPath); // 源文件
        if (!srcFile.exists()) {
            // 源文件不存在
            Log.w(TAG, "copyFile: 源文件不存在");
            return false;
        }
        // 获取待复制文件的文件名
        String fileName = srcPath.substring(srcPath.lastIndexOf(File.separator));
        String destPath = destDir + fileName;
        if (destPath.equals(srcPath)) {
            // 源文件路径和目标文件路径重复
            Log.w(TAG, "copyFile: 源文件路径和目标文件路径重复");
            return false;
        }

        File destFile = new File(destPath); // 目标文件
        if (destFile.exists() && destFile.isFile()) {
            // 该路径下已经有一个同名文件
            Log.w(TAG, "copyFile: 目标目录下已有同名文件!");
            return false;
        }

        File destFileDir = new File(destDir);
        destFileDir.mkdirs();
        try {
            FileInputStream fis = new FileInputStream(srcPath);
            FileOutputStream fos = new FileOutputStream(destFile);
            byte[] buf = new byte[1024];
            int c;
            while ((c = fis.read(buf)) != -1) {
                fos.write(buf, 0, c);
            }
            fis.close();
            fos.close();
            flag = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (flag) {
            Log.w(TAG, "copyFile: 复制文件成功!");
        }
        return flag;
    }

    /**
     * 复制目录
     *
     * @param srcDir  源文件夹目录
     * @param destDir 目标文件夹所在目录
     * @return 复制成功则返回true
     */
    public static boolean copyFolder(String srcDir, String destDir) {
        Log.w(TAG, "copyFolder: 复制文件夹开始!");
        boolean flag = false;

        File srcFile = new File(srcDir);
        if (!srcFile.exists()) {
            // 源文件夹不存在
            Log.w(TAG, "copyFolder: 源文件夹不存在");
            return false;
        }
        String dirName = getDirName(srcDir); // 获得待复制的文件夹的名字,比如待复制的文件夹为"E://dir"则获取的名字为"dir"

        String destPath = destDir ; // 如果想连同源目录名拷贝则String destPath = destDir + File.separator + dirName
        // Util.toast("目标文件夹的完整路径为:" + destPath);
        if (destPath.equals(srcDir)) {
            Log.w(TAG, "copyFolder: 目标文件夹与源文件夹重复");
            return false;
        }
        File destDirFile = new File(destPath);
        if (!destDirFile.exists()) {
            destDirFile.mkdirs(); // 如果指定目录不存在则生成目录
        }

        File[] files = srcFile.listFiles(); // 获取源文件夹下的子文件和子文件夹
        Log.i(TAG, "copyFolder: -----files,length::"+files.length+"      files::"+files);
        if (files.length == 0) {
            // 如果源文件夹为空目录则直接设置flag为true,这一步非常隐蔽,debug了很久
            flag = true;
        } else {
            for (File temp : files) {
                if (temp.isFile()) {
                    // 文件
                    flag = copyFile(temp.getAbsolutePath(), destPath);
                } else if (temp.isDirectory()) {
                    // 文件夹
                    flag = copyFolder(temp.getAbsolutePath(), destPath);
                }
                if (!flag) {
                    break;
                }
            }
        }
        if (flag) {
            Log.w(TAG, "copyFolder: 复制文件夹成功!");
        }
        return flag;
    }

    /**
     * 重命名文件
     *
     * @param oldPath 旧文件的绝对路径
     * @param newPath 新文件的绝对路径
     * @return 文件重命名成功则返回true
     */
    public static boolean renameTo(String oldPath, String newPath) {
        if (oldPath.equals(newPath)) {
            Log.w(TAG, "文件重命名失败:新旧文件名绝对路径相同!");
            return false;
        }
        File oldFile = new File(oldPath);
        File newFile = new File(newPath);

        boolean isSuccess = oldFile.renameTo(newFile);
        Log.w(TAG, "文件重命名是否成功:" + isSuccess);
        return isSuccess;
    }

    /**
     * 重命名文件
     *
     * @param oldFile 旧文件对象,File类型
     * @param newName 新文件的文件名,String类型
     * @return 重命名成功则返回true
     */
    public static boolean renameTo(File oldFile, String newName) {
        File newFile = new File(oldFile.getParentFile() + File.separator + newName);
        boolean flag = oldFile.renameTo(newFile);
        Log.w(TAG, "文件重命名是否成功:" + flag);
        return flag;
    }

    //*********************************计划文件大小/格式化**************************************************************


    /**
     * 计算某个文件的大小
     *
     * @param path 文件的绝对路径
     * @return
     */
    public static long getFileSize(String path) {
        File file = new File(path);
        if (file.exists()&&file.isFile()) {
            return file.length();
        }else{
            Log.e(TAG, "getFileSize: error!" );
            return -1;
        }
    }

    /**
     * 计算某个文件的大小
     *
     * @param file 文件对象
     * @return 文件大小,如果file不是文件,则返回-1
     */
    private static long getFileSize(File file) {
        if (file.isFile()) {
            return file.length();
        } else {
            return -1;
        }
    }


    /**
     * 计算某个目录的大小
     *
     * @param directory 目录生成的file对象
     * @return
     */
    public static long getFolderSize(File directory) {
        File[] files = directory.listFiles();
        if (files != null) {
            long size = 0;
            for (File f : files) {
                if (f.isFile()) {
                    // 获得子文件的大小
                    size = size + getFileSize(f);
                } else {
                    // 获得子目录的大小
                    size = size + getFolderSize(f);
                }
            }
            return size;
        }
        return -1;
    }

    /**
     * 文件大小的格式化
     *
     * @param size 文件大小,单位为byte
     * @return 文件大小格式化后的文本
     */
    public static String formatSize(long size) {
        DecimalFormat df = new DecimalFormat("####.00");
        if (size < 1024) // 小于1KB
        {
            return size + "Byte";
        } else if (size < 1024 * 1024) // 小于1MB
        {
            float kSize = size / 1024f;
            return df.format(kSize) + "KB";
        } else if (size < 1024 * 1024 * 1024) // 小于1GB
        {
            float mSize = size / 1024f / 1024f;
            return df.format(mSize) + "MB";
        } else if (size < 1024L * 1024L * 1024L * 1024L) // 小于1TB
        {
            float gSize = size / 1024f / 1024f / 1024f;
            return df.format(gSize) + "GB";
        } else {
            return "size: error";
        }
    }

    /**
     * 格式化文件最后修改时间字符串(针对以时间戳结尾的文件)
     *
     * @param time
     * @return
     */
    public static String formatTime(long time) {
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd,HH:mm:ss", Locale.getDefault());
        String formatedTime = sdf.format(date);
        return formatedTime;
    }

    //*********************************************获取文件列表*******************************************
    /**
     * 获取某个目录下的文件列表(如果存在子目录,则子目录中的文件是否计入)
     *
     * @param dir 文件目录
     * @return 文件列表File[] files
     */
    public static File[] getFileList(String dir) {
        File file = new File(dir);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                return files;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }


    //********************************************文件操作中封装的一些小方法*********************************************
    /**
     * 获取待复制文件夹的文件夹名
     *
     * @param dir 待复制的目录名称
     * @return String
     */
    private static String getDirName(String dir) {
        if (dir.endsWith(File.separator)) {
            // 如果文件夹路径以"//"结尾,则先去除末尾的"//"
            dir = dir.substring(0, dir.lastIndexOf(File.separator));
        }
        return dir.substring(dir.lastIndexOf(File.separator) + 1);
    }

    /**
     * 计算某个目录包含的文件数量
     *
     * @param directory
     * @return
     */
    public static int getFileCount(File directory) {
        File[] files = directory.listFiles();
        int count = files.length;
        return count;
    }

}

关于FileUtil工具类我多说点吧。
上述贴出的FileUtil工具类,所有的方法都经过了测试,有些地方还做了优化,鉴于代码不多,我下面将测试代码和资源文件贴出来:

@RuntimePermissions
public class SecondActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "SecondActivity";

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

        findViewById(R.id.get_sd_path).setOnClickListener(this);
        findViewById(R.id.get_file_suffix).setOnClickListener(this);
        findViewById(R.id.get_file_mime).setOnClickListener(this);
        findViewById(R.id.create_file).setOnClickListener(this);
        findViewById(R.id.create_dir).setOnClickListener(this);
        findViewById(R.id.delete_specific_file).setOnClickListener(this);
        findViewById(R.id.delete_all_for_dir).setOnClickListener(this);
        findViewById(R.id.copy_file).setOnClickListener(this);
        findViewById(R.id.copy_dir).setOnClickListener(this);
        findViewById(R.id.rename_file).setOnClickListener(this);
        findViewById(R.id.format_file_size).setOnClickListener(this);
        findViewById(R.id.format_dir_size).setOnClickListener(this);
        findViewById(R.id.get_file_list).setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.get_sd_path:
                //eg."getSdPath:/storage/emulated/0"
                //获取sd路径
                SecondActivityPermissionsDispatcher.getSdPathWithCheck(SecondActivity.this);
                break;
            case R.id.get_file_suffix:
                //读取文件后缀
                String fileName = "log.txt";
                String fileSuffix = FileUtil.getFileSuffix(fileName);
                Log.i(TAG, "onClick: fileSuffix::" + fileSuffix);
                break;
            case R.id.get_file_mime:
                //获取文件MIME类型
                String filePath1 = FileUtil.getSDPath() + "/00/" + "1.doc";
                String mimeType = FileUtil.getMIMEType(filePath1);
                Log.i(TAG, "onClick: mimeType::" + mimeType);
                break;
            case R.id.create_file:
                //创建文件
                String dirPath2 = FileUtil.getSDPath() + "/00";
                String fileName2 = "2.doc";
                boolean isCreated = FileUtil.createFile(dirPath2, fileName2);
                Log.i(TAG, "onClick: file isCreated::" + isCreated);
                break;
            case R.id.create_dir:
                //创建目录
                String dirPath3 = FileUtil.getSDPath()+"/00";
                boolean isCreated3 = FileUtil.createFolder(dirPath3);
                Log.i(TAG, "onClick: dir isCreated::" + isCreated3);
                break;
            case R.id.delete_specific_file:
                //删除指定的某个文件
                String filePath4 = FileUtil.getSDPath() + "/00/4.doc";
                boolean isDeleted = FileUtil.deleteFile(filePath4);
                Log.i(TAG, "onClick: file isDeleted::" + isDeleted);
                break;
            case R.id.delete_all_for_dir:
                //删除某一目录下的所有文件
                String dirPath5 = FileUtil.getSDPath() + "/00";
                boolean isDeleted5 = FileUtil.deleteFolder(new File(dirPath5));
                Log.i(TAG, "onClick: all sub file isDeleted(include current dir)::" + isDeleted5);
                break;
            case R.id.copy_file:
                //复制文件
                String filePath6 = FileUtil.getSDPath() + "/00/6.wav";
                String dirPath6 = FileUtil.getSDPath() + "/11";
                boolean isCopyed = FileUtil.copyFile(filePath6, dirPath6);
                Log.i(TAG, "onClick: file isCopyed::" + isCopyed);
                break;
            case R.id.copy_dir://v
                //复制目录
                String srcDir7 = FileUtil.getSDPath() + "/00";
                String destDir7 = FileUtil.getSDPath() + "/11";
                boolean isCopyed7 = FileUtil.copyFolder(srcDir7, destDir7);
                Log.i(TAG, "onClick: file isCopyed::" + isCopyed7);
                break;
            case R.id.rename_file:
                //文件重命名
                String srcPath8 = FileUtil.getSDPath() + "/00/8.wav";
                String destPath8 = FileUtil.getSDPath() + "/00/8_1.wav";
                boolean b = FileUtil.renameTo(srcPath8, destPath8);

                //filename="7_1.wav";
                //boolean b = FileUtil.renameTo(new File(srcPath8), destPath8);
                Log.i(TAG, "onClick: file rename::" + b);
                break;
            case R.id.format_file_size://593.71kb---->579.79
                //格式化文件大小
                String filePath9 = FileUtil.getSDPath() + "/00/9.wav";
                long fileSize = FileUtil.getFileSize(filePath9);
                Log.i(TAG, "onClick: get fileSize::" + fileSize);
                if (fileSize != -1) {
                    String formatSize = FileUtil.formatSize(fileSize);
                    Log.i(TAG, "onClick:formatSize file::" + formatSize);
                }
                break;
            case R.id.format_dir_size://4.95--->4.72M
                //格式化目录大小
                String dir10 = FileUtil.getSDPath() + "/11";
                long folderSize = FileUtil.getFolderSize(new File(dir10));
                if (folderSize != -1) {
                    String formatSize10 = FileUtil.formatSize(folderSize);
                    Log.i(TAG, "onClick: formatSize dir::" + formatSize10);
                }
                break;
            case R.id.get_file_list:
                //获取当前目录的文件列表
                String dir11 = FileUtil.getSDPath() + "/11";
                File[] fileList = FileUtil.getFileList(dir11);
                Log.i(TAG, "onClick: get file list size::" + fileList.length + "  fileList::" + fileList);
                break;
        }
    }


    //*****************************封装的文件操作方法***********************************************
    @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    void getSdPath() {
        String sdPath = FileUtil.getSDPath();
        Log.d(TAG, "getSdPath:"+sdPath);
    }

    //****************************************************************************

    //首次权限弹出是系统权限。当点击拒绝之后才会走此
    // 向用户说明为什么需要这些权限(可选)
    @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    void showRationaleForCamera(final PermissionRequest request) {
        new AlertDialog.Builder(this)
                .setMessage("文件操作需要读写权限,请求授予")
                .setPositiveButton("下一步", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int button) {
                        request.proceed();//继续请求权限
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int button) {
                        request.cancel();
                    }
                })
                .show();
    }

    // 用户拒绝授权回调(可选)//当点击系统权限的拒绝时候,不会触发
    @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    void showDeniedForCamera() {
        Toast.makeText(this, "读写权限被拒绝。", Toast.LENGTH_SHORT).show();
    }

    // 用户勾选了“不再提醒”时调用(可选)
    @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    void showNeverAskForCamera() {
        openAppDetails();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 代理权限处理到自动生成的方法
        //必须手动添加,不然下一步不走。。
        SecondActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }

    /**
     * 打开 APP 的详情设置
     */
    private void openAppDetails() {
        Toast.makeText(this, "勾选了不在拒绝。", Toast.LENGTH_SHORT).show();
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("操作文件读写权限,请到'设置'中授予权限.");
        builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.addCategory(Intent.CATEGORY_DEFAULT);
                intent.setData(Uri.parse("package:" + getPackageName()));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                startActivity(intent);
            }
        });
        builder.setNegativeButton("取消", null);
        builder.show();
    }

}

布局文件second_activity.xml:

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

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


        <Button
            android:id="@+id/get_sd_path"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="获取sd卡根路径" />


        <Button
            android:id="@+id/get_file_suffix"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="获取文件后缀名" />

        <Button
            android:id="@+id/get_file_mime"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="获取文件MIME类型" />


        <Button
            android:id="@+id/create_file"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="创建文件" />

        <Button
            android:id="@+id/create_dir"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="创建目录" />


        <Button
            android:id="@+id/delete_specific_file"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="删除指定文件(单个)" />


        <Button
            android:id="@+id/delete_all_for_dir"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="删除某一目录下的所有文件(包括此目录)" />


        <Button
            android:id="@+id/copy_file"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="复制文件" />

        <Button
            android:id="@+id/copy_dir"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="复制目录" />

        <Button
            android:id="@+id/rename_file"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="文件重命名" />


        <Button
            android:id="@+id/format_file_size"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="计算某个文件大小,并格式化" />

        <Button
            android:id="@+id/format_dir_size"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="计算某个目录大小,并格式化" />


        <Button
            android:id="@+id/get_file_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="获取某个路径下的文件列表" />
    </LinearLayout>
</ScrollView>

运行效果如下:

效果图如下

其中SecondActivity中设计android 6.0运行时权限的申请,使用教程可参考:
PermissionsDispatcher,Android 6.0 运行时权限
重点说明,避免踩坑:
(1).@NeedsPermission修饰的方法只能被void修饰
(2).android6.0动态权限申请不意味着只在代码中申请,清单文件也必须写。
(3).@NeedsPermission可以使用多次,即需要权限申请的都可以使用,不受次数限制
(4).FileUtils中的计算文件大小在小米5手机测试时候存在一定的偏差,比如某个文大小工具类中计算出来的是579.79KB,而通过小米手机的文件管理器查出来的是593.71KB. 这主要是进制的原因,MIUI目前使用的是1000进制,其他的app可能采用的是1024进制,因为存储芯片的生产厂商都是按照1000进制计算的这个问题的原因参见

扫描二维码关注公众号,回复: 1123316 查看本文章

猜你喜欢

转载自blog.csdn.net/zhangqunshuai/article/details/80420708
今日推荐