很久没有写东西了,鉴于某某同学文件管理器不会,这里简单介绍一下,同时写一个demon,参考了网上别人写的代码,自己也学习学习,研究研究。
首先所谓文件管理器,看起来就是一个列表,列表里面是文件夹或者文件,首先把布局写出来,我想在最上方的左边显示文件的路径,右边显示该路径下的文件个数,其实还是一个遍历文件,然后用列表显示出来的问题。下面是ListView,用来显示文件列表。下面是运行的效果图:
主界面的布局文件如下:
1 <?xml version="1.0" encoding="utf-8"?>
2
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
8 android:layout_width="match_parent"
9 android:layout_height="wrap_content">
10
12 android:layout_width="wrap_content"
13 android:layout_height="wrap_content"
14 android:layout_alignParentLeft="true"
15 android:layout_centerVertical="true"
16 android:textSize="@*android:dimen/list_item_size"
17 android:textColor="@android:color/white"/>
18
19
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:textSize="@*android:dimen/list_item_size"
24 android:textColor="@android:color/white"
25 android:layout_alignParentRight="true"
26 android:layout_centerVertical="true"/>
27
28
30 android:layout_height="2dip"
31 android:background="#09c"/>
32
33
35 android:layout_width="match_parent"
36 android:layout_height="match_parent">
37
38
40 android:layout_height="match_parent"
41 android:layout_width="match_parent"
42 android:fadingEdge="none"
43 android:cacheColorHint="@android:color/transparent"/>
44
45
首先在oncreate方法里面调用一个方法去获取布局文件里面的id:
1 @Override2 protected voidonCreate (Bundle savedInstanceState) {3 super.onCreate(savedInstanceState);4 setContentView(R.layout.file_manager);5 initView();6 }
initView之后添加apk的权限,777 表示可读可写可操作。
1 private voidinitView() {2 mListView =(ListView) findViewById(R.id.file_list);3 mPathView =(TextView) findViewById(R.id.path);4 mItemCount =(TextView) findViewById(R.id.item_count);5 mListView.setOnItemClickListener(this);6 String apkRoot = "chmod 777 " +getPackageCodePath();7 RootCommand(apkRoot);8 File folder = new File("/");9 initData(folder);10 }
修改Root权限的方法:
1 public static booleanRootCommand (String command) {2 Process process = null;3 DataOutputStream os = null;4 try{5 process = Runtime.getRuntime().exec("su");6 os = newDataOutputStream(process.getOutputStream());7 os.writeBytes(command + "\n");8 os.writeBytes("exit\n");9 os.flush();10 process.waitFor();11 }12 catch(Exception e) {13 return false;14 }15 finally{16 try{17 if (os != null) {18 os.close();19 }20 process.destroy();21 }22 catch(Exception e) {23 e.printStackTrace();24 }25 }26 return true;27 }
完了之后我们要获取根目录下面的所有的数据,然后设置到我们的ListView中让它显示出来。
1 private voidinitData(File folder) {2 boolean isRoot = folder.getParent() == null;3 mPathView.setText(folder.getAbsolutePath());4 ArrayList files = new ArrayList();5 if (!isRoot) {6 files.add(folder.getParentFile());7 }8 File[] filterFiles =folder.listFiles();9 mItemCount.setText(filterFiles.length + "项");10 if (null != filterFiles && filterFiles.length > 0) {11 for(File file : filterFiles) {12 files.add(file);13 }14 }15 mFileAdpter = new FileListAdapter(this, files, isRoot);16 mListView.setAdapter(mFileAdpter);17 }
首先是获取当前是否是根目录,然后把文件的路径设置给我们要显示的View。
然后用一个ArrayList来装我们目录下的所有的文件或者文件夹。
首先要把这个文件夹的父类装到我们的列表中去,然后把这个文件夹下的子文件都拿到,也装在列表中,然后调用Adapter显示出来。既然说到了Adapter, 那就看下Adapter吧。
1 private class FileListAdapter extendsBaseAdapter {2
3 privateContext context;4 private ArrayListfiles;5 private booleanisRoot;6 privateLayoutInflater mInflater;7
8 public FileListAdapter (Context context, ArrayList files, booleanisRoot) {9 this.context =context;10 this.files =files;11 this.isRoot =isRoot;12 mInflater =LayoutInflater.from(context);13 }14
15 @Override16 public intgetCount () {17 returnfiles.size();18 }19
20 @Override21 public Object getItem (intposition) {22 returnfiles.get(position);23 }24
25 @Override26 public long getItemId (intposition) {27 returnposition;28 }29
30 @Override31 public View getView (intposition, View convertView, ViewGroup parent) {32 ViewHolder viewHolder;33 if(convertView == null) {34 viewHolder = newViewHolder();35 convertView = mInflater.inflate(R.layout.file_list_item, null);36 convertView.setTag(viewHolder);37 viewHolder.title =(TextView) convertView.findViewById(R.id.file_title);38 viewHolder.type =(TextView) convertView.findViewById(R.id.file_type);39 viewHolder.data =(TextView) convertView.findViewById(R.id.file_date);40 viewHolder.size =(TextView) convertView.findViewById(R.id.file_size);41 } else{42 viewHolder =(ViewHolder) convertView.getTag();43 }44
45 File file =(File) getItem(position);46 if(position == 0 && !isRoot) {47 viewHolder.title.setText("返回上一级");48 viewHolder.data.setVisibility(View.GONE);49 viewHolder.size.setVisibility(View.GONE);50 viewHolder.type.setVisibility(View.GONE);51 } else{52 String fileName =file.getName();53 viewHolder.title.setText(fileName);54 if(file.isDirectory()) {55 viewHolder.size.setText("文件夹");56 viewHolder.size.setTextColor(Color.RED);57 viewHolder.type.setVisibility(View.GONE);58 viewHolder.data.setVisibility(View.GONE);59 } else{60 long fileSize =file.length();61 if(fileSize > 1024*1024) {62 float size = fileSize /(1024f*1024f);63 viewHolder.size.setText(new DecimalFormat("#.00").format(size) + "MB");64 } else if(fileSize >= 1024) {65 float size = fileSize/1024;66 viewHolder.size.setText(new DecimalFormat("#.00").format(size) + "KB");67 } else{68 viewHolder.size.setText(fileSize + "B");69 }70 int dot = fileName.indexOf('.');71 if(dot > -1 && dot < (fileName.length() -1)) {72 viewHolder.type.setText(fileName.substring(dot + 1) + "文件");73 }74 viewHolder.data.setText(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(file.lastModified()));75 }76 }77 returnconvertView;78 }79
80 classViewHolder {81 privateTextView title;82 privateTextView type;83 privateTextView data;84 privateTextView size;85 }86 }
看下adapter的布局文件:
列表的Item项分2行显示,上面一行显示文件名
下面一行分别显示文件大小,文件类型,文件修改时间。
我们可以通过File file = (File) getItem(position);拿到Item项的文件,如果是在第一个并且不再根目录我们就把第一个也就是parentFile显示为:“返回上一级”,下一行的都隐藏掉。
如果不是第一个位置,可以拿到这个文件的一系列信息。
先把String fileName = file.getName();文件名拿到,显示出来。
如果这个文件是一个文件夹,就把文件的大小显示为“文件夹”,类型和修改时间隐藏掉。
如果不是一个文件夹, 可以拿到文件的长度long fileSize = file.length();
根据特定的长度显示文件的大小,B, KB, MB, GB等。
然后拿到文件的类型,通过最后一个“.”之后的字符串就是该文件的类型。
通过viewHolder.data.setText(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(file.lastModified())); 设置文件的最近修改时间。
然后可以设置每个Item项的点击事件,如下所示:
1 @Override2 public void onItemClick (AdapterView> parent, View view, int position, longid) {3 File file =(File) mFileAdpter.getItem(position);4 if(!file.canRead()) {5 new AlertDialog.Builder(this).setTitle("提示").setMessage("权限不足").setPositiveButton(android.R.string.ok, newOnClickListener() {6
7 @Override8 public void onClick (DialogInterface dialog, intwhich) {9
10 }11 }).show();12 } else if(file.isDirectory()) {13 initData(file);14 } else{15 openFile(file);16 }17 }
如果这个文件不能读,就弹出对话框显示“权限不足”。
如果是一个文件夹,就在调用一次显示所有文件的方法:initData(file);把这个文件夹作为参数传递下去。
如果是一个文件,就可以调用打开文件的方法打开这个文件。
如何打开文件呢?
可以根据不同的文件的后缀名找到不同的文件类型:
可以用一个二维数组把一些常用的文件类型封装起来。如下:
1 private final String[][] MIME_MapTable ={2 //{后缀名, MIME类型}
3 { ".3gp", "video/3gpp"},4 { ".apk", "application/vnd.android.package-archive"},5 { ".asf", "video/x-ms-asf"},6 { ".avi", "video/x-msvideo"},7 { ".bin", "application/octet-stream"},8 { ".bmp", "image/bmp"},9 { ".c", "text/plain"},10 { ".class", "application/octet-stream"},11 { ".conf", "text/plain"},12 { ".cpp", "text/plain"},13 { ".doc", "application/msword"},14 { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},15 { ".xls", "application/vnd.ms-excel"},16 { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},17 { ".exe", "application/octet-stream"},18 { ".gif", "image/gif"},19 { ".gtar", "application/x-gtar"},20 { ".gz", "application/x-gzip"},21 { ".h", "text/plain"},22 { ".htm", "text/html"},23 { ".html", "text/html"},24 { ".jar", "application/java-archive"},25 { ".java", "text/plain"},26 { ".jpeg", "image/jpeg"},27 { ".jpg", "image/jpeg"},28 { ".js", "application/x-javascript"},29 { ".log", "text/plain"},30 { ".m3u", "audio/x-mpegurl"},31 { ".m4a", "audio/mp4a-latm"},32 { ".m4b", "audio/mp4a-latm"},33 { ".m4p", "audio/mp4a-latm"},34 { ".m4u", "video/vnd.mpegurl"},35 { ".m4v", "video/x-m4v"},36 { ".mov", "video/quicktime"},37 { ".mp2", "audio/x-mpeg"},38 { ".mp3", "audio/x-mpeg"},39 { ".mp4", "video/mp4"},40 { ".mpc", "application/vnd.mpohun.certificate"},41 { ".mpe", "video/mpeg"},42 { ".mpeg", "video/mpeg"},43 { ".mpg", "video/mpeg"},44 { ".mpg4", "video/mp4"},45 { ".mpga", "audio/mpeg"},46 { ".msg", "application/vnd.ms-outlook"},47 { ".ogg", "audio/ogg"},48 { ".pdf", "application/pdf"},49 { ".png", "image/png"},50 { ".pps", "application/vnd.ms-powerpoint"},51 { ".ppt", "application/vnd.ms-powerpoint"},52 { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},53 { ".prop", "text/plain"},54 { ".rc", "text/plain"},55 { ".rmvb", "audio/x-pn-realaudio"},56 { ".rtf", "application/rtf"},57 { ".sh", "text/plain"},58 { ".tar", "application/x-tar"},59 { ".tgz", "application/x-compressed"},60 { ".txt", "text/plain"},61 { ".wav", "audio/x-wav"},62 { ".wma", "audio/x-ms-wma"},63 { ".wmv", "audio/x-ms-wmv"},64 { ".wps", "application/vnd.ms-works"},65 { ".xml", "text/plain"},66 { ".z", "application/x-compress"},67 { ".zip", "application/x-zip-compressed"},68 { "", "*/*"}69 };
分别对应的是后缀名和对应的文件类型。
我们可以根据文件的后缀名拿到文件的MIMEType类型:
1 privateString getMIMEType(File file) {2 String type = "*/*";3 String fileName =file.getName();4 int dotIndex = fileName.indexOf('.');5 if(dotIndex < 0) {6 returntype;7 }8 String end =fileName.substring(dotIndex, fileName.length()).toLowerCase();9 if(end == "") {10 returntype;11 }12 for(int i=0; i
先遍历后缀名,如果找到,就把对应的类型找到并返回。
拿到了类型,就可以打开这个文件。
用这个intent.setDataAndType(Uri.fromFile(file), type); 打开设置打开文件的类型。
如果type是*/*会弹出所有的可供选择的应用程序。
到这里一个简易的文件管理器就成型了。。。
源代码:
1 packagecom.android.test;2
3 importjava.io.DataOutputStream;4 importjava.io.File;5 importjava.text.DecimalFormat;6 importjava.text.SimpleDateFormat;7 importjava.util.ArrayList;8
9 importandroid.app.Activity;10 importandroid.app.AlertDialog;11 importandroid.content.Context;12 importandroid.content.DialogInterface;13 importandroid.content.Intent;14 importandroid.content.DialogInterface.OnClickListener;15 importandroid.graphics.Color;16 importandroid.net.Uri;17 importandroid.os.Bundle;18 importandroid.view.LayoutInflater;19 importandroid.view.View;20 importandroid.view.ViewGroup;21 importandroid.widget.AdapterView;22 importandroid.widget.AdapterView.OnItemClickListener;23 importandroid.widget.BaseAdapter;24 importandroid.widget.ListView;25 importandroid.widget.TextView;26 importandroid.widget.Toast;27
28 public class FileManager extends Activity implementsOnItemClickListener {29
30 privateListView mListView;31 privateTextView mPathView;32 privateFileListAdapter mFileAdpter;33 privateTextView mItemCount;34
35 @Override36 protected voidonCreate (Bundle savedInstanceState) {37 super.onCreate(savedInstanceState);38 setContentView(R.layout.file_manager);39 initView();40 }41
42 private voidinitView() {43 mListView =(ListView) findViewById(R.id.file_list);44 mPathView =(TextView) findViewById(R.id.path);45 mItemCount =(TextView) findViewById(R.id.item_count);46 mListView.setOnItemClickListener(this);47 String apkRoot = "chmod 777 " +getPackageCodePath();48 RootCommand(apkRoot);49 File folder = new File("/");50 initData(folder);51 }52
53 public static booleanRootCommand (String command) {54 Process process = null;55 DataOutputStream os = null;56 try{57 process = Runtime.getRuntime().exec("su");58 os = newDataOutputStream(process.getOutputStream());59 os.writeBytes(command + "\n");60 os.writeBytes("exit\n");61 os.flush();62 process.waitFor();63 }64 catch(Exception e) {65 return false;66 }67 finally{68 try{69 if (os != null) {70 os.close();71 }72 process.destroy();73 }74 catch(Exception e) {75 e.printStackTrace();76 }77 }78 return true;79 }80
81 private voidinitData(File folder) {82 boolean isRoot = folder.getParent() == null;83 mPathView.setText(folder.getAbsolutePath());84 ArrayList files = new ArrayList();85 if (!isRoot) {86 files.add(folder.getParentFile());87 }88 File[] filterFiles =folder.listFiles();89 mItemCount.setText(filterFiles.length + "项");90 if (null != filterFiles && filterFiles.length > 0) {91 for(File file : filterFiles) {92 files.add(file);93 }94 }95 mFileAdpter = new FileListAdapter(this, files, isRoot);96 mListView.setAdapter(mFileAdpter);97 }98
99 private class FileListAdapter extendsBaseAdapter {100
101 privateContext context;102 private ArrayListfiles;103 private booleanisRoot;104 privateLayoutInflater mInflater;105
106 public FileListAdapter (Context context, ArrayList files, booleanisRoot) {107 this.context =context;108 this.files =files;109 this.isRoot =isRoot;110 mInflater =LayoutInflater.from(context);111 }112
113 @Override114 public intgetCount () {115 returnfiles.size();116 }117
118 @Override119 public Object getItem (intposition) {120 returnfiles.get(position);121 }122
123 @Override124 public long getItemId (intposition) {125 returnposition;126 }127
128 @Override129 public View getView (intposition, View convertView, ViewGroup parent) {130 ViewHolder viewHolder;131 if(convertView == null) {132 viewHolder = newViewHolder();133 convertView = mInflater.inflate(R.layout.file_list_item, null);134 convertView.setTag(viewHolder);135 viewHolder.title =(TextView) convertView.findViewById(R.id.file_title);136 viewHolder.type =(TextView) convertView.findViewById(R.id.file_type);137 viewHolder.data =(TextView) convertView.findViewById(R.id.file_date);138 viewHolder.size =(TextView) convertView.findViewById(R.id.file_size);139 } else{140 viewHolder =(ViewHolder) convertView.getTag();141 }142
143 File file =(File) getItem(position);144 if(position == 0 && !isRoot) {145 viewHolder.title.setText("返回上一级");146 viewHolder.data.setVisibility(View.GONE);147 viewHolder.size.setVisibility(View.GONE);148 viewHolder.type.setVisibility(View.GONE);149 } else{150 String fileName =file.getName();151 viewHolder.title.setText(fileName);152 if(file.isDirectory()) {153 viewHolder.size.setText("文件夹");154 viewHolder.size.setTextColor(Color.RED);155 viewHolder.type.setVisibility(View.GONE);156 viewHolder.data.setVisibility(View.GONE);157 } else{158 long fileSize =file.length();159 if(fileSize > 1024*1024) {160 float size = fileSize /(1024f*1024f);161 viewHolder.size.setText(new DecimalFormat("#.00").format(size) + "MB");162 } else if(fileSize >= 1024) {163 float size = fileSize/1024;164 viewHolder.size.setText(new DecimalFormat("#.00").format(size) + "KB");165 } else{166 viewHolder.size.setText(fileSize + "B");167 }168 int dot = fileName.indexOf('.');169 if(dot > -1 && dot < (fileName.length() -1)) {170 viewHolder.type.setText(fileName.substring(dot + 1) + "文件");171 }172 viewHolder.data.setText(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(file.lastModified()));173 }174 }175 returnconvertView;176 }177
178 classViewHolder {179 privateTextView title;180 privateTextView type;181 privateTextView data;182 privateTextView size;183 }184 }185
186 @Override187 public void onItemClick (AdapterView> parent, View view, int position, longid) {188 File file =(File) mFileAdpter.getItem(position);189 if(!file.canRead()) {190 new AlertDialog.Builder(this).setTitle("提示").setMessage("权限不足").setPositiveButton(android.R.string.ok, newOnClickListener() {191
192 @Override193 public void onClick (DialogInterface dialog, intwhich) {194
195 }196 }).show();197 } else if(file.isDirectory()) {198 initData(file);199 } else{200 openFile(file);201 }202 }203
204 private voidopenFile(File file) {205 Intent intent = newIntent();206 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);207 intent.setAction(Intent.ACTION_VIEW);208 String type =getMIMEType(file);209 intent.setDataAndType(Uri.fromFile(file), type);210 try{211 startActivity(intent);212 }213 catch(Exception e) {214 Toast.makeText(this, "未知类型,不能打开", Toast.LENGTH_SHORT).show();215 }216 }217
218 privateString getMIMEType(File file) {219 String type = "*/*";220 String fileName =file.getName();221 int dotIndex = fileName.indexOf('.');222 if(dotIndex < 0) {223 returntype;224 }225 String end =fileName.substring(dotIndex, fileName.length()).toLowerCase();226 if(end == "") {227 returntype;228 }229 for(int i=0; i
237 private final String[][] MIME_MapTable ={238 //{后缀名, MIME类型}
239 { ".3gp", "video/3gpp"},240 { ".apk", "application/vnd.android.package-archive"},241 { ".asf", "video/x-ms-asf"},242 { ".avi", "video/x-msvideo"},243 { ".bin", "application/octet-stream"},244 { ".bmp", "image/bmp"},245 { ".c", "text/plain"},246 { ".class", "application/octet-stream"},247 { ".conf", "text/plain"},248 { ".cpp", "text/plain"},249 { ".doc", "application/msword"},250 { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},251 { ".xls", "application/vnd.ms-excel"},252 { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},253 { ".exe", "application/octet-stream"},254 { ".gif", "image/gif"},255 { ".gtar", "application/x-gtar"},256 { ".gz", "application/x-gzip"},257 { ".h", "text/plain"},258 { ".htm", "text/html"},259 { ".html", "text/html"},260 { ".jar", "application/java-archive"},261 { ".java", "text/plain"},262 { ".jpeg", "image/jpeg"},263 { ".jpg", "image/jpeg"},264 { ".js", "application/x-javascript"},265 { ".log", "text/plain"},266 { ".m3u", "audio/x-mpegurl"},267 { ".m4a", "audio/mp4a-latm"},268 { ".m4b", "audio/mp4a-latm"},269 { ".m4p", "audio/mp4a-latm"},270 { ".m4u", "video/vnd.mpegurl"},271 { ".m4v", "video/x-m4v"},272 { ".mov", "video/quicktime"},273 { ".mp2", "audio/x-mpeg"},274 { ".mp3", "audio/x-mpeg"},275 { ".mp4", "video/mp4"},276 { ".mpc", "application/vnd.mpohun.certificate"},277 { ".mpe", "video/mpeg"},278 { ".mpeg", "video/mpeg"},279 { ".mpg", "video/mpeg"},280 { ".mpg4", "video/mp4"},281 { ".mpga", "audio/mpeg"},282 { ".msg", "application/vnd.ms-outlook"},283 { ".ogg", "audio/ogg"},284 { ".pdf", "application/pdf"},285 { ".png", "image/png"},286 { ".pps", "application/vnd.ms-powerpoint"},287 { ".ppt", "application/vnd.ms-powerpoint"},288 { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},289 { ".prop", "text/plain"},290 { ".rc", "text/plain"},291 { ".rmvb", "audio/x-pn-realaudio"},292 { ".rtf", "application/rtf"},293 { ".sh", "text/plain"},294 { ".tar", "application/x-tar"},295 { ".tgz", "application/x-compressed"},296 { ".txt", "text/plain"},297 { ".wav", "audio/x-wav"},298 { ".wma", "audio/x-ms-wma"},299 { ".wmv", "audio/x-ms-wmv"},300 { ".wps", "application/vnd.ms-works"},301 { ".xml", "text/plain"},302 { ".z", "application/x-compress"},303 { ".zip", "application/x-zip-compressed"},304 { "", "*/*"}305 };306 }
最后补充一下,布局文件中的dimension是编译到jar包里面去了的,没有jar包的童鞋可以改成自己定义大小。。