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

Android中自定义ListView

2014-02-02 01:57 225 查看



Author : lovecicy

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

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


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

假设新建一个文件名叫做custom_list_view.xml,此文件用于显示单个列表项,相当于显示一条微博,包含的内容有头像、微博内容、图片、转发数、评论数等。这个布局文件和Activity的布局文件并没有什么不同。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants">

<TextView
android:id="@+id/large_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test"
android:textAppearance="?android:attr/textAppearanceLarge" />

<TextView
android:id="@+id/small_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test2" />

<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>


二、覆写BaseAdapter类

Adapter的作用就是将数据集合中的每个条目取出,将条目内的每个数据项放入到对应的控件中。最主要的方法是getView,在显示ListView的时候,每取出列表一个列表项数据,就会调用一次getView方法,并返回要显示的view。
class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Map<String,Object>> data;
private TextView largeText;
private TextView smallText;
private ImageView imageView;
private List<View> holder;

public MyAdapter(Context context,List<Map<String,Object>> data){
this.inflater = LayoutInflater.from(context);
this.data = data;
}

public int getCount() {
return data.size();
}

public Object getItem(int arg0) {
return data.get(arg0);
}

@Override
public long getItemId(int arg0) {
return arg0;
}

@Override
public View getView(int p, View v, ViewGroup parent) {
if(v == null){
v = inflater.inflate(R.layout.custom_list_view, null);
largeText= (TextView)v.findViewById(R.id.large_text);
smallText= (TextView)v.findViewById(R.id.small_text);
imageView= (ImageView)v.findViewById(R.id.image);
holder = new ArrayList<View>();
holder.add(largeText);
holder.add(smallText);
holder.add(imageView);
v.setTag(holder);
}else{
holder = (ArrayList)v.getTag();
}
((TextView)holder.get(0)).setText((String)data.get(p).get("large_text"));
((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")));
return v;
}
}


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

通常数据来自数据库查询结果,这里只是简单的将构造的数据传给Adapter。
List<Map<String,Object>> data = new ArrayList<Map<String,Object>>();
Map<String,Object> d1 = new HashMap<String,Object>();
d1.put("large_text", "Large Text1");
d1.put("small_text", "Small Text1");
d1.put("drawable_id", R.id.testImage1);
d2.put("large_text", "Large Tex2t");
d2.put("small_text", "Small Text2");
d2.put("drawable_id", R.id.testImage2);
d3.put("large_text", "Large Text3");
d3.put("small_text", "Small Text3");
d3.put("drawable_id", R.id.testImage3);
d4.put("large_text", "Large Text4");
d4.put("small_text", "Small Text4");
d4.put("drawable_id", R.id.testImage4);
data.add(d1);
data.add(d2);
data.add(d3);
data.add(d4);
ListView list = (ListView)findViewById(R.id.customList);
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()方法了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: