先完成布局设置
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="当前位置: /mnt/sdcard" />
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="8"
android:cacheColorHint="#00000000" >
</ListView>
</LinearLayout>
由于显示的ListView中有图标的存在,因此需要声明一个自定义的Adapter来处理,也就是需要建立一个行的布局文件。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/file_img"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/file_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5" >
</TextView>
</LinearLayout>
需要根据屏幕高度动态设置每一行的高度,因此需要计算高度,直接使用之前的Globals类即可。
编写自定义Adapter
public class FileAdapter extends BaseAdapter {
private Context ctx;
private List<Map<String, Object>> allValues;
public FileAdapter(Context ctx, List<Map<String, Object>> allValues) {
this.ctx = ctx;
this.allValues = allValues;
}
@Override
public int getCount() {
return allValues.size();
}
@Override
public Object getItem(int position) {
return allValues.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(ctx).inflate(R.layout.file_line,
null);
// 需要动态设置高度
convertView.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT, Globals.SCREEN_HEIGHT / 9));
}
// 处理内容的设置
TextView fileImg = (TextView) convertView.findViewById(R.id.file_img);
fileImg.getLayoutParams().height = Globals.SCREEN_HEIGHT / 9;
TextView fileName = (TextView) convertView.findViewById(R.id.file_name);
// 取得数据
Map<String, Object> map = allValues.get(position);
fileName.setText(map.get("fileName").toString());
// 设置图片,根据传入的扩展名, 在Globals中的Map集合里查找该扩展名对应的文件图片id,并设置为这张图.
fileImg.setBackgroundResource(Globals.allImgsMap.get(map.get("extName")
.toString()));
return convertView;
}
}
下面需要编写程序来读取SD卡下的文件目录,并列表显示出来,这需要用到IO操作,以及Android读取SD卡的操作。
1.1、SD卡读取操作可以通过Environment类来动态取得扩展设备的所在目录,再通过listFiles方法列出目录下的所有文件
public class MainActivity extends Activity {
private TextView titleText;
private ListView list;
private List<Map<String, Object>> allValues = new ArrayList<Map<String, Object>>();
private FileAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Globals.init(this);
setContentView(R.layout.activity_main);
// 取得组件
titleText = (TextView) findViewById(R.id.title_text);
list = (ListView) findViewById(R.id.list);
// 读取SD卡,取得扩展设备的根目录
File root = Environment.getExternalStorageDirectory();
// 列出该目录下的所有文件
File[] allFiles = root.listFiles();
for (int i = 0; i < allFiles.length; i++) {
File f = allFiles[i];
Map<String, Object> map = new HashMap<String, Object>();
// 保存文件或文件夹的名称
map.put("fileName", f.getName());
// 判断是文件还是文件夹
if (f.isDirectory()) {
// 文件夹
String extName = "dir_close";
map.put("extName", extName);
} else {
// 文件
// 直接将文件的扩展名加入到map中即可.
String extName = f.getName()
.substring(f.getName().lastIndexOf(".") + 1)
.toLowerCase();
map.put("extName", extName);
}
// 将map集合加入到list中
allValues.add(map);
}
// 建立Adapter
adapter = new FileAdapter(this, allValues);
list.setAdapter(adapter);
}
}
下面实现点击查看功能,当点某一个文件夹时,可以展开这个文件夹,来查看该文件夹里面的数据。
也就是要将List集合中的数据重新加载。
最好将读取文件夹中文件的功能封装成一个方法。
为了更方数据的调用,需要将操作方法封装,方便从目录下读取所有文件列表
private void loadFileData(File parentDir) {
// 列出该目录下的所有文件
File[] allFiles = parentDir.listFiles();
for (int i = 0; i < allFiles.length; i++) {
File f = allFiles[i];
Map<String, Object> map = new HashMap<String, Object>();
// 保存文件或文件夹的名称
map.put("fileName", f.getName());
// 保存文件的整体路径
map.put("fullPath", f.getAbsolutePath());
// 判断是文件还是文件夹
if (f.isDirectory()) {
// 文件夹
String extName = "dir_close";
map.put("extName", extName);
map.put("dirFlag", true);
} else {
// 文件
// 直接将文件的扩展名加入到map中即可.
String extName = f.getName()
.substring(f.getName().lastIndexOf(".") + 1)
.toLowerCase();
map.put("extName", extName);
map.put("dirFlag", false);
}
// 将map集合加入到list中
allValues.add(map);
}
}
加入事件监听,完成读取某一个目录下所有文件的功能
// 加入监听
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// 取得当前点击的项目对应的数据
Map<String, Object> map = allValues.get(arg2);
// 判断点的是否是文件夹
boolean dirFlag = (Boolean) map.get("dirFlag");
if (dirFlag) {
// 是文件夹
// 读取绝对路径
String fullPath = (String) map.get("fullPath");
// 建立该文件对象
File dir = new File(fullPath);
// 将 该文件夹 下的所有文件读取到List集合中,先将原有数据清空
allValues.clear();
// 添加数据
loadFileData(dir);
// 如果只修改数据,界面不会得到改变的通知,当用户再进行操作时,会出现错误.
// 这里就需要通知界面进行修改.
adapter.notifyDataSetChanged();
}
}
});
还需要补充一个返回上一级目录的功能。
// 加入监听
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// 取得当前点击的项目对应的数据
Map<String, Object> map = allValues.get(arg2);
// 判断点的是否是文件夹
boolean dirFlag = (Boolean) map.get("dirFlag");
if (dirFlag) {
// 是文件夹
// 读取绝对路径
String fullPath = (String) map.get("fullPath");
// 建立该文件对象
File dir = new File(fullPath);
// 将 该文件夹 下的所有文件读取到List集合中,先将原有数据清空
allValues.clear();
// 判断当前是哪个目录,如果已经是SD卡的跟目录,就不应该再加入返回上一级的操作
if (!Environment.getExternalStorageDirectory()
.getAbsolutePath().equals(fullPath)) {
// 加入一个返回上一级的数据
Map<String, Object> parentData = new HashMap<String, Object>();
parentData.put("fileName", "返回上一级");
parentData.put("extName", "dir_open");
parentData.put("dirFlag", true);
parentData.put("fullPath", dir.getParent());
allValues.add(parentData);
}
// 添加数据
loadFileData(dir);
// 如果只修改数据,界面不会得到改变的通知,当用户再进行操作时,会出现错误.
// 这里就需要通知界面进行修改.
adapter.notifyDataSetChanged();
}
}
});
1.2、加入普通对话框
在进行文件操作时,如果点击的不是文件夹,而是文件,想实现查看当前点击文件的详情的功能,这就要通过对话框来弹出提示了。
需要使用AlertDialog类来完成对话框。
但建立对话框需要使用Builder类来完成。
// 点的是文件, 想弹出一个对话框
Builder builder = new Builder(MainActivity.this);
// 对话框标题
builder.setTitle("文件详情");
// 内容
builder.setMessage("文件大小: " + file.length() + "\r\n"
+ "文件名: " + file.getName());
// 按钮
builder.setNegativeButton("关闭", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 该事件自带一个功能, 可以自动关闭对话框
}
});
// 显示这个对话框
builder.create().show();
按钮如果有多个,使用Builder可以最多加入三个按钮,其中中间的按钮位置固定的,但确定和取消的位置不确定。
加入长按删除功能,当要删除文件时,先弹出提示,确定还是取消,然后再删除。
list.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
final int arg2, long arg3) {
// 取得要删除的数据
Map<String, Object> map = allValues.get(arg2);
// 取得文件路径
final String path = (String) map.get("fullPath");
boolean dirFlag = (Boolean) map.get("dirFlag");
if (!dirFlag) {
// 文件时可以提示删除
Builder builder = new Builder(MainActivity.this);
builder.setTitle("提示");
builder.setMessage("确定要删除该文件(" + map.get("fileName")
+ ")吗?");
builder.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 将文件夹中的文件真正删除
File f = new File(path);
if (f.exists()) {
f.delete();
}
// 将列表中的这条数据也删除
allValues.remove(arg2);
// 通知界面进行改变
adapter.notifyDataSetChanged();
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
return false;
}
});
注:
测试时发现无法真正的删除文件,因为该应用没有声明操作SD卡写出的权限。
因此需要在AndroidManifest.xml中进行声明。
1.3、返回键监听在编写应用程序时,一般点“返回”键并不会直接退出系统,而是根据用户当前的状态提示信息,来决定是否退出,为了让用户长时间留在应用程序中。
通过在Activity中覆写OnKeyDown的方法来监听用户按键的情况。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 根据keyCode判断用户按的是哪个键.
if (keyCode == KeyEvent.KEYCODE_BACK) {
// 判断当前List集合中的第一条数据是否是"返回上一级"
if ("TRUE".equals(allValues.get(0).get("real"))) {
// 直接返回上一级
list.performItemClick(list.getChildAt(0), 0, list.getChildAt(0)
.getId());
} else {
// 要真的退出
// 弹出对话框
Builder builder = new Builder(this);
builder.setTitle("提示");
builder.setMessage("亲,真的要退出吗?");
builder.setPositiveButton("真的", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 结束当前Activity程序
finish();
}
});
builder.setNegativeButton("暂不", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
}
// 取消返回键原有的作用
return false;
}
return super.onKeyDown(keyCode, event);
}