这篇其实应该是属于写自定义单选或多选的ListView的基础教程,无奈目前许多人对此的实现大多都绕了远路,反而使得这正规的写法倒显得有些技巧性了。
本文原创,转载请注明在CSDN上的出处:
http://blog.csdn.net/maosidiaoxian/article/details/45867927
Android中,ListView可以设置choiceMode,可见Android对ListView的单选或多选是有进行封装的,然而我看到的许多单选或多选的ListView,包括我以前写的例子,以前几个老外封装的库,都是自己维护了一个集合,用于存放每个item的选中状态。这样一来,不但代码显得繁复,逻辑上也成冗余,而且容易出BUG。
其实,ListView中,已经自己维护了一个SparseBooleanArray,用于保存每一项的选中状态。而对于每一项,它是通过adapter的getView中获取的view,来设置它的选中状态的。所以,我们需要使得adapter中,getView中返回的这个view实现Checkable接口。下面,将介绍具体实现。
这里介绍的实现方式有两个,一种是从零写一个单选的ListView。另一种是调用我的一个库的代码来实现。因为我已经对相关的必要逻辑都封装在了两个类里,使得易于使用。
原生实现
1,先写item的布局文件。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/checkedView" android:layout_centerVertical="true" android:layout_alignParentLeft="true" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" android:layout_width="wrap_content" android:layout_height="48dp" /> <TextView android:id="@+id/text" android:gravity="center_vertical" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="48dp" /> </RelativeLayout>
android:clickable="false" android:focusable="false" android:focusableInTouchMode="false"
package com.githang.android.choicelistview; import android.content.Context; import android.view.View; import android.widget.Checkable; import android.widget.FrameLayout; import android.widget.RadioButton; import android.widget.TextView; /** * FIXME * * @author Geek_Soledad (msdx.android@qq.com) */ public class ChoiceView extends FrameLayout implements Checkable{ private TextView mTextView; private RadioButton mRadioButton; public ChoiceView(Context context) { super(context); View.inflate(context, R.layout.item_single_choice, this); mTextView = (TextView) findViewById(R.id.text); mRadioButton = (RadioButton) findViewById(R.id.checkedView); } public void setText(String text) { mTextView.setText(text); } @Override public void setChecked(boolean checked) { mRadioButton.setChecked(checked); } @Override public boolean isChecked() { return mRadioButton.isChecked(); } @Override public void toggle() { mRadioButton.toggle(); } }
最后,在listview的adapter的getView方法里,返回这个实现了Checkable接口的ChoiceView。
package com.githang.android.choicelistview; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListAdapter; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<String> data = new ArrayList<>(); for(int i = 0; i < 5; i++) { data.add("test" + i); } ListView listView = (ListView) findViewById(R.id.list_view); listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); ListAdapter adapter = new ArrayAdapter<String>(this, R.layout.item_single_choice, data) { @Override public View getView(int position, View convertView, ViewGroup parent) { final ChoiceView view; if(convertView == null) { view = new ChoiceView(MainActivity.this); } else { view = (ChoiceView)convertView; } view.setText(getItem(position)); return view; } }; listView.setAdapter(adapter); } }
代码很简单方便,完全不用自己去维护一个选中状态的集合。Demo 项目下载地址:http://www.400gb.com/file/94898213
使用AndroidSnippet里的类实现
接下来还有更简单的实现方法,即使用我封装的类来实现。这种情况下,只需要写一个item的布局文件,然后写一个adapter即可,和写普通的ListView没多大区别。
item 的布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/checkedView" android:layout_centerVertical="true" android:layout_alignParentLeft="true" android:layout_width="wrap_content" android:layout_height="48dp" /> <TextView android:id="@+id/text" android:gravity="center_vertical" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="48dp" /> </RelativeLayout>
关于RadioButton的三个属性我已经在代码里封装好了,所以这里写不写那三个属性都无所谓。
接下来,就是使用我封装的ChoiceListAdapter,来实现单选(或多选)的ListView,代码如下:
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); ChoiceListAdapter adapter = new ChoiceListAdapter<String>(this, R.layout.item_single_choice, data, R.id.checkedView) { @Override protected void holdView(ChoiceLayout view) { view.hold(R.id.text); } @Override protected void bindData(ChoiceLayout view, int position, String data) { TextView text = view.get(R.id.text); text.setText(data); } }; listView.setAdapter(adapter);
这里的ChoiceLayout 我还对holder进行了封装,用起来是不是更简洁方便?
关于AndroidSnippet 库,我已把代码托管到github :https://github.com/msdx/AndroidSnippet。其中关于单选列表的例子,在app module中有。
最后附上实现效果。
效果GIF图: