适配器适配器实现过程:共3步:新建适配器-->添加数据源到适配器-->视图加载适配器适配器的数据源:ArrayAdapter:可以使数组或集合SimpleAdapter:只能是特定泛型的集合BaseAdapter:是一个抽象类,需要自己写一个类继承它。灵活性高,因为提供了4个方法去重写。其中有一个getView()方法,我们可以重写优化ListView的方法。SimpleAdapter是其子类。1.创建ArrayAdapter:public class Test1 extends Activity { private ListView mListView; private ArrayAdapterarr_Adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test1); //准备好视图,我们的目标ListView mListView = (ListView) findViewById(R.id.id_listView); //准备好数据源(数组或集合) String [] arr = {"Android_1","Android_2","Android_3","Android_4"}; //ArrayAdapter传入3个参数: 上下文,当前ListView加载的“每一个列表项所对应的布局文件”,数据源 //此例用了一个自带的布局文件,Ctrl+左键可以看到这其实是一个简单的TextView,ArrayAdapter功能有限,每个列表项布局只能是TextView arr_Adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, arr); mListView.setAdapter(arr_Adapter); }}2.创建SimpleAdapterSimpleAdapter不同ArrayAdapter每一个列表项只能显示TextView,它每一个列表项可以自己定义各种内容,所以一般需要自己创建布局。res/layout/item.xml:代码部分:(1)根据需要定义ListView每一个列表项所实现的布局(上面已写)。(2)定义一个HashMap构成的列表,将数据以键值对的方式存放在里面。(3)构造SimpleAdapter对象。(4)将LsitView绑定到SimpleAdapter上。public class Test2 extends Activity { private ListView mListView; private SimpleAdapter sim_Adapter; private List> datalist; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test2); //准备好视图 mListView = (ListView) findViewById(R.id.id_listView); //准备好数据源 datalist = new ArrayList>(); /** SimpleAdapter传入3个参数: * Context:上下文 * data:数据源(List> data),是一个特定泛型的集合。简单讲就是Map组成的List集合。 * 每一个Map<>对应ListView列表中的一行。 * 每一个Map<>(key-value)中的key必须包含所有在from中指定的key * 因为SimpleAdapter每一个列表项中既要放图片又要放文字,所以需要一个Map<>组合起来,不同于ArrayAdapter只有文字,数据源是简单的List集合。 * resource:列表项布局文件的id * from:Map<>中的键; new String [] * to:填充那些组件(在列表项的布局文件中定义的) new int [] */ sim_Adapter = new SimpleAdapter(this, getData(), R.layout.item, new String []{"pic","text"}, new int[]{R.id.pic, R.id.text}); mListView.setAdapter(sim_Adapter); } //通过方法返回数据源,更加灵活 private List> getData() { for(int i= 0 ; i<20; i++){ Mapdata = new HashMap(); //这里每一Map的键就是第4个参数声明的键,每一个Map的值就是第5个参数声明的组件类型,只不过此处要传入组件具体的显示内容 data.put("pic", R.drawable.ic_launcher); data.put("text", "Android"+i ); //遍历每一个Map,添加到List集合中去 datalist.add(data); } return datalist; } }3.创建BaseAdapter(及性能优化)使用BaseAdapter必须写一个类继承它,BaseAdapter是一个抽象类,继承它必须实现它的方法。BaseAdapter的灵活性就在于它要重写4种方法。原理:当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。重点是getView()方法 (常被用于优化):getView()在每一个列表项被滚动到屏幕内的时候被调用,然后new一个View出来作为列表项的布局。假如有10000个列表项,就new 10000次 View出来吗?这肯定会极大的消耗资源,导致ListView滑动非常的慢。convertView(每个列表项的布局,即View):ListView第一次显示的时候,此时convertView是null,getView()会加载一个屏幕所有列表项的View,加载完convertView便不为null。然后在ListView滑动的过程中,会有列表项被滑出屏幕,它所对应的布局convertView便不再给它使用。所以我们可以对convertView进行复用,把失去原主人(旧列表项)的convertView赋给新出来的列表项。View view;if (convertView == null){ view = inflater.inflate(R.layout.item_addmore, null);}else{ view = convertView;}ViewHolder上面的优化仅仅复用了View,但是要得到其中的控件,需要在控件的容器中通过findViewById的方法来获得。当convertView为null时,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder对象里。这样所有控件的实例都缓存在ViewHolder里。setTage(), getTag()实例缓存好了,怎么给View使用呢?当convertView为null时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为null,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById对控件的层层查询。public class MyAdapter extends BaseAdapter { ArrayListapk_list; LayoutInflater inflater; public MyAdapter(Context context, ArrayListapk_list) { this.apk_list = apk_list; this.inflater = LayoutInflater.from(context); } //返回值表示该Adapter共包含多个列表项 @Override public int getCount() { return apk_list.size(); } //返回值表示第position处的列表项的内容 @Override public Object getItem(int position) { return apk_list.get(position); } //返回值表示第position处的列表项的id @Override public long getItemId(int position) { return position; } //返回的view将作为列表框布局 @Override public View getView(int position, View convertView, ViewGroup parent) { ApkEntity entity = apk_list.get(position); ViewHolder holder; if (convertView == null){ holder = new ViewHolder(); convertView = inflater.inflate(R.layout.item_addmore, null); //得到各个控件的对象,并缓存到View中 holder.name_tv = (TextView) convertView .findViewById(R.id.item3_apkname); holder.des_tv = (TextView) convertView .findViewById(R.id.item3_apkdes); holder.info_tv = (TextView) convertView .findViewById(R.id.item3_apkinfo); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.name_tv.setText(entity.getName()); holder.des_tv.setText(entity.getDes()); holder.info_tv.setText(entity.getInfo()); return convertView; } //布局里的控件 class ViewHolder { TextView name_tv; TextView des_tv; TextView info_tv; }}监听器ListView中主要有2种监听器:OnItemClickListener: 处理视图中单个条目的点击事件。(OnItemSelectedListener,参数与其一致,只是监听用户手指行为不同)OnScrollListener:监听滚动变化,可以用于视图滚动中加载数据。关于OnItemClickListener中4个参数:举个例子解释:有X, Y两个listview,X里有1,2,3,4这4个item,Y里有a,b,c,d这4个item。如果点了b这个item,那么:1.parent 相当于listview Y适配器的一个指针,可以通过它来获得Y里装着的一切东西,再通俗点就是说告诉你,你点的是Y,不是X 。2.view 是你点b item的view的句柄,就是你可以用这个view,来获得b里的控件的id后操作控件。3.position是b在Y适配器里的位置(生成listview时,适配器一个一个的做item,然后把他们按顺序排好队,在放到listview里,意思就是这个b是第position号做好的)。4.id 是b在listview Y里的第几行的位置(很明显是第2行),大部分时候position和id的值是一样的。关于OnScrollListener中4个参数:滚动时一直回调,直到停止滚动时才停止回调。firstVisibleItem:当前能看见的第一个列表项ID(从0开始)visibleItemCount:当前能看见的列表项个数(小半个也算)totalItemCount:列表项共数 //判断是否滚到最后一行 if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) { isLastRow = true;在SimpleAdapter例子上,加上这两个监听器事件:public class Test2 extends Activity implements OnItemClickListener, OnScrollListener{ private ListView mListView; private SimpleAdapter sim_Adapter; private List> datalist; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test2); //准备好视图 mListView = (ListView) findViewById(R.id.id_listView); //准备好数据源 datalist = new ArrayList>(); /** SimpleAdapter传入3个参数: * Context:上下文 * data:数据源(List> data),是一个特定泛型的集合。简单讲就是Map组成的List集合。 * 每一个Map<>对应ListView列表中的一行。 * 每一个Map<>(key-value)中的key必须包含所有在from中指定的key * 因为SimpleAdapter每一个列表项中既要放图片又要放文字,所以需要一个Map<>组合起来,不同于ArrayAdapter只有文字,数据源是简单的List集合。 * resource:列表项布局文件的id * from:Map<>中的键; new String [] * to:填充那些组件(在列表项的布局文件中定义的) new int [] */ sim_Adapter = new SimpleAdapter(this, getData(), R.layout.item, new String []{"pic","text"}, new int[]{R.id.pic, R.id.text}); mListView.setAdapter(sim_Adapter); mListView.setOnItemClickListener(this); mListView.setOnScrollListener(this); } //通过方法返回数据源,更加灵活 private List> getData() { Mapdata = new HashMap(); for(int i= 0 ; i<20; i++){ //这里每一Map的键就是第4个参数声明的键,每一个Map的值就是第5个参数声明的组件类型,只不过此处要传入组件具体的显示内容 data.put("pic", R.drawable.ic_launcher); data.put("text", "Android"+i); //遍历每一个Map,添加到List集合中去 datalist.add(data); } return datalist; } @Override public void onItemClick(AdapterViewparent, View view, int position, long id) { String text = mListView.getItemIdAtPosition(position)+""; Toast.makeText(this, position +":" +text, Toast.LENGTH_SHORT).show(); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case SCROLL_STATE_FLING: Log.i("Main", "用户手指离开屏幕之前,由于用力滑了一下,视图仍继续向前滑动"); break; case SCROLL_STATE_IDLE: Log.i("Main", "视图已经停止滑动"); break; case SCROLL_STATE_TOUCH_SCROLL: Log.i("Main", "手指没有离开屏幕,视图正在滑动"); break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }}滚动到底部,刷新添加更多内容:1.自定义ListView//自定义一个ListView,在构造方法里就给底部添加一个布局(加载提示布局)public class LoadListView extends ListView implements OnScrollListener{ View footer;// 底部布局; boolean isLastRow; //最后一行 boolean isLoading ;// 正在加载; ILoadListener iLoadListener; public LoadListView(Context context) { super(context); initView(context); } public LoadListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public LoadListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } //添加底部加载提示布局到ListView private void initView(Context context) { LayoutInflater inflater = LayoutInflater.from(context); footer = inflater.inflate(R.layout.footer_layout, null); //设置底部布局一开始是隐藏的,只有当滚动到最低端才显示 footer.findViewById(R.id.load_layout).setVisibility(View.GONE); //给ListView添加底部布局 this.addFooterView(footer); this.setOnScrollListener(this); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //判断是否滚到最后一行 if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) { isLastRow = true; } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //当滚到最后一行且停止滚动时,显示布局,加载数据 if(isLastRow && scrollState == SCROLL_STATE_IDLE){ if(!isLoading){ isLoading = true; footer.findViewById(R.id.load_layout).setVisibility(View.VISIBLE); //(加载更多)使用接口回调的方式(当监听到滑动到底端,只需调用接口的onLoad方法,剩余的操作交给onLoad方法就可以了) iLoadListener.onLoad(); } } } public void setInterface(ILoadListener iLoadListener){ this.iLoadListener = iLoadListener; } //加载更多数据的回调接口 public interface ILoadListener{ public void onLoad(); } //加载完毕 public void loadComplete(){ isLoading = false; footer.findViewById(R.id.load_layout).setVisibility( View.GONE); }}2.自定义Adapterpublic class MyAdapter extends BaseAdapter { ArrayListapk_list; LayoutInflater inflater; public MyAdapter(Context context, ArrayListapk_list) { this.apk_list = apk_list; this.inflater = LayoutInflater.from(context); } //刷新ListView,更新UI public void onDateChange(ArrayListapk_list) { this.apk_list = apk_list; this.notifyDataSetChanged(); } //返回值表示该Adapter共包含多个列表项 @Override public int getCount() { return apk_list.size(); } //返回值表示第position处的列表项的内容 @Override public Object getItem(int position) { return apk_list.get(position); } //返回值表示第position处的列表项的id @Override public long getItemId(int position) { return position; } //返回的view将作为列表框布局 @Override public View getView(int position, View convertView, ViewGroup parent) { ApkEntity entity = apk_list.get(position); ViewHolder holder; if (convertView == null){ holder = new ViewHolder(); convertView = inflater.inflate(R.layout.item_addmore, null); //得到各个控件的对象,并缓存到View中 holder.name_tv = (TextView) convertView .findViewById(R.id.item3_apkname); holder.des_tv = (TextView) convertView .findViewById(R.id.item3_apkdes); holder.info_tv = (TextView) convertView .findViewById(R.id.item3_apkinfo); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.name_tv.setText(entity.getName()); holder.des_tv.setText(entity.getDes()); holder.info_tv.setText(entity.getInfo()); return convertView; } //布局里的控件 class ViewHolder { TextView name_tv; TextView des_tv; TextView info_tv; }}3.工具类public class ApkEntity {private String name;private String des;private String info;public String getName() { return name;}public void setName(String name) { this.name = name;}public String getDes() { return des;}public void setDes(String des) { this.des = des;}public String getInfo() { return info;}public void setInfo(String info) { this.info = info;}}4.注意主布局下的ListView改成自定义的ListView5.主代码public class Test3 extends Activity implements ILoadListener {private ArrayListapk_list = new ArrayList();private MyAdapter adapter;private LoadListView listView;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test3); getData(); showListView(apk_list);}private void getData() { for (int i = 0; i < 10; i++) { ApkEntity entity = new ApkEntity(); entity.setName("测试程序"); entity.setInfo("50w用户"); entity.setDes("这是很棒的应用!"); apk_list.add(entity); }}private void getLoadData() { for (int i = 0; i < 4; i++) { ApkEntity entity = new ApkEntity(); entity.setName("更多程序"); entity.setInfo("50w用户"); entity.setDes("这是一个神奇的应用!"); apk_list.add(entity); }}private void showListView(ArrayListapk_list) {
if (adapter == null) {
listView = (LoadListView) findViewById(R.id.id_listView);
listView.setInterface(this);
adapter = new MyAdapter(this, apk_list);
listView.setAdapter(adapter);
}else{
adapter.onDateChange(apk_list);
}
}
//正常情况下,不要加延时,此处只为演示
@Override
public void onLoad() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//获取更多数据
getLoadData();
//更新listview显示;
showListView(apk_list);
//通知listview加载完毕
listView.loadComplete();
}
}, 2000);
}
}