目录
列表视图ListView
列表类视图是 Android 开发中最重要和最常用的组件之一,用于展示大量结构化数据。ListView与Spinner一样通过setAdapter方法设置列表项的数据适配器,但它设置监听器的方法却不同。它通过调用方法setOnItemClickListener设置列表项的点击监听器OnItemClickListener,调用方法setOnitemLongClickListener设置列表项的长按监听器OnItemLongClickListener。列表视图ListView还新增了几个属性以及对应的方法。属性divider指定分割线的图形若不需要分割线只需将该属性的值设为"@null",其对应的方法为setDivider;属性dividerHeight指定分割线的高度,对应方法为setDividerHeight;属性listSelector指定列表项的按压背景,其对应方法为setSelector。代码示例如下,
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".SeniorWidget.ListViewActivity">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
button_selector.xml的代码如下。
XML
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/teal_200" android:state_pressed="true"/>
<item android:drawable="@color/green" />
</selector>
部分Java代码如下,数据适配器的内容与下拉框中的用法是一样的,不同在于属性以及对应方法的使用,还有监听器的不同。
java
public class ListViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
private int[] image_id = {R.drawable.one, R.drawable.two, R.drawable.three, R.drawable.four, R.drawable.five,
R.drawable.six, R.drawable.seven, R.drawable.eight, R.drawable.nine, R.drawable.zero};
private Integer[] number = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
private String[] number_english = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
ListView listView = findViewById(R.id.listView);
List<NumberInfo> numberInfoList = getList();
listView.setAdapter(new MyBaseAdapter(this, numberInfoList));
listView.setSelection(0);
listView.setDivider(getDrawable(R.drawable.image_3));
listView.setDividerHeight(5);
listView.setSelector(R.drawable.button_selector);
listView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (adapterView.getId() == R.id.listView) {
Toast.makeText(this,String.format("选中了第%d项",i),Toast.LENGTH_SHORT).show();
}
}
public List<NumberInfo> getList() {
List<NumberInfo> numberInfoList = new ArrayList<NumberInfo>();
for (int i = 0; i < number.length; i++) {
NumberInfo numberInfo = new NumberInfo();
numberInfo.setImage_id(image_id[i]);
numberInfo.setNumber(number[i]);
numberInfo.setNumber_english(number_english[i]);
numberInfoList.add(numberInfo);
}
Log.e(TAG, "getList: ");
return numberInfoList;
}
class NumberInfo {
private int image_id;
private int number;
private String number_english;
public int getImage_id() {
return image_id;
}
public void setImage_id(int image_id) {
this.image_id = image_id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getNumber_english() {
return number_english;
}
public void setNumber_english(String number_english) {
this.number_english = number_english;
}
}
class MyBaseAdapter extends BaseAdapter {
private List<NumberInfo> numberInfoList;
private Context mcontext;
public MyBaseAdapter(Context context, List<NumberInfo> list) {
super();
numberInfoList = list;
mcontext = context;
}
@Override
public int getCount() {
return numberInfoList.size();
}
@Override
public Object getItem(int i) {
return numberInfoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
view = LayoutInflater.from(mcontext).inflate(R.layout.item_base, null);
viewHolder.imageView = view.findViewById(R.id.imageView);
viewHolder.textView_1 = view.findViewById(R.id.textView_1);
viewHolder.textView_2 = view.findViewById(R.id.textView_2);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
NumberInfo numberInfo = numberInfoList.get(i);
viewHolder.imageView.setImageResource(numberInfo.getImage_id());
viewHolder.textView_1.setText(String.format("%d", numberInfo.getNumber()));
viewHolder.textView_2.setText(numberInfo.getNumber_english());
viewHolder.imageView.requestFocus();
return view;
}
final class ViewHolder {
public ImageView imageView;
public TextView textView_1;
public TextView textView_2;
}
}
}
效果图如下,可以看到列表视图能够展示全部的数据项,这与下拉框不一样,下拉框在选择后只能展示一项的内容。

焦点抢占问题及解决方式
如果在列表项中有按钮或者编辑框这类控件,当发生点击事件时这类控件会抢占焦点导致列表项的点击监听器失效。为了解决该问题,可以在列表项的布局文件中的的根节点添加属性descendantFocusability并设置其值或者在Java代码中调用方法setdescendantFocusability设置。该属性有三种取值,如下表所示说明。
|-------------------|------------------------------------|----------------------------------|
| 属性取值 | 代码中的方法参数取值 | 说明 |
| beforeDescendants | ViewGroup.FOCUS_BEFORE_DESCENDANTS | 视图组优先:ViewGroup 先于子视图获取焦点 |
| afterDescendants | ViewGroup.FOCUS_AFTER_DESCENDANTS | 子视图优先:子视图先于 ViewGroup 获取焦点 |
| blocksDescendants | ViewGroup.FOCUS_BLOCK_DESCENDANTS | 阻止子视图:ViewGroup 获取焦点,阻止子视图获取 |
代码示例如下,页面布局文件不变。
下面为item_listview.xml中的代码,在原来的列表项布局中新加了一个按钮并且在布局的根结点添加了属性descendantFocusability并且取值为blocksDescendants。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="1"
android:src="@drawable/image_3"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/textView_1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:textSize="17sp"
android:textColor="#1269db"
android:text="@string/app_name"/>
<TextView
android:id="@+id/textView_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="17sp"
android:text="@string/app_name"/>
</LinearLayout>
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:text="Button" />
</LinearLayout>
Java代码的不同主要在于适配器中,在类ViewHolder中添加了一个按钮button用于获取按钮实例,在方法getView中改变在于与按钮相关的部分,如获取按钮实例以及设置按钮监听器,其余基本不变。
java
class MyBaseAdapter extends BaseAdapter {
private List<NumberInfo> numberInfoList;
private Context mcontext;
public MyBaseAdapter(Context context, List<NumberInfo> list) {
super();
numberInfoList = list;
mcontext = context;
}
@Override
public int getCount() {
return numberInfoList.size();
}
@Override
public Object getItem(int i) {
return numberInfoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
view = LayoutInflater.from(mcontext).inflate(R.layout.item_listview, null);
viewHolder.imageView = view.findViewById(R.id.imageView);
viewHolder.textView_1 = view.findViewById(R.id.textView_1);
viewHolder.textView_2 = view.findViewById(R.id.textView_2);
viewHolder.button = view.findViewById(R.id.button);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
NumberInfo numberInfo = numberInfoList.get(i);
viewHolder.imageView.setImageResource(numberInfo.getImage_id());
viewHolder.textView_1.setText(String.format("%d", numberInfo.getNumber()));
viewHolder.textView_2.setText(numberInfo.getNumber_english());
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mcontext,String.format("点击了第%d项的按钮!",i+1),Toast.LENGTH_SHORT).show();
}
});
viewHolder.imageView.requestFocus();
return view;
}
final class ViewHolder {
public ImageView imageView;
public TextView textView_1;
public TextView textView_2;
public Button button;
}
}
效果图如下,可以看到按钮并没有抢占列表项的焦点,两者的监听器能够正常运行。

