編輯:關於android開發
引言
我們在android的APP開發中有時候會碰到提供一個選項列表供用戶選擇的需求,如在投票類型的項目中,我們提供一些主題給用戶選擇,每個主題有若干選項,用戶對這些主題的選項進行選擇,然後提交。
本文以一個支持單選和多選投票項目為例,演示了在一個ListView中如何構建CheckBox列表和RadioButton列表,並分析了實現的原理和思路,提供有需要的朋友參考。
項目的演示效果如下。
通常我們的數據源來自於數據庫。首先,我們構建投票項目類SubjectItem。
/** * 主題項目類 * @author zoupeiyang * */ public class SubjectItem { /** * 主題id */ private String subjectId; /** * 主題名稱 */ private String subjectName; /** * 主題id */ private String itemId; /** * 主題名稱 */ private String itemName; /** * 是否多選 */ private Boolean isMultiChoice; public String getSubjectId() { return subjectId; } public void setSubjectId(String subjectId) { this.subjectId = subjectId; } public String getSubjectName() { return subjectName; } public void setSubjectName(String subjectName) { this.subjectName = subjectName; } public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public Boolean getIsMultiChoice() { return isMultiChoice; } public void setIsMultiChoice(Boolean isMultiChoice) { this.isMultiChoice = isMultiChoice; } }
然後我們構造一個SubjectItem對象的List集合作為我們這個投票項目的數據源,實際項目中這個數據源可以來自數據庫投票項目表。
/** * 模擬從數據庫表獲取投票主題項目的數據源 * * @return */ public static List<SubjectItem> getSubjectItems() { List<SubjectItem> list = new ArrayList<SubjectItem>(); HashMap<String, Boolean> subjectMap = new HashMap<String, Boolean>(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { SubjectItem item = new SubjectItem(); item.setSubjectId(i + ""); //為了方便主題標題和主題項目的布局,集合中同一個主題的項目,只有主題第一個項目的對象的主題名稱不為空,其它為空 //這樣顯示ListView的每列時如果主題名稱為空就隱藏主題名稱 if (subjectMap.containsKey(item.getSubjectId())) { item.setSubjectName(""); } else { item.setSubjectName("投票主題" + i); subjectMap.put(item.getSubjectId(), true); } item.setItemId(i + "" + j); item.setItemName("項目名稱" + i + "" + j); item.setIsMultiChoice(i % 2 == 1 ? true : false); list.add(item); } } return list; }
如何在ListView控件上展示以列表的方式展示投票項目
首先我們先來了解下在ListView控件展示列表數據的流程。
1、定義一個展示列表每一行的布局layout,我們這裡定義這個layout的文件名為listview_subject_item.xml。
2、定義展示listview的布局layout,我們這裡定義這個layout的文件名為listview_subject_activity.xml。
3、定義listview的數據適配器SubjectAdapter。
listview_subject_item.xml文件定義的ListView控件中每列view的布局。我們這裡的投票項目是支持單選和多項,可以每列view的布局都包含了CheckBox和RadioButton控件,在手機界面顯示視圖的時候根據當前項目的投票類型(單選或多選)來自動顯示(隱藏)對應的控件。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <!-- 投票主題ID,默認隱藏 --> <TextView android:id="@+id/tv_subject_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" android:visibility="gone" /> <!-- 投票主題下項目ID,默認隱藏 --> <TextView android:id="@+id/tv_subject_item_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" android:visibility="gone" /> <!-- 投票主題類型,true為多選,否則為單選,默認隱藏 --> <TextView android:id="@+id/tv_is_multi_choice" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" android:visibility="gone" /> <!-- 投票主題名稱,只有主題下的第一個項目才會顯示主題名稱,其它項目不顯示 --> <TextView android:id="@+id/tv_subject_name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginBottom="10dp" android:textSize="14sp" android:textColor="#1387DD" android:text android:text="" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <!-- 投票項目名稱 --> <TextView android:id="@+id/tv_subject_item_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center_vertical" /> <!-- 多選項目顯示CheckBox --> <CheckBox android:id="@+id/cb_subject_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" android:gravity="center_vertical" /> <!-- 單選項目顯示RadioButton --> <RadioButton android:id="@+id/rb_subject_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" android:gravity="center_vertical" /> </LinearLayout> </LinearLayout>
定義展示ListView的布局
定義展示listview的布局layou,文件名為listview_subject_activity.xml
這裡使用了RelativeLayout布局,將提交按鈕固定在屏幕底部,方便用戶提交投票信息。
<?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="match_parent" android:background="#ffffffff" > <RelativeLayout android:id="@+id/rl_head" android:layout_width="match_parent" android:layout_height="45dp" android:layout_alignParentTop="true" android:background="#0C99EF" android:paddingLeft="10dp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="投票項目" android:textColor="#ffffffff" android:textSize="16sp" /> </RelativeLayout> <!-- 投票項目ListView --> <ListView android:id="@+id/lv_subject" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/rl_head" android:layout_marginBottom="50dp" android:layout_marginLeft="10dp" > </ListView> <Button android:id="@+id/btn_add" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:text="提交" /> </RelativeLayout>
定義數據適配器SubjectAdapter
listview_subject_activity.xml文件定義了名為lv_subject的ListView,這個ListView如何和listview控件中每列view的布局listview_subject_item.xml進行關聯,還有我們前面定義了投票主題項目數據源,它又如何和listview進行關聯數據綁定,要完成這些,我們必須依賴一個Apdater適配器類。
ListView控件通過方法setAdapter和Adapter關聯。
在Adapter中通過getView方法和列view的布局listview_subject_item.xml進行關聯。
數據源通過Adapter的自定義構造函數的參數傳人Adapter。
package com.example.listviewcheckbox.adapter; import java.util.HashMap; import java.util.List; import com.example.listviewcheckbox.R; import com.example.listviewcheckbox.entity.SubjectItem; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.RadioButton; import android.widget.TextView; public class SubjectAdapter extends BaseAdapter { private List<SubjectItem> list; private Context context; //存儲所有主題的項目的選中狀態,遍歷這個容器可以獲取選中的項目信息 private HashMap<String,Boolean> subjectItemMap; private LayoutInflater inflater; public class ViewHolder{ //投票主題id控件 public TextView tvSubjectId; //投票主題名稱控件 public TextView tvSubjectName; //投票項目名稱控件 public TextView tvSubjectItemName; //投票項目id控件 public TextView tvSubjectItemId; //投票主題類型(單選或多選)控件 public TextView tvIsMultiChoice; //選中CheckBox控件(主題類型為多選時顯示) public CheckBox cbSubjectItem; //選中RadioButton控件(主題類型為單選時顯示) public RadioButton rbSubjectItem; } public SubjectAdapter(List<SubjectItem> list,Context context) { this.list=list; this.context=context; inflater = LayoutInflater.from(context); this.subjectItemMap=new HashMap<String, Boolean>(); //初始化subjectItemMap,默認所有項目為未選中狀態 for (int i = 0; i < list.size(); i++) { this.subjectItemMap.put(list.get(i).getItemId(), false); } } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder viewHolder = null; SubjectItem item = list.get(position); if(convertView!=null&&convertView.getId()==R.id.lv_subject) { viewHolder=(ViewHolder)convertView.getTag(); } else { viewHolder = new ViewHolder(); convertView=inflater.inflate(R.layout.listview_subject_item, null); viewHolder.tvSubjectId=(TextView)convertView.findViewById(R.id.tv_subject_id); viewHolder.tvSubjectName=(TextView) convertView.findViewById(R.id.tv_subject_name); viewHolder.tvSubjectItemId = (TextView) convertView.findViewById(R.id.tv_subject_item_id); viewHolder.tvSubjectItemName = (TextView) convertView.findViewById(R.id.tv_subject_item_name); viewHolder.cbSubjectItem = (CheckBox) convertView.findViewById(R.id.cb_subject_item); viewHolder.rbSubjectItem = (RadioButton) convertView.findViewById(R.id.rb_subject_item); viewHolder.tvIsMultiChoice = (TextView) convertView.findViewById(R.id.tv_is_multi_choice ); } //如果項目名稱為空就隱藏當前項的產品名稱,即所有子項目只允許第一個子項目出現產品名稱 if(item.getSubjectName().equals("")) { viewHolder.tvSubjectName.setVisibility(View.GONE); } else { viewHolder.tvSubjectName.setText(item.getSubjectName()); } viewHolder.tvSubjectItemId.setText(item.getItemId()); viewHolder.tvSubjectId.setText(item.getSubjectId()); viewHolder.tvSubjectItemName.setText(item.getItemName()); viewHolder.tvIsMultiChoice.setText(item.getIsMultiChoice().toString()); //當前項目為多選項目 if(item.getIsMultiChoice().toString().equals("true")) { viewHolder.cbSubjectItem.setVisibility(View.VISIBLE); viewHolder.rbSubjectItem.setVisibility(View.GONE); viewHolder.cbSubjectItem.setChecked(this.subjectItemMap.get(item.getItemId())); } //當前項目為單選項目 else { viewHolder.cbSubjectItem.setVisibility(View.GONE); viewHolder.rbSubjectItem.setVisibility(View.VISIBLE); viewHolder.rbSubjectItem.setChecked(this.subjectItemMap.get(item.getItemId())); } convertView.setTag(viewHolder); return convertView; } /** * 獲取所有主題的項目的選中狀態容器 * @return */ public HashMap<String,Boolean> getSubjectItemMap() { return this.subjectItemMap; } }
定義顯示投票項目的Activity組件
最後我們定義一個Activity組件,將投票項目顯示出來。
為了解決單選項目選中後同時要將同主題原來已經選中的項目取消,定義了一個Map(radioButtonSelectedMaps)來存儲單選主題的選中的項目信息,key為單選主題ID,value為選中的項目ID。
這樣在用戶選擇某個單選項目時,程序先將SubjectAdapter對象中subjectItemMap該項目主題之前選中的項目的狀態設置為false,然後將當前選中的項目設置為true,然後更新ListView,實現單選效果。
public class SubjectActivity extends Activity { private ListView lvSubject; private SubjectAdapter subjectAdapter; private List<SubjectItem> list; private Button btnAdd; // 用來保存單選主題當前選中的項目,這樣用戶在切換選擇同一個主題下其它選項時能夠將之前選中的項目的狀態設置為未選狀態 private HashMap<String, String> radioButtonSelectedMaps; @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.listview_subject_activity); lvSubject = (ListView) findViewById(R.id.lv_subject); btnAdd = (Button) findViewById(R.id.btn_add); //從數據源獲取投票主題和項目信息 list = DataService.getSubjectItems(); subjectAdapter = new SubjectAdapter(list, this); lvSubject.setAdapter(subjectAdapter); radioButtonSelectedMaps = new HashMap<String, String>(); // 提交投票事件處理 btnAdd.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String selectValues="選中信息:"; //遍歷用戶選中項目,可以根據實際需求獲取選中項目的任何信息 for (int i = 0; i < list.size(); i++) { if(subjectAdapter.getSubjectItemMap().get(list.get(i).getItemId())) { selectValues+="項目ID:"+list.get(i).getItemId()+"項目名稱:"+list.get(i).getItemName(); } } Toast.makeText(SubjectActivity.this, selectValues.equals("選中信息:")?"未選中任何信息":selectValues, Toast.LENGTH_LONG).show(); } }); // ListView控件每一行點擊事件處理 lvSubject.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub ViewHolder viewHolder = (ViewHolder) view.getTag(); // 如果當前行是多選項目 if (viewHolder.tvIsMultiChoice.getText().equals("true")) { viewHolder.cbSubjectItem.toggle(); subjectAdapter.getSubjectItemMap().put(viewHolder.tvSubjectItemId.getText().toString(),viewHolder.cbSubjectItem.isChecked()); } //如果當前行為單選項目,注意單選項目選中後需要將同一主題下已經選中的項目設置為未選中狀態 else { String currentSubjectIdSelected=viewHolder.tvSubjectId.getText().toString(); String currentSubjectItemId=viewHolder.tvSubjectItemId.getText().toString(); //判斷該單選主題是否有已經選中項目,如果有需要將它的選中狀態設置為未選中 if (radioButtonSelectedMaps.containsKey(currentSubjectIdSelected)) { subjectAdapter.getSubjectItemMap().put(radioButtonSelectedMaps.get(currentSubjectIdSelected),false); } //將當前選中的項目設置為該單選主題的選中項目 radioButtonSelectedMaps.put(currentSubjectIdSelected,currentSubjectItemId); viewHolder.rbSubjectItem.toggle(); subjectAdapter.getSubjectItemMap().put(currentSubjectItemId,viewHolder.rbSubjectItem.isChecked()); //更新ListView updateListView(); }}}); } /** * 更新ListView */ private void updateListView() { subjectAdapter.notifyDataSetChanged(); } }
獲取投票結果
// 提交投票事件處理 btnAdd.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String selectValues="選中信息:"; //遍歷用戶選中項目,可以根據實際需求獲取選中項目的任何信息 for (int i = 0; i < list.size(); i++) { if(subjectAdapter.getSubjectItemMap().get(list.get(i).getItemId())) { selectValues+="項目ID:"+list.get(i).getItemId()+"項目名稱:"+list.get(i).getItemName(); } } Toast.makeText(SubjectActivity.this, selectValues.equals("選中信息:")?"未選中任何信息":selectValues, Toast.LENGTH_LONG).show(); } });
代碼下載
有需要的朋友可以訪問我的GitHub下載本文示例的完整代碼。
https://github.com/zoupeiyang/ListViewCheckBox
Cordova系列學習教程02 開發環境的准備和運行 主要用Eclipse來搭建環境: 下載Eclipse 安裝ADThttp://de
Android窗口機制分析與UI管理系統,androidui類圖關系 在看Android的窗口機制之前,先看看其主要的類圖關系以及層級之間的依賴與調用關系
Android 貝塞爾曲線實現QQ拖拽清除效果 純屬好奇心驅動寫的一個學習性Demo,效果如下: 兩個帶圓弧的線就是由三點確認的一個貝塞爾曲線: 在Android已經
Android Design Support Library——Snackbar,androidsnackbar Snackbar是一個輕量級控件,它可以很方便的提供消