扩展BaseAdapter实现在ListView中浏览文件
2010-12-27 10:26 乱世文章 阅读(384) 评论(0) 编辑 收藏 举报
我们可以在一个普通的ListView中列出指定目录下的所有文件,每个文件列出该文件的文件名和文件图标,在每个文件名前面有一个checkbox按钮,用户可对该文件进行选择(支持多选),并实现某些操作(如打开、删除功能):
实现步骤如下。
1、新建类FileInfo
package ydtf.listview.filebrowser;
public class FileInfo {
public String path; //文件路径
public String fileName; //文件名
public String type; //文件类型
public boolean checked; //是否选中
public int imageId; //图片资源id
public FileInfo(String path,String fileName,String type,boolean checked,int imageId) {
this.path = path;
this.fileName = fileName;
this.type=type;
this.checked = checked;
this.imageId=imageId;
}
}
这是一个pojo类,除了我们定制的构造器外。它只用于表示ListView中列出的一个文件对象,为简便起见没有使用getter/setter方法。
2、新建一个layout文件file_info.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="horizontal" android:minHeight="40px"
android:layout_gravity="center_vertical">
<CheckBox android:id="@+file_info/chk_check" android:focusable="false"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="35px" android:checked="false"/>
<ImageView android:id="@+file_info/iv_icon" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="center_vertical" />
<TextView android:id="@+file_info/tv_filename" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textColor="?android:attr/textColorPrimary"
android:paddingLeft="3px" android:layout_gravity="center_vertical" />
</LinearLayout>
仅包含了3个widgets,分别用于显示FileInfo对象的部分属性,作为概念演示程序,向用户展示文件的3个属性就足够了。
3、实现adapter类FileInfoAdaper:
package ydtf.listview.filebrowser;
import java.util.List;
import com.ydtf.android.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
public class FileInfoAdapter extends BaseAdapter{
private LayoutInflater layoutInflater; //用于从xml文件加载listview的layout
private Context ctx; //容器/activity
private List<FileInfo> fileInfoList; //所有item
public FileInfoAdapter(Context ctx,List<FileInfo> list){
this.ctx=ctx;
fileInfoList=list;
layoutInflater=LayoutInflater.from(ctx);
}
@Override
public int getCount() {
return fileInfoList.size();
}
@Override
public Object getItem(int position) {
return fileInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder=null; //清空临时变量
if (convertView == null) { //若行未初始化
convertView = layoutInflater.inflate(
R.layout.file_info, null); //通过flater初始化行视图
holder = new ViewHolder(); //并将行视图的3个子视图引用放到tag中
holder.itemIcon = (ImageView) convertView.findViewById(R.file_info.iv_icon);
holder.itemText = (TextView) convertView.findViewById(R.file_info.tv_filename);
holder.itemCheckBox = (CheckBox) convertView.findViewById(R.file_info.chk_check);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag(); //若行已初始化,直接从tag属性获得子视图的引用
}
FileInfo info = fileInfoList.get(position); //获得行数据(模型)
if (info != null) { //根据模型数据,设置行视图的控件值
holder.itemText.setText(info.fileName);
Drawable draw = this.ctx.getResources().getDrawable(
info.imageId);
holder.itemIcon.setImageDrawable(draw);
holder.itemCheckBox.setChecked(info.checked);
}
return convertView;
}
static class ViewHolder{
CheckBox itemCheckBox;
TextView itemText;
ImageView itemIcon;
}
}
在android中,采用了所谓适配器的概念。这有点像mvc。一个组件,需要由mvc三部分构成才能正常工作,在这里,m是FileInfo-提供了要展示的(一行)数据,v是file_info.xml-提供了要显示的(一行)布局,c则是适配器(整个ListView,多行),把数据m通过一定的逻辑进行转换并有选择地提供给视图v展示。
BaseAdapter是一个抽象类,我们需要继承它,实现它的4个方法,以便向ListView提供数据。
当然,最关键的方法是getView方法。我们需要在其中构建ListView的一行View,初始化这个View中需要展示的所有数据,并返回这个View。
4、接下来,在一个Activity中应用上面所有的3个东西:m、v和c。
这需要做两件事,一个xml,一个Activity。
首先做第1件事,新建一个布局文件downloadfilelist.xml,你可以用DroidDraw来干这个或者手工编写 :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<!-- 顶部工具栏 对齐顶部-->
<RelativeLayout
xmlns:Android="http://schemas.android.com/apk/res/android"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:layout_alignParentTop="true">
<include layout="@+layout/filebrowser_top" />
</RelativeLayout>
<!-- 下部的列表 对齐到工具栏下方-->
<ListView
android:id="@+file_browser/lvFileList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
这个layout里,我们又引用了另外一个布局文件filebrowser_top.xml。这是一个工具栏,上边放了两个按钮:
<RelativeLayout
xmlns:Android="http://schemas.android.com/apk/res/android"
Android:background="@drawable/top"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
><ImageView
Android:id="@+file_browser/imgCheck"
Android:layout_toLeftOf="@+file_browser/imgClose"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_marginRight="10px"
Android:src="@drawable/checked"
Android:layout_centerVertical="true"
>
</ImageView>
<ImageView
Android:id="@+file_browser/imgClose"
Android:layout_alignParentRight="true"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_marginRight="10px"
Android:src="@drawable/close"
Android:layout_centerVertical="true"
>
</ImageView>
</RelativeLayout>
接下来,我们需要做第2件事情,新建一个Activity,在其中放上我们的各个布局:
public class ScanDownloadFile extends Activity{
private static final String tag ="ScanDownloadFile";
private ArrayList<FileInfo> listItem;
private String dir=Environment.getExternalStorageDirectory()+"/downloads/";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取配置文件,检查用户是否已登录
showView();
}
private void showView() {
setContentView(R.layout. downloadfilelist);
// 获取intent的参数
Bundle bundle = getIntent().getExtras();
String itemtitle = bundle.getString("data");
setTitle(itemtitle);
// 构造ListView
ListView listview = (ListView) findViewById(R.file_browser.lvFileList);
// 生成动态数组,加入数据
listItem = getFiles();
// 构造适配器,指明数据源字段与listitem中子控件的对应关系
FileInfoAdapter adapter = new FileInfoAdapter(this,listItem);
// 添加适配器,进行绑定
listview.setAdapter(adapter);
// 添加监听器,处理item点击事件
listview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setAction(android.content.Intent.ACTION_VIEW);
FileInfo info=listItem.get(arg2);
File file = new File(dir+info.fileName);
String type=typeof(info.fileName);
if(type!=null){
intent.setDataAndType(Uri.fromFile(file), info.type);
Log.d(tag,"uri:"+Uri.fromFile(file)+",type:"+info.type);
startActivity(Intent.createChooser(intent, "选择一个应用程序去打开它"));
}else
Toast.makeText(QxtScanDownloadFile.this, "不能识别的文件类型", Toast.LENGTH_SHORT);
}
});
}
private String typeof(String file){
String[] arr;
//文本文件
arr=this.getResources().getStringArray(
R.array.text);
for(String each:arr){
if(file.toLowerCase().endsWith(each)){
Log.i(tag,"each:"+each);
return "txt/*";
}
}
//图片文件
arr=this.getResources().getStringArray(
R.array.image);
for(String each:arr){
if(file.toLowerCase().endsWith(each)){
return "image/*";
}
}
//视频文件
arr=this.getResources().getStringArray(
R.array.video);
for(String each:arr){
if(file.toLowerCase().endsWith(each)){
return "video/*";
}
}
//音频文件
arr=this.getResources().getStringArray(
R.array.audio);
for(String each:arr){
if(file.toLowerCase().endsWith(each)){
return "audio/*";
}
}
//word文件
arr=this.getResources().getStringArray(
R.array.word);
for(String each:arr){
if(file.toLowerCase().endsWith(each)){
return "application/msword";
}
}
return null;
}
private int iconof(String type){
return R.drawable.icon;
}
private ArrayList<FileInfo> getFiles(){
ArrayList<FileInfo> array=new ArrayList<FileInfo>();
File file=new File(dir);
File[] files=file.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isFile()){//过滤目录,只显示文件
String name=files[i].getName();
String type=typeof(name);
FileInfo info=new FileInfo(dir,name,type,false,iconof(type));
array.add(info);
}
}
return array;
}
}
其中,我们加载了布局,通过getFiles方法读取了sd卡某个目录下的内容,对ListView应用了我们前面定义的适配器FileInfoAdapter,用typeof方法对每个文件的类型进行了识别,并响应了ListView的OnItemClick事件,选择合适的程序打开每个文件。
程序运行的效果如下:
5、现在,程序还没有响应任何特殊的事情。需要做一件事情:当checkbox被改变时,我们需要同时改变对应的FileInfo对象的checked属性。这需要修改适配器的getView方法,在以下代码处进行如下修改:
if (info != null) { //根据模型数据,设置行视图的控件值
⋯⋯(省略部分代码)
//在checkbox中保存一个FileInfo对象的引用
holder.itemCheckBox.setTag(info);
//为checkbox增加onClick事件的处理
holder.itemCheckBox.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
//从tag中取出FileInfo的引用 FileInfo fi=(FileInfo)v.getTag();
//设置FileInfo的checked属性 fi.checked=((CheckBox)v).isChecked();
}
});
}
6、此外,我们还要为工具栏按钮增加响应的动作。打开ScanDownloadFile.java,在showView方法中增加代码:
//check按钮的事件处理
btnChecked=(ImageView)findViewById(R.file_browser.imgCheck);
btnChecked.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// Log.i(getClass()+"","down");
btnChecked.setBackgroundDrawable(getResources().getDrawable(R.drawable.sendsms_bk));
break;
case MotionEvent.ACTION_UP:
// Log.i(getClass()+"","up");
btnChecked.setBackgroundDrawable(getResources().getDrawable(R.drawable.sendsms_bk_clear));
showDialog(0);
break;
}
return true;//必须返回true,否则down事件后不会触发给up和move事件
}
});
因为check按钮实际上是一个imageView,我们只有通过在触摸事件中切换两张不同的图片来模拟按钮被按下、弹起的动作。同时我们在弹起事件中通过showDialog()触发一个弹出式对话框:
//创建activity托管对话框
protected Dialog onCreateDialog(int id) {
return new AlertDialog.Builder(this).setMessage("确定要删除所选文件吗?文件删除后将不能被恢复!")
.setPositiveButton("是",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFiles();
}
}).setNegativeButton("不", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismissDialog(0);//removeDialog(0);移除对话框
}
}
).create();
}
当用户点击“是”,才触发真正的删除文件方法deleteFiles():
private void deleteFiles(){
for(FileInfo each:listItem){
//遍历FileInfo对象的checked属性
if(each.checked){
Log.i(tag,”删除文件:”+each.fileName);
}
}
}
现在从文件列表中选择一些文件,点击按钮,后台将打印出所选择的文件列表(模拟删除文件操作)。