自制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,让其显示当前目录的文件。另外还有其他一些辅助函数,这里没有写出来。
@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
- 自制Android 文件浏览器
- android学习笔记之自制简易浏览器
- 自制浏览器
- Android自制浏览器WebView-android学习之旅(64)
- Qt 自制文件浏览器并实现按照扩展名过滤
- Android 自制浏览器 一 能够跳转链接并返回
- Android 自制浏览器 二 前进后退返回主页退出
- 自制IE浏览器
- 自制简单浏览器界面
- 自制浏览器右键菜单
- Android文件浏览器
- android文件浏览器
- Android之文件浏览器
- android 文件浏览器实现
- Android文件浏览器
- Android 编写文件浏览器
- 自制工具:Cocos2d-x Android.mk文件自动修改器
- delphi实现自制IE浏览器
- 如何在SQL Server数据库中加密数据
- Android系统init.rc分析
- 第十章实验10.2
- 视频显示格式720p
- [总结] Bitwise operations
- 自制Android 文件浏览器
- android 修改ramdisk.img和init.rc && android启动后设置/data权限为770
- 9岁留守男童因父母不回家过年上吊自杀
- android init.rc语法标准
- A+B for Input-Output Practice (I)
- EtherType
- 物理地址和虚拟地址的概念
- 把他的依靠当作回报
- 物理地址和虚拟地址(二)