花了一天的时间撸完了Android本地文件管理器~!!GitHub下载地址为:PumpkinFileManager
整体思路:
大概的思路是首先遍历本地所有文件的根目录,然后通过使用栈结构存储文件路径,因为出栈入栈的特性很适合处理随着用户操作不断推出存入的文件路径,之后增删改查的具体实现在下文都有详细解释以及缩略图,总共花费大概10个小时(折腾了好久Github..sad),也是完成了周末の任务2333
具体实现:
1: 实现了在ListView中浏览本地所有文件.
实现原理:
使用了栈结构保存当前的文件路径,每一次点击文件夹,就会把当前文件名推入栈组成新的文件路径。
实现得到当前栈路径的方法:
//得到当前栈路径的String private String getPathString() { Stack<String> temp = new Stack<>(); temp.addAll(nowPathStack); String result = ""; while (temp.size() != 0) { result = temp.pop() + result; } return result; }在item的onItemClick方法中将点击的文件名推入栈:
//如果是文件夹 // 清除列表数据 // 获得目录中的内容,计入列表中 // 适配器通知数据集改变 nowPathStack.push("/" + file.getName()); showChangge(getPathString());
//显示改变data之后的文件数据列表 private void showChangge(String path) { showtv.setText(path); files = new File(path).listFiles(); data.clear(); for (File f : files) { data.add(f); } files = fileAdapter.setfiledata(data); }
2: 实现了对文件的增(新建文件夹)
实现原理:
根据当前路径,以及通过dialog得到的用户输入的文件名执行新建文件夹的操作:
/** * 创建新文件夹 */ private void doCreateNewFolder() { mydialog = new AlertDialog.Builder(MainActivity.this).create(); mydialog.show(); mydialog.getWindow().setContentView(R.layout.newfloder_dialog); mydialog.setView(new EditText(MainActivity.this)); //加入下面两句以后即可弹出输入法 mydialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); mydialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); newfloder_name = (EditText) mydialog.getWindow().findViewById(R.id.newfloder_name); mydialog.getWindow() .findViewById(R.id.newfloder_cancle) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mydialog.dismiss(); } }); mydialog.getWindow() .findViewById(R.id.newfloder_create) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String name = newfloder_name.getText().toString(); if (name != null) { File folder = new File(getPathString() + "/" + name); folder.mkdirs(); if (folder.exists()) { Toast.makeText(MainActivity.this,"文件:"+name + " 创建成功",Toast.LENGTH_SHORT).show(); showChangge(getPathString()); mydialog.dismiss(); } } } }); }
3: 删(删除文件或文件夹).
在adapter中直接执行删除操作:
/** * 删除 */ private void doRemove() { final File file = filedata.get(position); judgeAlertDialog(context, "提醒", "你确认删除" + file.getName() + "吗(不可逆)?", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { deleteDir(file); filedata.remove(file); notifyDataSetChanged(); showToast(file.getName() + " 删除成功"); } }, null); }
4: 改(重命名以及复制粘贴文件).
实现原理:
1、重命名
在adapter中直接执行重命名操作:
/** * 重命名 */ private void doRename() { showToast("重命名" + position); RenameFileDialog dialog = new RenameFileDialog(context, filedata, position); dialog.setOnFileRenameListener(new RenameFileDialog.OnFileRenameListener() { @Override public void onFileRenamed(boolean success) { String message = null; if (filedata.get(position).isFile()) { message = "文件"; } else { message = "文件夹"; } if (success) { message += "重命名成功"; } else { message += "重命名失败"; } showToast(message); } }); dialog.show(); setfiledata(filedata); }2、复制文件:
如下图所示:
使用了简单的回调执行复制操作,在FileAdapter定义复制接口,然后在MainActivity中实现接口,并通过setListener将自身实例传入FileAdapter,从而实现在FileAdapter中执行复制操作。
MainActivity中的粘贴操作实现如下:
/** * 复制或粘贴 */ private void doPaste() { File newFile = new File(getPathString()+"/"+watingCopyFile.getName()); if (watingCopyFile.equals(null)) { Snackbar.make(findViewById(R.id.main_view), "当前粘贴板为空,不能粘贴", Snackbar.LENGTH_SHORT).show(); } else { if (watingCopyFile.isFile()&&watingCopyFile.exists()){ try { FileInputStream fis = new FileInputStream(watingCopyFile); FileOutputStream fos = new FileOutputStream(newFile); int len = -1; long contentSize = watingCopyFile.length(); long readed = 0; byte[] buff = new byte[8192]; while ((len=fis.read(buff))!=-1){ //写文件 fos.write(buff,0,len); readed+=len; //发布进度 } fos.flush(); fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } finally { } } if (newFile.exists()) { Toast.makeText(MainActivity.this,"复制" + newFile.getName() + "成功",Toast.LENGTH_SHORT).show(); fileAdapter.notifyDataSetChanged(); } } }
5: 查(对当前路径下的递归查询).
实现原理:
data = new ArrayList<>(); searchfilemap = new HashMap<>(); searchByPath(path); if (searchfilemap.size() > 0) { //取出map中数据,赋值给data Object[] list = searchfilemap.entrySet().toArray(); for (int i = 0; i < searchfilemap.size(); i++) { data.add(new File(list[i].toString())); } }通过递归查找本地所有文件,寻找匹配输入字符query的文件名的文件,并全部显示在listview上。
递归方法:
/** * 通过路径递归查找文件 * @param path */ private void searchByPath(String path) { File[] files = new File(path).listFiles(); filenum += files.length; publishProgress(filenum); for (int i = 0; i < files.length; i++) { File f = files[i]; if (f.isDirectory()) { searchByPath(path + "/" + f.getName()); } else { if (f.getName().contains(query)) { searchfilemap.put(files[i], files[i].getName()); } } } }
6:排(对显示在listView中的文件按时间,大小或文件名排序)
实现方法使用杨羿的方式,通过对文件种类进行分类比较实现排序:
首先需要一个格式工厂类:
public class FileSortFactory { public static final int SORT_BY_FOLDER_AND_NAME = 1; public static final int SORT_BY_FOLDER_REVERSE_AND_NAME = 2; public static final int SORT_BY_FOLDER_AND_NAME_REVERSE = 3; public static final int SORT_BY_FOLDER_REVERSE_AND_NAME_REVERSE = 4; public static final int SORT_BY_FOLDER_AND_SIZE = 5; public static final int SORT_BY_FOLDER_REVERSE_AND_SIZE = 6; public static final int SORT_BY_FOLDER_AND_SIZE_REVERSE = 7; public static final int SORT_BY_FOLDER_REVERSE_AND_SIZE_REVERSE = 8; public static final int SORT_BY_FOLDER_AND_TIME = 9; public static final int SORT_BY_FOLDER_REVERSE_AND_TIME = 10; public static final int SORT_BY_FOLDER_AND_TIME_REVERSE = 11; public static final int SORT_BY_FOLDER_REVERSE_AND_TIME_REVERSE = 12; public static Comparator<File> getWebFileQueryMethod( int method) { switch (method) { case SORT_BY_FOLDER_AND_NAME: return new SortByFolderAndName(true, true); case SORT_BY_FOLDER_REVERSE_AND_NAME: return new SortByFolderAndName(false, true); case SORT_BY_FOLDER_AND_NAME_REVERSE: return new SortByFolderAndName(true, false); case SORT_BY_FOLDER_REVERSE_AND_NAME_REVERSE: return new SortByFolderAndName(false, false); case SORT_BY_FOLDER_AND_SIZE: return new SortByFolderAndSize(true, true); case SORT_BY_FOLDER_REVERSE_AND_SIZE: return new SortByFolderAndSize(false, true); case SORT_BY_FOLDER_AND_SIZE_REVERSE: return new SortByFolderAndSize(true, false); case SORT_BY_FOLDER_REVERSE_AND_SIZE_REVERSE: return new SortByFolderAndSize(false, false); case SORT_BY_FOLDER_AND_TIME: return new SortByFolderAndTime(true, true); case SORT_BY_FOLDER_REVERSE_AND_TIME: return new SortByFolderAndTime(false, true); case SORT_BY_FOLDER_AND_TIME_REVERSE: return new SortByFolderAndTime(true, false); case SORT_BY_FOLDER_REVERSE_AND_TIME_REVERSE: return new SortByFolderAndTime(false, false); default: break; } return null; } }之后再对应的具体排序方法类(这里只列出一种):
public class SortByFolderAndName implements Comparator<File> { boolean first; boolean second; public SortByFolderAndName(boolean first,boolean second) { this.first = first; this.second = second; } @Override public int compare(File lhs, File rhs) { if (first) { if (!lhs.isFile() && rhs.isFile()) { return -1; } if (lhs.isFile() && !rhs.isFile()) { return 1; } } else { if (!lhs.isFile() && rhs.isFile()) { return 1; } if (lhs.isFile() && !rhs.isFile()) { return -1; } } if (second) { if (!(lhs.isFile() ^ rhs.isFile())) { return lhs.getName().compareTo(rhs.getName()); } } else { if (!(lhs.isFile() ^ rhs.isFile())) { return -lhs.getName().compareTo(rhs.getName()); } } return 0; } }
/** * 将文件列表排序 */ private void sort() { Collections.sort(this.filedata, FileSortFactory.getWebFileQueryMethod(sortWay)); }
以上,就是这两天の战斗成果总结了。
话说这两天碰到的坑实在太多,先是Github提交以后不显示进度点,折腾了好久才发现是邮箱写错了!
编码过程中倒是没有太大的难点,虽然这个项目比较简单但是也把以前学过的东西很好的复习了一遍。
但是还是平时练得太少了!!!很多东西很久不用就忘掉了~!
orz,哎好想找个实习充实自己~~~2333(残念脸)