这应该是大部分Android开发者在使用ListView时会碰到问题,在解决这个问题前,需要先了解什么叫触摸模式。
触摸模式(Touch Mode),在Android SDK的官方文档中有详细解释(http://developer.android.com/resources/articles/touch-mode.html),其大概内容如下:
用户通过触摸屏操作设备时,设备将自动进入触摸模式,相对的,使用键盘、轨迹球等其他设备时,设备处于非触摸模式
进入触摸模式后,View将失去焦点(Focus)和选择(Selection)状态(文本输入框是例外),原因是Android的设计者没有好的办法解决同时操作触摸屏和轨迹球、键盘等外部设备所带来的混乱状态
Android的设计者认为进入触摸模式后,View失去焦点和选择状态不是一个需要修正的问题,开发者也不应该在触摸模式中试图保持View的焦点或选择状态,可以使用CheckBox或RadioButton等控件来标识选择状态
这种设计策略可以说完全是从工程师的思维而不是从用户的需求去考虑问题,新的手机的性能越来越强,屏幕分辨率越来越高,应用程序界面设计趋向复杂,使用触摸屏操作时居然连个高亮显示都无法做到,真是件十足坑爹的事。
考察了几种不同的实现后,评估其性能代价和解决方式的优雅程度,我决定用checked状态来模拟选择选择selection来实现ListView的选中行的高亮显示:
1. 将ListView设为CHOICE_MODE_SINGLE或CHOICE_MODE_MULTIPLE,使ListView支持checked/unchecked状态
2. 在ListView的适配器中,返回的View实现Checkable接口,跟随选中状态的改变而改变自身背景
下面是我的测试代码:
001 package org.noodies;
002
003 import android.app.Activity;
004 import android.content.Context;
005 import android.graphics.drawable.Drawable;
006 import android.graphics.drawable.StateListDrawable;
007 import android.os.Bundle;
008 import android.view.View;
009 import android.view.ViewGroup;
010 import android.widget.BaseAdapter;
011 import android.widget.Checkable;
012 import android.widget.ListView;
013 import android.widget.TextView;
014
015 public class TestListActivity extends Activity {
016
017private ListView mListView;
018
019@Override
020public void onCreate(Bundle savedInstanceState) {
021super.onCreate(savedInstanceState);
022setContentView(R.layout.main);
023
024mListView = (ListView) findViewById(R.id.listView1);
025
026// 设为单选,允许列表项切换checked/unchecked状态
027mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
028// 列表的选择效果设为透明,由列表项自行维护各状态显示
029mListView.setSelector(android.R.color.transparent);
030
031mListView.setAdapter(new TestAdapter());
032}
033
034private class MyView extends TextView implements Checkable {
035
036private final int[] STATE_CHECKED = { android.R.attr.state_checked };
037
038private int[] mSavedState;
039private boolean mChecked = false;
040
041public MyView(Context context) {
042super(context);
043}
044
045public void setChecked(boolean checked) {
046if (mChecked != checked) {
047mChecked = checked;
048updateBackground();
049}
050}
051
052public boolean isChecked() {
053return mChecked;
054}
055
056public void toggle() {
057setChecked(!mChecked);
058}
059
060private void updateBackground() {
061Drawable bg = this.getBackground();
062
063// 在这里切换checked/unchecked状态
064if (bg.getClass().equals(StateListDrawable.class)) {
065if (isChecked()) {
066mSavedState = bg.getState();
067bg.setState(STATE_CHECKED);
068} else if (mSavedState != null) {
069bg.setState(mSavedState);
070}
071}
072}
073}
074
075private class TestAdapter extends BaseAdapter {
076
077private String[] testData = { "test1", "test2", "abcd", "abcdefg" };
078
079public int getCount() {
080return testData.length;
081}
082
083public Object getItem(int position) {
084return testData[position];
085}
086
087public long getItemId(int position) {
088return 0;
089}
090
091public View getView(int position, View convertView, ViewGroup parent) {
092TextView v;
093
094if (convertView == null) {
095v = new MyView(TestListActivity.this);
096v.setBackgroundResource(R.drawable.list_item_bg);
097} else {
098v = (TextView) convertView;
099}
100
101v.setText(testData[position]);
102
103return v;
104}
105
106}
107 }
需要建立一个selector作为View的背景,包含选中状态的图像:
1 <?xml version="1.0" encoding="utf-8"?>www.2cto.com
2 <selector xmlns:android="http://schemas.android.com/apk/res/android">
3
4<item android:drawable="@drawable/list_item_bg_checked" android:state_checked="true"/>
5<item android:drawable="@drawable/list_item_bg_normal" android:state_pressed="false"/>
6<item android:drawable="@drawable/list_item_bg_pressed" android:state_pressed="true"/>
7
8 </selector>
作者:dean83