最近在研究网易云信demo的时候,我发现他们居然提供API发送文件,这个功能是微信都没有的,于是我点开看了看,结果觉得很low,只是起了一个抛砖引玉的作用而已。于是我继续搬砖来抛!

demo效果:

android项目 读取根目录_android项目 读取根目录

这里看内容也知道布局很简单,所以我就不贴代码了,接着讲java实现吧!其实实现非常简单,主要是获得根目录,拿到数据,然后将数据提供给ListView的Adapter。
系统提供获取根目录的方法有四个:

一、Android系统的总根目录,包含”/sdcard”、”/system”、”/data”、”/cache”、”/dev”(最后这个文件夹自行搜索吧,我搜出来的也不是很准确。注①),说了这么多,这个文件怎么找出来呢?两个方法:

File[] fileList=File.listRoots();  
            File rootFile = fileList[0];
            //File rootFile = new File("/");

Android系统的总根目录下可以加载出来有哪些文件呢?,效果如图:

android项目 读取根目录_android_02

二、获取系统存储根目录,这里说的系统仅仅指”/system” ,不包括外部存储的,不过手机存储的范围远远大于所谓的系统存储,这个文件可以这样写:

File file=Environment.getRootDirectory();
//File file=new File("/system");

系统存储根目录下具体有哪些文件呢?,效果如图:

android项目 读取根目录_数据_03

这个路径下的文件和上一个路径下的文件是不是不一样呢?感觉我们平时看到的都不是这些文件?对的,我们平时一般查阅的都是SD卡的根目录。
三、获取SD卡根目录,这个文件写法:

File file=Environment.getExternalStorageDirectory();
//File file=new File("/sdcard");

SD卡根目录下具体有哪些文件呢?,效果如图:

android项目 读取根目录_android_04

相信现在大家看见这个目录都很熟悉,这个才是我们平时经常用的目录,那么最后一个我们平时不常用的目录是什么呢?细节之处有魔鬼,我们还是去看看这里有没有魔鬼的存在!

四、获取data根目录,这个目录下就是我们的ApplicationData数据,不过一般情况下,面子不大是拿不到这个文件的。我们先看看这个文件应该怎么写?

File file=Environment.getDataDirectory();
//File file=new File("/data");

File file=new File(“/data”),郭神在《我的第一行代码》第六章讲数据存储持久化举例就是这样写的,那我们去看看以这个作为根目录下有哪些文件呢?

android项目 读取根目录_android_05

咦?怎么会一个程序都没有,至少我现在运行这个程序应该存在额?为什么没有显示出来呢?原来是由于data文件夹是android里一个非常重要的文件夹,所以一般权限是无法获取到文件的,即fileList.length返回为0

OK,既然知道了这么多根目录,那挑一个SD卡根目录开工吧!这里我们先看看SD卡根目录下面的文件我们是怎么一个不少的全部找到的?

/**
     * 初始化控件
     */
    private void initView() 
    {
        ivBack = (ImageView) findViewById(R.id.fileManager_title_bar_back);
        tvTitle = (TextView) findViewById(R.id.fileManager_title_bar_filename);
        lvContext = (ListView) findViewById(R.id.fileManager_listView);
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
        {
            rootFile = Environment.getExternalStorageDirectory();//14
            data = rootFile.listFiles();
            adapter = new fileManagerAdapter( data,true);
            lvContext.setAdapter(adapter);
            tvTitle.setText("data根目录");
        }
    }

这里重点是列出根目录文件夹下的所有文件和文件夹名,我们使用到一个API :File[] files = File.listFiles();就是如此简单的把Adapter的数据搞定了。but,怎么实现根目录的时候数组的第一个位置为点击没有无响应事件,但是非根目录下点击则可以返回上一级目录呢?
我想大家应该已经想到很多方法来实现,反正我只负责搬砖来抛砖,引来你们好玉的。看好,我要抛砖了!

Adapter的一些关键方法

this.data = addBack(data);
/**
         * 第一个增加返回的位置
         * @param data
         * @return
         */
        private File[] addBack(File[] data) 
        {
            if(data==null)//遇到空文件夹
            {
                data = new File[0];//ListView的返回位置,表明这个集合是一个长度为1的空集合
            }
            File[] fileData = new File[data.length+1];
            for (int i = 0; i < data.length; i++) 
            {
                String path = "/";
                //由于ListView下每一项都是文件类型,所以这里必须定义一个文件,只要是文件的类型即可
                fileData[0] = new File(path);
                fileData[i+1] = data[i];
            }
            return fileData;


        }

@Override
        public View getView(int position, View convertView, ViewGroup parent) 
        {
            ViewHolder viewHolder;
            if(convertView==null)
            {
                convertView = inflater.inflate(R.layout.list_filemanager_item, parent, false);
                viewHolder = new ViewHolder();
                viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.list_filemanager_item_icon);
                viewHolder.tvName = (TextView) convertView.findViewById(R.id.list_filemanager_item_name);
                convertView.setTag(viewHolder);
            }
            else 
            {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            if(position!=0)//一定要判断这个位置是不是第一个
            {
                File item = data[position];
                String name = item.getName();
                viewHolder.tvName.setText(name);
                if(item.isFile())
                {
                    //根据文件类型item设置不同的Icon
                    setIcon(name,viewHolder);
                }else
                {
                    viewHolder.ivIcon.setImageResource(R.drawable.directory);
                }
            }
            else //第一个位置
            {
                viewHolder.ivIcon.setImageResource(R.drawable.directory);
                if(isDir)//当前是根目录下
                {
                    viewHolder.tvName.setText("/");
                }
                else //当前非根目录
                {
                    viewHolder.tvName.setText("..");
                }
            }
            return convertView;
        }


/**
         * 设置文件icon
         * @param name
         * @param viewHolder
         */
        private void setIcon(String name, ViewHolder viewHolder) 
        {
            if(name.lastIndexOf(".xls")!=-1 ||name.lastIndexOf(".xlsx")!=-1)
            {
                viewHolder.ivIcon.setImageResource(R.drawable.file_ic_detail_excel);
            }
            else if(name.lastIndexOf(".zip")!=-1 )
            {
                viewHolder.ivIcon.setImageResource(R.drawable.file_ic_detail_zip);
            }
            else 
            {
                viewHolder.ivIcon.setImageResource(R.drawable.file);
            }

        }

Adapter里面整明白了是怎么一回事之后,我们看看ListView的监听事件:

/**
     * 初始化监听事件
     */
    private void initListener() 
    {
        lvContext.setOnItemClickListener(new OnItemClickListener() 
        {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                    long id) 
            {

                if(position!=0)//不是第一项
                {
                    File item = adapter.data[position];
                    if(item.isFile())//点击了文件
                    {
                        Intent intent = new Intent();
                        intent.putExtra("filePath", item.getAbsolutePath());
                        setResult(RESULT_OK, intent);
                        finish();
                    }
                    else//点击了文件夹
                    {
                        Log.i("FileManagerActivity", "filePath:"+item.getAbsolutePath());
                        data = item.listFiles();
                        adapter = new fileManagerAdapter( data,false);
                        lvContext.setAdapter(adapter);
                        tvTitle.setText(item.getName());
                        rootFile = item;
                    }
                }
                else//点击第一项
                {
                    //当前是否在SD卡根目录下
                    if(rootFile.equals(Environment.getExternalStorageDirectory()))
                    {//这里本来说好点击无响应,处于放假状态的,为什么还有活要干呢?
                    //因为从根目录的下一层文件返回回来的时候,需要对adapter进行更新
                        data = rootFile.listFiles();
                        adapter = new fileManagerAdapter( data,true);
                        lvContext.setAdapter(adapter);
                        tvTitle.setText(FileManagerActivity.this.getResources().getString(R.string.file_manager_title_name));
                    }
                    else 
                    {//当前不是在SD卡的根目录下,需要返回上一级目录
                        rootFile=rootFile.getParentFile();
                        if(rootFile!=null)
                        {
                            data = rootFile.listFiles();
                            adapter = new fileManagerAdapter( data,false);
                            lvContext.setAdapter(adapter);
                            tvTitle.setText(rootFile.getName());
                        }
                    }
                }

            }
        });
        ivBack.setOnClickListener(new OnClickListener()
        {

            @Override
            public void onClick(View v) 
            {
                FileManagerActivity.this.setResult(RESULT_CANCELED);
                FileManagerActivity.this.finish();
            }
        });
    }

这里有使用到几个比较常用的关于File的API:
判断SD卡是否插入
Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED);
因为涉及到SDK的读取,自然就需要先判断SDK是否插入。
判断一个文件是不是文件还是文件夹?
判断是不是文件? File.isFile();
判断是不是文件夹? File.isDirectory();
获取文件所在路径即文件所在文件夹:String parentPath = File.getParent();

这里因为我只需要将文件选中之后执行发送,大家看到我选中一个文件之后直接返回路径然后finish()了。不过如果大家有兴趣还可以做一个对话框对文件进行重命名( File.renameTo(destName);)、删除( File.delete();)、移动(获取到文件绝对路径,然后在指定位置写入该文件,最后删除原文件)等等,最后看看你们能做出什么样的文件管理器!!动手试一试吧!!

我最后加载出来的效果是这样:

android项目 读取根目录_listview_06

本文写到这里就完成了,如果有不明白的地方小菜欢迎指出,大家一起学习讨论,谢谢!

注①:dev是设备(device)的英文缩写。/dev这个目录对所有的用户都十分重要。因为在这个目录中包含了所有Linux系统中使用的外部设备。但是这里并不是放的外部设备的驱动程序,这一点和windows,dos操作系统不一样。它实际上是一个访问这些外部设备的端口。我们可以非常方便地去访问这些外部设备,和访问一个文件,一个目录没有任何区别。 Linux沿袭Unix的风格,将所有设备认成是一个文件。)