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
好吧,谢谢各位捧场,希望能有前辈们指出我代码的不足之处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容