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

前言

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

效果

传送门

https://github.com/Ccapton/FileChooser
### 项目结构
2121515255059_.pic.jpg
FileChooser、FileChooserActivity、FileTourController 这三个类实现界面与文件列表展示逻辑的耦合与内聚。

不过,因为我用到了databinding框架和vectorDrawable文件,所以要在app的build.gradle中如下图绿色框配置代码。
image.png

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<ActivityFileChooserBinding> {

    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<FileInfo>) tourController.getCurrenFileInfoList(),R.layout.item_file);
        binding.fileRv.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
        binding.fileRv.setAdapter(adapter);

        currentFileAdapter = new CurrentFileAdapter(this, (ArrayList<File>) 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<FileInfo> childFileInfoList = (ArrayList<FileInfo>) 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<FileInfo> 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<String> 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<String>(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<FileInfo> 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<FileInfo> currenFileInfoList;
    private List<File> 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<File> 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<File> getCurrentFolderList() {
        return currentFolderList;
    }

    public List<FileInfo> 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;
    }

    public   List<FileInfo> addCurrentFile(File file){
        List<FileInfo>  fileInfoList = new ArrayList<>();
        currentFile = file;
        currentFolderList.add(file);
        fileInfoList = searchFile(file);
        this.currenFileInfoList = fileInfoList;
        return fileInfoList;
    }
    public   List<FileInfo> resetCurrentFile(int position){
        List<FileInfo>  fileInfoList = 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<FileInfo> searchFile(File file){
        this.currentFile = file;
        List<FileInfo>  fileInfoList = 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<FileInfo> 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

好吧,谢谢各位捧场,希望能有前辈们指出我代码的不足之处。

猜你喜欢

转载自blog.csdn.net/ccapton/article/details/78996080