Android中自定义ListView

来源:互联网 发布:j2网络用语 编辑:程序博客网 时间:2024/06/05 17:49


Author : lovecicy

在使用Android的ListView的时候,Android系统自带了一些简单的布局,但是如果要做出比较复杂的显示列表——像新浪微博的微博列表,就需要对列表的显示进行自定义。

在自定义时,大致需要一下步骤:

一、在res/layout/文件下新建一个布局文件

假设新建一个文件名叫做custom_list_view.xml,此文件用于显示单个列表项,相当于显示一条微博,包含的内容有头像、微博内容、图片、转发数、评论数等。这个布局文件和Activity的布局文件并没有什么不同。

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:orientation="vertical"
  6. android:descendantFocusability="blocksDescendants">
  7.  
  8. <TextView
  9. android:id="@+id/large_text"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="@string/test"
  13. android:textAppearance="?android:attr/textAppearanceLarge" />
  14.  
  15. <TextView
  16. android:id="@+id/small_text"
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"
  19. android:text="@string/test2" />
  20.  
  21. <ImageView
  22. android:id="@+id/image"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"/>
  25. </LinearLayout>

二、覆写BaseAdapter类

Adapter的作用就是将数据集合中的每个条目取出,将条目内的每个数据项放入到对应的控件中。最主要的方法是getView,在显示ListView的时候,每取出列表一个列表项数据,就会调用一次getView方法,并返回要显示的view。

  1. class MyAdapter extends BaseAdapter {
  2. private LayoutInflater inflater;
  3. private List<Map<String,Object>> data;
  4. private TextView largeText;
  5. private TextView smallText;
  6. private ImageView imageView;
  7. private List<View> holder;
  8.  
  9. public MyAdapter(Context context,List<Map<String,Object>> data){
  10. this.inflater = LayoutInflater.from(context);
  11. this.data = data;
  12. }
  13.  
  14. public int getCount() {
  15.  return data.size();
  16. }
  17.  
  18. public Object getItem(int arg0) {
  19.  return data.get(arg0);
  20. }
  21.  
  22. @Override
  23. public long getItemId(int arg0) {
  24.  return arg0;
  25. }
  26.  
  27. @Override
  28. public View getView(int p, View v, ViewGroup parent) {
  29. if(v == null){
  30. v = inflater.inflate(R.layout.custom_list_view, null);
  31. largeText= (TextView)v.findViewById(R.id.large_text);
  32. smallText= (TextView)v.findViewById(R.id.small_text);
  33. imageView= (ImageView)v.findViewById(R.id.image);
  34. holder = new ArrayList<View>();
  35. holder.add(largeText);
  36. holder.add(smallText);
  37. holder.add(imageView);
  38. v.setTag(holder);
  39. }else{
  40. holder = (ArrayList)v.getTag();
  41. }
  42. ((TextView)holder.get(0)).setText((String)data.get(p).get("large_text"));
  43. ((TextView)holder.get(1)).setText((String)data.get(p).get("small_text"));((ImageView)holder.get(2)).setImageDrawable(getResources().getDrawable((int)data.get(p).get("drawable_id")));
  44. return v;
  45. }
  46. }

三、在Activity中为ListView设置Adapter,并将数据传入Adapter。

通常数据来自数据库查询结果,这里只是简单的将构造的数据传给Adapter。

  1. List<Map<String,Object>> data = new ArrayList<Map<String,Object>>();
  2. Map<String,Object> d1 = new HashMap<String,Object>();
  3. d1.put("large_text", "Large Text1");
  4. d1.put("small_text", "Small Text1");
  5. d1.put("drawable_id", R.id.testImage1);
  6. d2.put("large_text", "Large Tex2t");
  7. d2.put("small_text", "Small Text2");
  8. d2.put("drawable_id", R.id.testImage2);
  9. d3.put("large_text", "Large Text3");
  10. d3.put("small_text", "Small Text3");
  11. d3.put("drawable_id", R.id.testImage3);
  12. d4.put("large_text", "Large Text4");
  13. d4.put("small_text", "Small Text4");
  14. d4.put("drawable_id", R.id.testImage4);
  15. data.add(d1);
  16. data.add(d2);
  17. data.add(d3);
  18. data.add(d4);
  19. ListView list = (ListView)findViewById(R.id.customList);
  20. list.setAdapter(new MyAdapter(this,data));

这样,就可以通过覆写BaseAdapter创建自定义的Adapter并显示自定义的ListView了。

自定义ListView的item无法响应OnItemClickListener的OnItemClick方法问题的解决方案

查看第一步的布局文件,可以看到最外层的RelativeLayout中有一行android:descendantFocusability=”blocksDescendants”,这样就可以响应item的click事件了。

关于这个方案的解释,来自这里:

在Android软件设计与实现中我们通常都会使用到ListView这个控件,系统有一些预置的Adapter可以使用,例如SimpleAdapter和ArrayAdapter,但是总是会有一些情况我们需要通过自定义ListView来实现一些效果,那么在这个时候,我们通常会碰到自定义ListView无法选中整个ListViewItem的情况,也就是无法响应ListView的onItemClickListener中的onItemClick()方法,究竟是为什么呢?我之前也在网上查过不少的资料,但是没有发现什么有价值的文章,有一些是建议在Adapter的getView方法中对自己需要响应单击事件的控件进行设置。但是最终的效果并不是特别理想,而且我认为这是一种取巧的方式,并不推荐。之后自己查看了一下ViewGroup的源码,发现了以下的一段常量声明:

public static final int FOCUS_BEFORE_DESCENDANTS = 0×20000;
public static final int FOCUS_AFTER_DESCENDANTS = 0×40000;
public static final int FOCUS_BLOCK_DESCENDANTS = 0×60000;
public static final int FOCUS_BEFORE_DESCENDANTS = 0×20000;
public static final int FOCUS_AFTER_DESCENDANTS = 0×40000;
public static final int FOCUS_BLOCK_DESCENDANTS = 0×60000;
我们看到了一行代码定义的变量的意思是:

“当前View将屏蔽他所有子控件的Focus状态,即便这些子控件是可以Focus的”,

其实这段话的意思就是这个变量代表着当前的View将不顾其子控件是否可以Focus自身接管了所有的Focus,通常默认能获得focus的控件有Button,Checkable继承来的所有控件,这就意味着如果你的自定义ListViewItem中有Button或者Checkable的子类控件的话,那么默认focus是交给了子控件,而ListView的Item能被选中的基础是它能获取Focus,也就是说我们可以通过将ListView中Item中包含的所有控件的focusable属性设置为false,这样的话ListView的Item自动获得了Focus的权限,也就可以被选中了,也就会响应onItemClickListener中的onItemClick()方法,然而将ListView的Item Layout的子控件focusable属性设置为false有点繁琐,我们可以通过对Item Layout的根控件设置其android:descendantFocusability=”blocksDescendant”即可,这样Item Layout就屏蔽了所有子控件获取Focus的权限,不需要针对Item Layout中的每一个控件重新设置focusable属性了,如此就可以顺利的响应onItemClickListener中的onItenClick()方法了。

0 0
原创粉丝点击