自制Android 文件浏览器

来源:互联网 发布:淘宝如何看店铺号 编辑:程序博客网 时间:2024/04/28 17:59

以前手机上一直使用的文件浏览器是文件大师,但是这些android的软件经常提示更新,然后还有很多附加功能,根本用不上,以及一些讨厌的通知。

作为一个程序员理应动手解决这个烦恼,因此我编写了下面的文件浏览器。我的程序用的比较特殊的功能总结如下:

1、Intent.addCategory和Uri;

2、TextView的CompoundDrawable;

3、ListView的重绘;

4、BaseAdapter的继承;

5、各种Listener的实现;

6、ActionBar的实现和改变;

7、带ProgressBar的dialog。

一、环境配置

我选择的target 是android 4.0, 估计android 3.0以前的程序用不了。

二、Mainifest文件

文件中只配置了一个Activity,使用的权限有

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.INTERNET"/>


第一个权限主要是操作存储器上的文件,没有它甚至不能看到文件。

第二个权限主要是使用Intent。

三、source code

源代码可以在github上下载。下载下来的源码是不能导入到eclipse的,要运行的话先创建一个工程:

工程名:FileExplorer

包名:com.huneng.fileexplorer

然后把下载的源码中的src,res,和AndroidManifest复制到新建的工程中,文件选择覆盖。


下面就依次介绍我的想法

1、首先是建立界面

界面上主要用到的控件是ListView,ListView 的Item也要自己设计,目前这个Item上的控件有TextView和Checkable。ListView和ListViewItem分别在文件activity_main.xml和listitem中设计

        2、自定义Adapter

        如果只是简单的Item,例如item内容只是string,是不需要自定义Adapter。我希望我的FileExplorer功能复杂一点所以就定义了Adapter:

public class MyAdapter extends BaseAdapter {public static final int ListMode_Edit = 0;public static final int ListMode_Default = 1;public static final int ListMode_Open = 2;
private List<String> m_sFileNames;private boolean m_CheckArray[];public int m_ListMode;private Context m_Context;public MyAdapter(Context a_Context) {}public MyAdapter(Context a_Context, List<String> a_FileNames) {}@Overridepublic int getCount() {}@Overridepublic Object getItem(int a_Index) {}@Overridepublic long getItemId(int a_Pos) {}@Overridepublic View getView(int a_Position, View a_ConvertView, ViewGroup a_Parent) {}    public List<String> getFileNames() {}public void setFileNames(List<String> a_FileNames) {}public boolean setCheck(int a_Index, boolean a_Value) {}public void setListMode(int a_ListMode) {}public List<String> getCheckedList(){} public List<String> getUnCheckedList(){} }

ListModeXXX:用来标记状态的,主要的状态包括:浏览、编辑、打开文件,当我们需要对文件进行操作,诸如删除或者移动时就需要进入编辑状态,其余时候就是浏览状态了。

m_sFileNames:文件名

m_CheckArray:选择文件时的标记数组。

m_Context:MainActivity对象的Context,生成自定义item时需要用其生成LayoutInflater

这个类的主要函数是 :

@Overridepublic View getView(int a_Position, View a_ConvertView, ViewGroup a_Parent) {if (m_sFileNames == null)return null;ViewHolder t_Holder = null;if (a_ConvertView == null) {t_Holder = new ViewHolder();LayoutInflater l_Inflater = LayoutInflater.from(m_Context);a_ConvertView = l_Inflater.inflate(R.layout.listitem, null);t_Holder.tvFileName = (TextView) a_ConvertView.findViewById(R.id.tv_FileName);t_Holder.cbCheckBox = (CheckBox) a_ConvertView.findViewById(R.id.cb_FileCheck);a_ConvertView.setTag(t_Holder);} else {t_Holder = (ViewHolder) a_ConvertView.getTag();}String l_FilePath = m_sFileNames.get(a_Position);String l_FileName = FileUtil.getFileName(l_FilePath);t_Holder.tvFileName.setText(l_FileName);int l_FileType = FileUtil.getFileType(l_FilePath);int l_ResourceID = MToolBox.getFileImage(l_FileType);Drawable l_Drawable = m_Context.getResources().getDrawable(l_ResourceID);int x = 50 * MainActivity.m_xd;l_Drawable.setBounds(0, 0, x - 5, x - 5);t_Holder.tvFileName.setCompoundDrawablePadding(5);t_Holder.tvFileName.setCompoundDrawables(l_Drawable, null, null, null);if (m_ListMode == ListMode_Edit)t_Holder.cbCheckBox.setVisibility(View.VISIBLE);elset_Holder.cbCheckBox.setVisibility(View.INVISIBLE);t_Holder.cbCheckBox.setOnCheckedChangeListener(new MyOnCheckedChangeListener(m_CheckArray, a_Position));t_Holder.cbCheckBox.setChecked(m_CheckArray[a_Position]);return a_ConvertView;}

ViewHolder是一个包含了所有listviewitem上的控件的的,所以这里的成员有 CheckBox cbCheckBox和TextView tvFileName。

FileUtil和ToolBox在之后会讲到;

MainActivity的m_xd是1dp的像素数,TextView有一个CompoundDrawables属性,指的是TextView的上下左右放置的图片,我是用了左边的图像用来标记文件类型,这里指支持目录、jpg和png,其他文件一概而论。

这个函数的最下面初始化了cbCheckBox,如果处在浏览状态,则让它不可见。自定义的MyOnCheckedChangeListener类实现了接口OnCheckedChangeListener,当cbCheckBox的check状态改变时会反映到m_CheckArray上。所以m_CheckArray要与m_sFileNames的数量保持一致。

3、MainActivity

类MainActivity继承自Activity,启用了ActionBar,因此在Menu/main.xml中有如下定义:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >    <item        android:id="@+id/action_back"        android:icon="@drawable/back"        android:showAsAction="always"        android:title="@string/action_back"/>    <item        android:id="@+id/action_removefile"        android:icon="@drawable/trash"        android:showAsAction="never"        android:title="@string/trash"/>    <item        android:id="@+id/action_move"        android:showAsAction="never"        android:title="@string/action_move"/>    <item        android:id="@+id/action_cancel"        android:showAsAction="never"        android:title="@android:string/cancel"/>    <item        android:id="@+id/action_ok"        android:showAsAction="never"        android:title="@android:string/ok"/></menu>


总共5项,返回上一级、删除、移动、取消和确定,他们的属性中android:showAsAction是控制其是否在ActionBar上显示的选项。

下面是MainActivity的定义:

public class MainActivity extends Activity {MyAdapter m_Adapter;ListView m_ListView;Menu m_OptionMenu;MenuItem m_RemoveItem;MenuItem m_BackItem;MenuItem m_CancelItem;MenuItem m_OkItem;MenuItem m_MoveItem;List<String> m_RenameList;public static int m_xd, m_yd;@Overrideprotected void onCreate(Bundle savedInstanceState) {}public void updateFileList(String a_DirPath) {}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {}@Overridepublic boolean onCreateOptionsMenu(Menu a_Menu) {}@Overridepublic boolean onOptionsItemSelected(MenuItem a_Item) {}}

updateFileList是更新m_ListView,让其显示当前目录的文件。另外还有其他一些辅助函数,这里没有写出来。


入口函数onCreate

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);FileUtil.setCurPath(GlobalVariable.ROOT_PATH);setContentView(R.layout.activity_main);ListView m_ListView = (ListView) findViewById(R.id.lv_FileList);List<String> l_List = FileUtil.getCurDirFileNames();m_Adapter = new MyAdapter(this);m_Adapter.setFileNames(l_List);m_ListView.setAdapter(m_Adapter);m_ListView.setOnItemClickListener(new MyOnItemClickListener(this));m_ListView.setOnItemLongClickListener(new MyOnItemLongClickListener(this));DisplayMetrics l_Metrics = new DisplayMetrics();this.getWindowManager().getDefaultDisplay().getMetrics(l_Metrics);m_xd = (int) (l_Metrics.widthPixels / l_Metrics.xdpi);m_yd = (int) (l_Metrics.heightPixels / l_Metrics.ydpi);}

首先,设置当前工作目录为根目录,然后,初始化Actvity的ContentView,也就是界面,之后找到界面中的ListView,并初始化它:

1)得到当前目录下的文件;

2)创建MyAdapter对象m_Adapter,将文件路径列表传递给它;

3)讲m_Adapter传递给m_ListView;


最后,计算x轴和y轴1dp的像素数。


MenuItem选择的入口函数:

@Overridepublic boolean onOptionsItemSelected(MenuItem a_Item) {switch (a_Item.getItemId()) {case R.id.action_back:String l_Path = FileUtil.getParentPath();updateFileList(l_Path);break;case R.id.action_removefile:List<String> l_List = m_Adapter.getCheckedList();for (int i = 0; i < l_List.size(); i++)FileUtil.removeFile(l_List.get(i));// m_Adapter.setListMode(MyAdapter.ListMode_Default);List<String> l_FileLists = FileUtil.getCurDirFileNames();m_Adapter.setFileNames(l_FileLists);m_Adapter.notifyDataSetChanged();displayBackAction();break;case R.id.action_move:displayJudgeActions();m_RenameList = m_Adapter.getCheckedList();m_Adapter.setListMode(MyAdapter.ListMode_Default);m_Adapter.notifyDataSetChanged();break;case R.id.action_ok:if (FileUtil.getCurPath().startsWith(MToolBox.getSdcardPath())) {for (int i = 0; i < m_RenameList.size(); i++) {String l_FilePath = m_RenameList.get(i);String l_FileName = FileUtil.getFileName(l_FilePath);FileUtil.renameFile(l_FilePath, FileUtil.getCurPath() + "/"+ l_FileName);}} else {Toast.makeText(this, "Can't operate file here",Toast.LENGTH_LONG).show();}case R.id.action_cancel:displayBackAction();m_Adapter.setFileNames(FileUtil.getCurDirFileNames());m_Adapter.notifyDataSetChanged();break;}return super.onOptionsItemSelected(a_Item);}

这里的逻辑比较简单,首先是更新m_Adapter的List,然后是MainActivity的ActionBar以及ListView。

删除文件:

1)获取被选择的文件名,根据MyAdapter.m_sFileNames和MyAdapter.m_CheckArray确定,具体见MyAdapter的定义;

2)逐次删除文件,有可能删除的文件是目录,这样就会递归删除;

3)刷新m_ListView,这是m_Adapter.notifyDataSetChanged()函数的工作,告诉ListView重新构造自己。

4)更新ActionBar的MenuItem,隐藏removeItem,显示BackItem。

移动文件:

1)长按ListView的某一项,然后选择文件;

2)点击ActionBar上的Item,Move,将会进入默认状态,但是ActionBar上的按钮有区别;

3)选择目录,点击确定,移动就完成了。


类MyOnItemClickListener和类MyOnItemLongClickListener定义为MainAcitivity的内部类,主要是图方便。

public void onItemClick(AdapterView<?> a_AdapterView, View a_View,int a_Pos, long a_Id) {String l_FilePath = m_Parent.m_Adapter.getFileNames().get(a_Pos);int l_Type = FileUtil.getFileType(l_FilePath);if (l_Type == Global.FileType_Dir) {m_Parent.updateFileList(l_FilePath);} else if (l_Type == Global.FileType_Other) {return;} else {FileUtil.openFile(l_FilePath, m_Parent);}}

点击到的文件是目录,就进入,文件的话,则打开,有些特殊的文件类型是打不开的。

public boolean onItemLongClick(AdapterView<?> a_AdapterView,View a_View, int a_Pos, long a_id) {m_Parent.displayEditActions();CheckBox l_CheckBox = (CheckBox) a_View.findViewById(R.id.cb_FileCheck);l_CheckBox.setChecked(true);m_Parent.updateFileList(FileUtil.getCurPath());m_Parent.m_Adapter.setListMode(MyAdapter.ListMode_Edit);return true;}

public boolean onItemLongClick(AdapterView<?> a_AdapterView,View a_View, int a_Pos, long a_id) {m_Parent.displayEditActions();CheckBox l_CheckBox = (CheckBox) a_View.findViewById(R.id.cb_FileCheck);l_CheckBox.setChecked(true);m_Parent.updateFileList(FileUtil.getCurPath());m_Parent.m_Adapter.setListMode(MyAdapter.ListMode_Edit);return true;}

长按某一项,就会切换ListView的状态为ListMode_Edit,显示Remove和Move按钮,隐藏back按钮。
5、FileUtil和MToolBox
FileUitl中定义了程序中用到的文件操作的所有函数,包括获取文件类表,返回单签工作目录,删除文件,根据路径返回文件名,以及根据文件名和文件类型对文件进行排序。
MToolBox中定义的就是一些杂七杂八的函数。
6、Global
Global类中定义的是在程序中所要用到的常量,包括根目录,文件类型。
7、今后的工作
现在我的程序还有很多bug,但是能够满足基本的需要,今后我会增加更多的功能,最终的想法是,还能通过内网或者蓝牙传送文件。另外还要让我的软件更加漂亮和和谐。
       由于我的软件还很简陋,就不贴图了。


Author E-mail:huneng1991@163.com

1 1
原创粉丝点击