前言
额,突然心血来潮,把这个功能给做出来了。之前一直以为要一次遍历手机的所有文件再显示出来,后来仔细想了会,没那么复杂,只需要先指定一个路径再获取其子目录,即可完成我想要的效果。
效果
文章图片
文章图片
传送门
https://github.com/Ccapton/FileChooser
### 项目结构
文章图片
FileChooser、FileChooserActivity、FileTourController 这三个类实现界面与文件列表展示逻辑的耦合与内聚。
不过,因为我用到了databinding框架和vectorDrawable文件,所以要在app的build.gradle中如下图绿色框配置代码。
文章图片
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】好吧,谢谢各位捧场,希望能有前辈们指出我代码的不足之处。