Android 一天撸一个文件路径选择器FileChooser

前言
额,突然心血来潮,把这个功能给做出来了。之前一直以为要一次遍历手机的所有文件再显示出来,后来仔细想了会,没那么复杂,只需要先指定一个路径再获取其子目录,即可完成我想要的效果。
效果
Android 一天撸一个文件路径选择器FileChooser
文章图片
Android 一天撸一个文件路径选择器FileChooser
文章图片

传送门
https://github.com/Ccapton/FileChooser
### 项目结构
Android 一天撸一个文件路径选择器FileChooser
文章图片

FileChooser、FileChooserActivity、FileTourController 这三个类实现界面与文件列表展示逻辑的耦合与内聚。
不过,因为我用到了databinding框架和vectorDrawable文件,所以要在app的build.gradle中如下图绿色框配置代码。
Android 一天撸一个文件路径选择器FileChooser
文章图片

FileChooser.java

package com.capton.fc; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.support.v4.app.Fragment; /** * Created by capton on 2018/1/5. */public class FileChooser {private Context mContext; private int themeColorRes = R.color.themeColor; private FileChoosenListener fileChoosenListener; private String mChoosenFilePath = ""; private String title = "选择目标"; private String doneText = "完成"; private int backIconRes = R.drawable.back_white; private boolean showFile = true; public boolean isFileShow() { return showFile; }public FileChooser showFile(boolean showFile) { this.showFile = showFile; return this; }public FileChooser setCurrentPath(String currentPath){ this.mChoosenFilePath = currentPath; return this; } public FileChooser setTitle(String title){ this.title = title; return this; }public FileChooser setDoneText(String doneText) { this.doneText = doneText; return this; }public FileChooser setBackIconRes(int backIconRes) { this.backIconRes = backIconRes; return this; }public FileChooser(Fragment fragment , FileChoosenListener fileChoosenListener) { this.mContext = fragment.getContext(); this.fileChoosenListener = fileChoosenListener; } public FileChooser(Activity activity ,FileChoosenListener fileChoosenListener) { this.mContext = activity; this.fileChoosenListener = fileChoosenListener; } public void open(){ FileChooserActivity.mFileChooser = this; Intent intent = new Intent(mContext,FileChooserActivity.class); intent.putExtra("themeColorRes",this.themeColorRes); intent.putExtra("currentPath",this.mChoosenFilePath); intent.putExtra("title",this.title); intent.putExtra("doneText",this.doneText); intent.putExtra("backIconRes",this.backIconRes); intent.putExtra("showFile",this.showFile); this.mContext.startActivity(intent); }protected void finish(String filePath){ if(fileChoosenListener != null) fileChoosenListener.onFileChoosen(filePath); } public FileChooser setThemeColor(int themeColorRes){ this.themeColorRes = themeColorRes; return this; }public FileChooser setFileChoosenListener(FileChoosenListener fileChoosenListener) { this.fileChoosenListener = fileChoosenListener; return this; }public interface FileChoosenListener{ void onFileChoosen(String filePath); } }

FileChooserActivity.java
package com.capton.fc; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.ListPopupWindow; import android.view.View; import android.widget.AdapterView; import com.blankj.utilcode.util.Utils; import com.capton.fc.databinding.ActivityFileChooserBinding; import java.io.File; import java.util.ArrayList; import java.util.List; public class FileChooserActivity extends BaseActivity {private int themeColorRes; private int backIconRes; private boolean showFile = true; public static FileChooser mFileChooser; private String mChoosenFilePath; private FileTourController tourController; private FileAdapter adapter; private CurrentFileAdapter currentFileAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Utils.init(getApplication()); resetThemeColor(); resetBackIconRes(); setMiddleTitle(); setDoneText(); setShowRightText(true); baseBinding.back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FileChooserActivity.this.finish(); } }); }private void setMiddleTitle(){ String middleTitle = getIntent().getStringExtra("title"); setTitle(middleTitle); } private void setDoneText(){ String doneText = getIntent().getStringExtra("doneText"); setRightText(doneText); } private void resetThemeColor() { this.themeColorRes = getIntent().getIntExtra("themeColorRes",R.color.themeColor); setThemeColor(themeColorRes); } private void resetBackIconRes(){ this.backIconRes = getIntent().getIntExtra("backIconRes",R.drawable.back_white); setBackIcon(backIconRes); }@Override public String[] getPermissions() { return requestPermissions; }@Override public int getLayoutId() { return R.layout.activity_file_chooser; }@Override public void setClickListener() { this.showFile = getIntent().getBooleanExtra("showFile",true); this.mChoosenFilePath =getIntent().getStringExtra("currentPath"); tourController = new FileTourController(this,mChoosenFilePath); tourController.setShowFile(this.showFile); adapter = new FileAdapter(this, (ArrayList) tourController.getCurrenFileInfoList(),R.layout.item_file); binding.fileRv.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); binding.fileRv.setAdapter(adapter); currentFileAdapter = new CurrentFileAdapter(this, (ArrayList) tourController.getCurrentFolderList(),R.layout.item_current_file); binding.currentPath.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false)); binding.currentPath.setAdapter(currentFileAdapter); binding.currentPath.scrollToPosition(tourController.getCurrentFolderList().size()-1); adapter.setItemClickListener(new CommonAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { File selectFile =new File(tourController.getCurrenFileInfoList().get(position).getFilePath()); ArrayList childFileInfoList = (ArrayList) tourController.addCurrentFile(selectFile); adapter.setData(childFileInfoList); adapter.notifyDataSetChanged(); currentFileAdapter.setData(tourController.getCurrentFolderList()); currentFileAdapter.notifyDataSetChanged(); binding.currentPath.scrollToPosition(tourController.getCurrentFolderList().size()-1); } }); currentFileAdapter.setItemClickListener(new CommonAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { List fileInfoList = tourController.resetCurrentFile(position); adapter.setData(fileInfoList); adapter.notifyDataSetChanged(); currentFileAdapter.setData(tourController.getCurrentFolderList()); currentFileAdapter.notifyDataSetChanged(); binding.currentPath.scrollToPosition(tourController.getCurrentFolderList().size()-1); } }); binding.switchSdcard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final ListPopupWindow listPopupWindow= new ListPopupWindow(FileChooserActivity.this); listPopupWindow.setAnchorView(v); ArrayList sdcardList = new ArrayList<>(); sdcardList.add("手机存储"); if(FileTourController.getStoragePath(FileChooserActivity.this,true) != null) sdcardList.add("SD卡"); SdCardAdapter sdCardAdapter = new SdCardAdapter(FileChooserActivity.this,sdcardList); listPopupWindow.setAdapter(sdCardAdapter); listPopupWindow.setWidth(sdCardAdapter.getItemViewWidth()); //listPopupWindow.setAdapter(new ArrayAdapter(FileChooserActivity.this,android.R.layout.simple_list_item_1,sdcards)); listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if(tourController!=null) tourController.switchSdCard(position); if(adapter!=null) { adapter.setData(tourController.getCurrenFileInfoList()); adapter.notifyDataSetChanged(); } if(currentFileAdapter!=null) { currentFileAdapter.setData(tourController.getCurrentFolderList()); currentFileAdapter.notifyDataSetChanged(); } listPopupWindow.dismiss(); } }); listPopupWindow.show(); } }); }@Override public void clickMore() {}@Override public void clickRightText() { if(tourController != null) mChoosenFilePath = tourController.getCurrentFile().getAbsolutePath(); if(this.mFileChooser != null) mFileChooser.finish(mChoosenFilePath); finish(); }@Override protected void onDestroy() { super.onDestroy(); mFileChooser = null; System.out.println("FileChooserActivity.onDestroy"); }@Override public void onBackPressed() { if(!tourController.isRootFile()) {List currentList = tourController.backToParent(); adapter.setData(currentList); adapter.notifyDataSetChanged(); currentFileAdapter.setData(tourController.getCurrentFolderList()); currentFileAdapter.notifyDataSetChanged(); } else { super.onBackPressed(); } } }

FileTourController.java
package com.capton.fc; import android.content.Context; import android.os.storage.StorageManager; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * Created by capton on 2018/1/6. */public class FileTourController {private File currentFile; private File rootFile; private File sdcard0Root; private File sdcard1Root; private List currenFileInfoList; private List currentFolderList = new ArrayList<>(); private boolean isRootFile = true; private boolean showFile = true; private int sdcardIndex; private Context mContext; public FileTourController(Context context,String currentPath){ //try { this.currentFile = new File(currentPath); //}catch (Exception e){ //e.printStackTrace(); //} this.mContext = context; rootFile = getRootFile(); System.out.println("FileTourController.getRootFile " + rootFile.getAbsolutePath()); if(currentFile == null) { this.currentFile = rootFile; } else if(!currentFile.exists()) { this.currentFile = rootFile; } else isRootFile = false; if(!currentFile.getAbsolutePath().equals(getRootFile().getAbsolutePath())){ currentFolderList.add(rootFile); ArrayList fileList = new ArrayList<>(); File tempFile = currentFile; while (!tempFile.getParent().equals(rootFile.getAbsolutePath())){ fileList.add(tempFile.getParentFile()); tempFile = tempFile.getParentFile(); } for (int i = fileList.size()-1; i >= 0 ; i--) { currentFolderList.add(fileList.get(i)); } }currenFileInfoList = searchFile(this.currentFile); currentFolderList.add(this.currentFile); } public FileTourController(Context context){ this.mContext = context; rootFile = getRootFile(); this.currentFile = rootFile; currenFileInfoList = searchFile(this.currentFile); currentFolderList.add(this.currentFile); }public boolean isShowFile() { return showFile; }public void setShowFile(boolean showFile) { this.showFile = showFile; }public List getCurrentFolderList() { return currentFolderList; }public List getCurrenFileInfoList() { return currenFileInfoList; }public File getRootFile(){ if(sdcardIndex == 1){ return getSDcard1(); } else { return getSDcard0(); } } public void switchSdCard(int sdcardIndex){ if(sdcardIndex == 0){ rootFile = getSDcard0(); } else { rootFile = getSDcard1(); } this.currentFile = rootFile; currenFileInfoList = new ArrayList<>(); currentFolderList = new ArrayList<>(); currenFileInfoList = searchFile(this.currentFile); currentFolderList.add(this.currentFile); }public File getSDcard0(){ return new File(getStoragePath(mContext,false)); } public File getSDcard1(){ if(getStoragePath(mContext,true) == null) return new File(getStoragePath(mContext,false)); return new File(getStoragePath(mContext,true)); } public static String getStoragePath(Context mContext, boolean is_removale) {StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); Class storageVolumeClazz = null; try { storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList"); Method getPath = storageVolumeClazz.getMethod("getPath"); Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); Object result = getVolumeList.invoke(mStorageManager); final int length = Array.getLength(result); for (int i = 0; i < length; i++) { Object storageVolumeElement = Array.get(result, i); String path = (String) getPath.invoke(storageVolumeElement); boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement); if (is_removale == removable) { return path; } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; }public boolean isRootFile() { if(isRootFile(currentFile)) isRootFile = true; else isRootFile = false; return isRootFile; }public void setCurrentFile(File currentFile){ this.currentFile = currentFile; }public File getCurrentFile() { return currentFile; }publicList addCurrentFile(File file){ ListfileInfoList = new ArrayList<>(); currentFile = file; currentFolderList.add(file); fileInfoList = searchFile(file); this.currenFileInfoList = fileInfoList; return fileInfoList; } publicList resetCurrentFile(int position){ ListfileInfoList = new ArrayList<>(); while ( currentFolderList.size()-1 > position){ currentFolderList.remove(currentFolderList.size()-1); } if(currentFolderList.size() != 0) currentFile = new File(currentFolderList.get(currentFolderList.size()-1).getAbsolutePath()); else currentFile = rootFile; fileInfoList = searchFile(currentFile); this.currenFileInfoList = fileInfoList; return fileInfoList; }public List searchFile(File file){ this.currentFile = file; ListfileInfoList = new ArrayList<>(); File childFiles[] = file.listFiles(); if(childFiles != null ) for (int i = 0; i < childFiles.length; i++) { FileInfo fileInfo = new FileInfo(); fileInfo.setFileName(childFiles[i].getName()); fileInfo.setFilePath(childFiles[i].getAbsolutePath()); if(childFiles[i].isDirectory()){ fileInfo.setFolder(true); fileInfo.setFileType(FileInfo.FILE_TYPE_FOLDER); } else { fileInfo.setFolder(false); if("mp4".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "mkv".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "avi".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "3gp".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "mov".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_VIDEO); else if("mp3".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "aac".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "amr".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "ogg".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "wma".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "wav".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "flac".equals(getFileTypeName(childFiles[i].getAbsolutePath()))|| "ape".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_AUDIO); else if("apk".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_APK); else if("zip".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_ZIP); else if("rar".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_RAR); else if("jpeg".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_JPEG); else if("jpg".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_JPG); else if("png".equals(getFileTypeName(childFiles[i].getAbsolutePath()))) fileInfo.setFileType(FileInfo.FILE_TYPE_PNG); else fileInfo.setFileType(FileInfo.FILE_TYPE_FILE); } if(this.showFile) { fileInfoList.add(fileInfo); }else { if (fileInfo.isFolder()) fileInfoList.add(fileInfo); } } return fileInfoList; }public List backToParent(){ currentFile = currentFile.getParentFile(); if(isRootFile(currentFile)) isRootFile = true; else isRootFile = false; currentFolderList.remove(currentFolderList.size()-1); // if(tourListener != null) //tourListener.onBackWard(currentFile,isRootFile); return resetCurrentFile(currentFolderList.size()); }public boolean isRootFile(File file){ return rootFile.getAbsolutePath().equals(file.getAbsolutePath()); }private String getParentName(String path){ int end =path.lastIndexOf("/") + 1; return path.substring(0,end); }private String getFileTypeName(String path){ int start = path.lastIndexOf(".") + 1; if(start == -1) return ""; return path.substring(start); }/* public void setTourListener(TourListener tourListener) { this.tourListener = tourListener; }private TourListener tourListener; public interface TourListener{ void onBackWard(File file,boolean isRootFile); }*/ }

后续
感觉还可以继续完善,后续我会把UI框架独立出来单独配置,这样就能自由地控制视觉效果了。本来打算做出对话框形式的,但是想到文件数量多的情况会显示不友好,就先用Activity来展示了,今后会添加一个底部弹出框的形式来显示文件列表,就像我上一个项目BottomDialog的效果那样 https://github.com/Ccapton/BottomDialog
【Android 一天撸一个文件路径选择器FileChooser】好吧,谢谢各位捧场,希望能有前辈们指出我代码的不足之处。

    推荐阅读