我們或許曾一次又一次的接到這樣的需求,有一堆數據需要用戶去選擇,並且以列表的方式呈現,根據需求,我們需要單選或者復選列表里面的數據,並且最終取得所選數據作下一步的操作。那么對於這個需求,我們聰明的程序員往往都能想到一些解決方案去處理。譬如我,咳咳,雖然我不是很聰明,但是我想到了。
【也許這樣實現是對的】通常需要選擇的列表數據我都會在adapter所綁定的數據實體內增加一個標記代表是否選中,在點擊的時候去遍歷並改變adapter中的實體標記,通知更新,然后根據標記在adapter的getView方法改變視圖樣式。這樣能直觀地實現我們的需求。
【探索更好的解決方案】上面那種方式,很直接,估計大家都能想到,但這樣做存在一些問題,比如遍歷的效率會低,比如我們的adapter會增加一些非功能性的代碼,讓我們的adapter變得復雜。但當我們更深入地了解listview之后,我們可以發現listview有個屬性--choicemode,英文不錯的童鞋都能翻譯:選擇模式。
【選擇模式的例子】這里我寫了個簡單的使用選擇模式中單選的例子。其中關鍵的部分便是自定義view,也許這是這種方式唯一的弱點,必須使用自定義view.
【自定義選項】自定義的列表項繼承checkable,實現checkable內的方法修改列表項的界面狀態。
1 package com.change.selectablelistviewdemo; 2 3 import android.content.Context; 4 import android.widget.CheckBox; 5 import android.widget.Checkable; 6 import android.widget.LinearLayout; 7 import android.widget.TextView; 8 9 import com.change.selectablelistviewdemo.R; 10 11 /** 12 * 自定義選項view,實現checkable。在實現的方法中修改界面狀態。 13 * @author Change 14 * 15 */ 16 public class SelectItemView extends LinearLayout implements Checkable { 17 CheckBox cbx; 18 TextView name; 19 public SelectItemView(Context context){ 20 super(context); 21 inflate(getContext(), R.layout.item_select, this); 22 cbx = (CheckBox)findViewById(R.id.cbx); 23 name = (TextView)findViewById(R.id.name); 24 } 25 26 @Override 27 public boolean isChecked() { 28 return cbx.isChecked(); 29 } 30 31 @Override 32 public void setChecked(boolean arg0) { 33 cbx.setChecked(arg0); 34 } 35 36 @Override 37 public void toggle() { 38 cbx.toggle(); 39 } 40 41 public void setName(String name){ 42 this.name.setText(name); 43 } 44 45 }
我的列表項只有一個顯示名字的textview和一個代表單選狀態的選擇框,單選框我們需要取消他自身的點擊效果,把它的狀態交由列表項的點擊去決定。所以,我們需要禁止checkbox的點擊和焦點獲取。代碼如下--》
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:weightSum="1" > <TextView android:id="@+id/name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight=".9" /> <CheckBox android:id="@+id/cbx" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:layout_weight=".1" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" /> </LinearLayout>
【如何使用listview】很簡單,在主頁界面xml中定義listview,並設定choiceMode,此處為single,單選模式。
1 <RelativeLayout 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="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 11 <TextView 12 android:layout_width="wrap_content" 13 android:layout_height="wrap_content" 14 android:text="@string/hello_world" /> 15 16 <Button android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:onClick="choice" 19 android:text="current" 20 android:layout_below="@+id/lv" 21 /> 22 23 <ListView android:layout_width="match_parent" 24 android:layout_height="wrap_content" 25 android:id="@+id/lv" 26 android:choiceMode="singleChoice" 27 /> 28 29 </RelativeLayout>
【在主activity中的做了什么?】activity只是把listview的數據綁定到界面。使用adapter,此處是個內部類。adapter出奇的簡單,只是設置了名字的顯示而已,剩下的選擇狀態完全交由listview的機制和我們的自定義view.是不是很簡單?快行動起來吧。
1 package com.change.selectablelistviewdemo; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 import android.os.Bundle; 8 import android.view.Menu; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.widget.BaseAdapter; 12 import android.widget.ListView; 13 import android.widget.Toast; 14 15 /** 16 * listview單選模式demo, 17 * @author Change 18 * 19 */ 20 public class MainActivity extends Activity { 21 List<String> names = new ArrayList<String>(); 22 ListView lv; 23 SelAdapter adapter = new SelAdapter(); 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_main); 28 lv = (ListView)findViewById(R.id.lv); 29 initData(); 30 lv.setAdapter(adapter); 31 } 32 33 private void initData(){ 34 for(int i=0;i<10;i++){ 35 names.add("name"+i); 36 } 37 } 38 39 @Override 40 public boolean onCreateOptionsMenu(Menu menu) { 41 // Inflate the menu; this adds items to the action bar if it is present. 42 getMenuInflater().inflate(R.menu.main, menu); 43 return true; 44 } 45 46 /** 47 * 列表適配器,只需要設置view的內容,狀態變化完全交由android機制和自定義view.是不是很簡單? 48 * @author Change 49 * 50 */ 51 class SelAdapter extends BaseAdapter{ 52 53 @Override 54 public int getCount() { 55 return names.size(); 56 } 57 58 @Override 59 public Object getItem(int arg0) { 60 return names.get(arg0); 61 } 62 63 @Override 64 public long getItemId(int arg0) { 65 return arg0; 66 } 67 68 @Override 69 public View getView(int arg0, View arg1, ViewGroup arg2) { 70 if(null==arg1){ 71 arg1 = new SelectItemView(MainActivity.this); 72 } 73 ((SelectItemView)arg1).setName(names.get(arg0)); 74 return arg1; 75 } 76 77 78 } 79 public void choice(View v){ 80 int pos = lv.getCheckedItemPosition(); 81 if(ListView.INVALID_POSITION!=pos){ 82 Toast.makeText(MainActivity.this, "current pos="+pos, Toast.LENGTH_SHORT).show(); 83 } 84 } 85 }
【關鍵部分:取得當前選項】代碼中標紅的部分便可以輕松獲得當前的選項,從此媽媽再也不擔心我們不會選擇了。
【完整項目】在github中,地址:https://github.com/ChangeWu/SomePoject/tree/master/SelectableListViewDemo