您的位置:首页 > 移动开发 > Android开发

android开发:自定义ListView总结

2015-10-12 09:51 211 查看

android开发:自定义ListView总结

1.ListView

2.默认的ListView使用方法
效果图如下:



使用自定义的布局文件,可以显示文本(如上图的1,2,3,4.。。。)。
String[] myStringArray={"1","2","3","4","1","2","3","4","1","2","3","4"};
/*新建ArrayAdapter,类型为String,
* 第一个参数为context,即当前,
* 第二个参数为list每个项目的布局文件,android自带的,
* 第三个参数为要传递的数据,也就是上面定义的字符串数组
*
* */
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);
//通过findViewById找到住布局文件中的listView
ListView listView = (ListView) findViewById(R.id.list);
//绑定Adapter
listView.setAdapter(adapter);


主布局文件代码如下:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.filechooselistadapter.MainActivity" >

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/text"/>
</RelativeLayout>


2.ListView的自定义方法

1.定义主布局文件(main_layout.xml)其中ListView的属性;准备自定义的ListView的布局
2.获取ListView每一项的数据
3.通过Adapter(可自己定义)来绑定数据和ListView
下面来看具体的实现:

2.1实现效果





2.2布局文件

主布局文件
从上到下分三部分:显示当前路径的TextView、显示文件的ListView,执行操作的buttons
activity_main.xml
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.filechooselistadapter.MainActivity" >

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
/>
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="50sp"
android:layout_below="@id/text"
/>
<LinearLayout
android:layout_marginBottom="15dp"
android:layout_width="fill_parent"
android:layout_height="40sp"
android:orientation="horizontal"
android:layout_alignParentBottom="true" >
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/back"
style="?android:attr/buttonBarButtonStyle"/>

<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/confirm" />
</LinearLayout>

</RelativeLayout>
,定义好主布局文件后,需要定义ListView每个item的布局文件,list_view_layout_detail.xml
<?xml version="1.0" encoding="utf-8"?>

<!--
<LinearLayout 线性布局,
方向水平,
设置最低高度50sp,使得ListView每一行间距适中
/>
<imageView 放置图像(ic_folde/ic_file),
contentDescription:当图像没能正确加载时显示文字;
layout_gravity设置为垂直居中;
paddingRight向右间隔3个相对像素
/>
<TextView 放置文本(当前文件名称)
layout_gravity设置为垂直居中;
/>
<LinearLayout 线性布局,
填充父容器
设置水平靠右
<imageView 放置图像(ic_folde/ic_file),
contentDescription:当图像没能正确加载时显示文字;
layout_gravity设置为垂直居中;
paddingRight向右间隔3个相对像素
/>
/>
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:minHeight="50sp" >
<ImageView
android:id="@+id/imgView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="40sp"
android:maxHeight="40sp"
android:contentDescription="@string/img_view_text"
android:layout_gravity="center_vertical"
android:paddingLeft="1sp"
android:paddingRight="3sp"
/>
<TextView
android:id="@+id/text_file_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="normal"
android:layout_gravity="center_vertical"
android:paddingLeft="3sp"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="right"
android:layout_weight="2"
android:minWidth="20sp"
>
<ImageView
android:id="@+id/imgView_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="40sp"
android:maxHeight="40sp"
android:contentDescription="@string/img_view_text"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8sp"
android:layout_marginRight="8dp"
/>
</LinearLayout>
</LinearLayout>

2.3 定义自己的Adapter

关于Adapter的相关内容可以自行搜索。
new一个class,(高能预警 多代码,具体其实完全可以直接查看源文件)
MyListAdapter

package com.example.filechooselistadapter;

/*
* 更新自定义ListView的顺序
* 1.更新String []mapKey
* 2.更新Class ViewHolder
* 3.更新function getView()
* 4.更新function updateList()
* 5.更新Class MainActivity->function setData()
*
* 封装很重要啊!
* */

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.view.LayoutInflaterFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class MyListAdapter extends BaseAdapter{

private Context context=null;
//每一个map中的数据填充一个ListView的item
private ArrayList<HashMap<String,Object>> list=null;
private LayoutInflater layoutInflater=null;
/**
* list保存map,map存放的Object,
*/
public static final String [] mapKey={"image","text_file_info","file_info","image_select"};
private FileInfo currentFi=null;
/**
* 点击选择的文件的集合,上传文件时,调用此变量即可
*/
private HashSet<FileInfo> selectFileSet=null;
/**
* 这里仿照的是SimpleAdapter的形参列表
* @param context 获取上下文
* @param list 要传入的数据
* @param 设置当前的所在文件夹
*/
public MyListAdapter(Context context,
ArrayList<HashMap<String, Object>> list,FileInfo currentFi) {
super();
this.context = context;
this.list = list;
this.layoutInflater=LayoutInflater.from(context);
this.currentFi=currentFi;
this.selectFileSet=new HashSet<FileInfo>();
}

/**
* list 中数据的数量
*/
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}

/**
* 获取list中相应position的map
*/
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

/**
* 为了为convertView设置附加信息Tag,这里创建一个内部类ViewHolder,用于盛放一行中所有控件的引用,将这些引用
* 实例化后作为convertView的附加信息。
*/
class ViewHolder{
public ImageView imgView=null;
public TextView tx=null;
public ImageView imgView_select=null;
/*注意View和Activity都属于容器类,都需要设置布局文件,内部都含有子控件,且都有findViewById()
* 他们之间没有明显的继承关系
*/
public ViewHolder(){};
public ViewHolder(View convertView){
imgView=(ImageView)convertView.findViewById(R.id.imgView);
tx=(TextView)convertView.findViewById(R.id.text_file_info);
imgView_select=(ImageView)convertView.findViewById(R.id.imgView_select);
}
}

/**
*
* @author xcw
*每个item被点击过后执行的侦听函数的类参数,建议还是在Activity中绑定到ListView上
*/
class ImageListener implements OnClickListener{
private int position=0;
public ImageListener(int p){
this.position=p;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}

}

/**
* getView方法为系统在绘制每一行时调用,在此方法中要设置需要显示的文字,图片,
* 以及为按钮设置监听器。
*
* 形参意义:
* position:当前绘制的item 的位置(ID);
* convertView,系统在绘制ListView时,如果是绘制第一个Item(即第一行),convertView为null,当
* 绘制第二个及以后的Item的convertView不为空,这时可以直接利用这个convertView的getTag()方法,获得各控件
* 的实例,并进行相应的设置,这样可以加快绘图速度。
* 这个也是ListViewAdapter最重要的函数
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
/**
* 首先判断是不是第一次创建Item,若是,则创建convertView实例和ViewHolder对象,并通过fandViewById()方法
* 获得每一行中所有空间的实例放在ViewHolder对象中,然后对convertView设置标签
*/
ViewHolder vh=new ViewHolder();
if(convertView==null){
//获取每个item的布局
convertView=layoutInflater.inflate(R.layout.list_view_layout_detail, null);
vh=new ViewHolder(convertView);
convertView.setTag(vh);
}
else{
vh=(ViewHolder)convertView.getTag();
}
//绑定数据
vh.imgView.setBackgroundResource((Integer)list.get(position).get(mapKey[0]));
vh.tx.setText((String)list.get(position).get(mapKey[1]));
vh.imgView_select.setBackgroundResource((Integer)list.get(position).get(mapKey[3]));
//convertView.setOnClickListener(new ImageListener(position));
return convertView;
}

//将扫描FileInfo下所有的文件及文件夹,更新至list视图
public void updateList(FileInfo fi){
currentFi=fi;
if(list!=null)
list.clear();
File currentFile=new File(fi.getFilePath());
File []childFiles=currentFile.listFiles();
HashMap<String, Object> map=null;
for(int i=0;(childFiles!=null&&i<childFiles.length);i++){
map=new HashMap<String, Object>();
FileInfo myFI=new FileInfo(childFiles[i].getAbsolutePath(),
childFiles[i].getName(),childFiles[i].isDirectory());
if(myFI.isDirectory()){
map.put(mapKey[0], R.drawable.ic_folder);
}
else
map.put(mapKey[0], R.drawable.ic_file);

map.put(mapKey[1], myFI.getFileName());
map.put(mapKey[2], myFI);
if(!myFI.isDirectory())
map.put(mapKey[3], R.drawable.ic_circle_unselect);
else
map.put(mapKey[3], R.drawable.arrow_right);
list.add(map);
}
//按照文件名称排序
Collections.sort(list,new Comparator<HashMap<String, Object>>() {

@Override
public int compare(HashMap<String, Object> lhs, HashMap<String, Object> rhs) {
// TODO Auto-generated method stub
return lhs.get(mapKey[1]).toString().compareTo(rhs.get(mapKey[1]).toString());
}
});
/**
* 调用此函数,当adapter的数据改变时,通知与其绑定的ListView更新视图
*/
notifyDataSetChanged();
}
//当点击文件时,调用此函数。将文件后面的“圆圈”换成“对勾”
public void updateItemSelect(int position){
FileInfo fi=(FileInfo)list.get(position).get(mapKey[2]);
if(!fi.isDirectory()){
HashMap<String, Object> map=list.get(position);
if(!selectFileSet.contains((FileInfo)map.get(mapKey[2]))){
selectFileSet.add((FileInfo)map.get(mapKey[2]));
map.put(mapKey[3], R.drawable.ic_circle_ok);
}
else{
selectFileSet.remove((FileInfo)map.get(mapKey[2]));
map.put(mapKey[3], R.drawable.ic_circle_unselect);
}
notifyDataSetChanged();
}
}

public FileInfo getCurrentFi(){
return this.currentFi;
}
}


关于FileInfo类,查看源代码即可。就是一个保存文件信息的实体类。

2.4主程序使用自定义Adapter

然后,主程序中要做的事情就是
1.扫描文件夹,把文件绑定到list中
2.将控件和变量绑定
3.将Adapter绑定到ListView控件上
4.设置ListView的item和buttons的监听函数

package com.example.filechooselistadapter;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.example.filechooselistadapter.MyListAdapter.ViewHolder;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

ArrayList<HashMap<String,Object>> list=null;
HashMap<String,Object> map=null;
String initPath=null;
MyListAdapter mla=null;

ListView listView=null;
TextView text_file_path=null;
Button btn_back=null;
Button btn_confirm=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//获取顶端显示路径的TextView
text_file_path=(TextView)findViewById(R.id.text);
//设置最初的路径为根目录
initPath=Environment.getExternalStorageDirectory().getAbsolutePath();
File f=new File(initPath);
FileInfo initFi=new FileInfo(f.getAbsolutePath(), f.getName(), f.isDirectory());
//为list分配空间
list=new ArrayList<HashMap<String,Object>>();

//通过findViewById找到住布局文件中的listView
listView = (ListView) findViewById(R.id.list);
//new自定义的Adapter
mla=new MyListAdapter(this,list,initFi);
//初始更新Adapter的数据
mla.updateList(initFi);

//设置当ListView被点击后的事件监听,更新当前目录
listView.setOnItemClickListener(myItemClickListener);

//绑定Adapter
listView.setAdapter(mla);
//显示路径
text_file_path.setText(mla.getCurrentFi().getFilePath());

//点击返回返回上层目录
btn_back=(Button)findViewById(R.id.btn_back);
btn_back.setOnClickListener(new ButtonBackClickListener());
//点击确定
btn_confirm=(Button)findViewById(R.id.btn_confirm);
btn_confirm.setOnClickListener(new ButtonConfirmClickListener());
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
*ListView 的item被点击后执行
*如果是文件夹,则扫描,更新数据,更新视图update
*如果是文件,将后面的“圆圈”改成“对勾”加入选择文件的set中
*/
private AdapterView.OnItemClickListener myItemClickListener =new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
//获取点击的位置的文件信息
FileInfo fi=(FileInfo)((HashMap<String, Object>)mla.getItem(position))
.get(MyListAdapter.mapKey[2]);
//设置路径
text_file_path.setText(fi.getFilePath());
//Toast.makeText(MainActivity.this,position+"listview is clicked" , Toast.LENGTH_SHORT).show();
//如果是文件夹,更新ListView
if(fi.isDirectory())
mla.updateList(fi);
//如果是文件
else{
mla.updateItemSelect(position);
}
}
};
private class ButtonBackClickListener implements View.OnClickListener{

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//在此填写点击返回按钮后要执行的代码
// TODO Auto-generated method stub
File f=new File(mla.getCurrentFi().getFilePath());
//如果不是到根目录,则返回父文件夹
if(!f.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getAbsolutePath())){
File f_parent=f.getParentFile();
FileInfo fi_parent=new FileInfo(f_parent.getAbsolutePath(),f_parent.getName(),f_parent.isDirectory());
mla.updateList(fi_parent);
text_file_path.setText(fi_parent.getFilePath());
}
//否则,弹出确认对话框是否退出
else{
new AlertDialog.Builder(MainActivity.this).setTitle("确认退出吗?")
.setIcon(android.R.drawable.ic_dialog_info)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// 点击“确认”后的操作
MainActivity.this.finish();
}
})
.setNegativeButton("返回", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// 点击“返回”后的操作,这里不设置没有任何操作
}
}).show();
}

}

}

private class ButtonConfirmClickListener implements View.OnClickListener{

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//在此填写点击确认按钮后要执行的代码
}

}
}


。最后运行即可

3.一些开发过程中可能遇到的问题

1.和实例代码完全一样,但是eclipse却提示报错?
一般有两种情况:第一是工程依赖的包不同。到工程上右键,build path,看看是否缺少依赖的包。这个工程需要android-support-v4.jar包
第二种是在程序文件收,import的包不同,注意比较。
2.增加或修改了XML控件的id,但是程序中findViewById(R.id.xxx)却报错
这时只需要在工程,右键,build project(或者在菜单栏 project-》build project即可)

最后附上源代码github地址: https://github.com/summerspringwei/FileChooseListAdapter.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: