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
相关文章推荐
- Android打开文件代码
- android 获取版本号的方法(非Activity 类下也可以)
- android中的ellipsize设置(省略号的问题)
- ShareSDK for Android 2.3.8它已发表
- Android studio导入工程
- Android开源项目第二篇——工具库篇
- Android Apk打包过程概述_Android是如何打包apk的
- Android Studio初步使用及HelloWorld详解
- android应用打包成为安装包(常会出现的问题)
- Android NDK编程浅入深出之--Android.mk(3)
- Android NDK编程浅入深出之--Android.mk(2)
- Android Studio 模拟器对应键盘快捷键映射
- windows下Qt5.4.2 for android开发环境配置
- android 与servlet json数据交互
- android Drawable各种类型使用
- Android NDK编程浅入深出之--Android.mk
- Android 项目实践(一)——开发流程
- Android Material Design动画
- Android应用程序与SurfaceFlinger服务的连接过程分析
- android四大组件