引言

我们在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控件每列的布局

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:textStyle="bold"
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();
}
});

代码下载

https://github.com/zoupeiyang/ListViewCheckBox

扩展阅读

ListView的性能优化之convertView和viewHolder

如何在Android的ListView中构建CheckBox和RadioButton列表(支持单选和多选的投票项目示例)的更多相关文章

  1. 如何在Android应用程序中使用传感器模拟器SensorSimulator

    原文地址; 如何在Android应用程序中使用传感器模拟器 - 移动平台应用软件开发技术 - 博客频道 - CSDN.NET http://blog.csdn.net/pku_android/arti ...

  2. 在微信框架模块中,基于Vue&Element前端,通过动态构建投票选项,实现单选、复选的投票操作

    最近把微信框架的前端改造一下,在原来基于Bootstrap框架基础上的微信后台管理,增加一套Vue&Element的前端,毕竟Vue的双向绑定开发起来也还是很方便的,而且Element本身也提 ...

  3. android -------- 流式布局,支持单选、多选等

    最近开发中有流式标签这个功能,网上学了下,来分享一下 Android 流式布局,支持单选.多选等,适合用于产品标签等. 效果图: 用法: dependencies { compile 'com.hym ...

  4. Android listview中使用checkbox

    最近比较忙碌,我也不知道忙的什么东西,打算写的博客写了一半,还没写完,今天先扯一扯项目中遇到的一个问题,一方面防止以后遇到这个问题忘记如何解决,另一方面希望可以提供给遇到同样问题的朋友一个思路.下面开 ...

  5. Android ListView 中的checkbox

    Q:ListView + CheckBox 当上下滚动的时候有事会自动选中或取消 A:这个与ListView的缓存机制有关.当你屏幕滚动后,ListView中的item选项视图先检查缓存中是否有视图, ...

  6. Android ListView 中加入CheckBox/RadioButton 选择状态保持、全选、反选实现

    最近在一个项目中,需要在ListView的item中加入CheckBox,但是遇到的一个问题是上下滑动的时候如果有选择了的CheckBox,就会出现选择项错误的问题,下面将个人的解决方法总结如下;先说 ...

  7. android代码优化----ListView中自定义adapter的封装(ListView的模板写法)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. Android关于ListView中item与控件抢夺焦点的那些事

    在开发中,listview可以说是我们使用最频繁的控件之一了,但是关于listview的各种问题也是很多.当我们使用自定义布局的Listview的时候,如果在item的布局文件里面存在Button或者 ...

  9. Android开发-Listview中显示不同的视图布局

    1. 使用场景 在重写ListView的BaseAdapter时,我们常常在getView()方法中复用convertView,以提高性能.convertView在Item为单一的同种类型布局时,能够 ...

随机推荐

  1. 腾讯云短信服务使用记录与.NET Core C#代码分享

    1.即使是相同的短信签名与短信正文模板,也需要针对“国内文本短信”与“海外文本短信”分别申请.开始不知道,以为只要申请一次,给国外手机发短信时给api传对应的国家码就行,后来才发现需要分别申请. 2. ...

  2. python 给字符串加颜色

    msg = '\033[41;1m字符串内容\033[0m' print(msg) # \033[41;1m起始位置 改变41数值就是改变其他颜色,.033[0m 结束位置

  3. SQL之分组排序取top n

    转自:http://blog.csdn.net/wguangliang/article/details/50167283 要求:按照课程分组,查找每个课程最高的两个成绩. 数据文件如下: 第一列no为 ...

  4. 远程登录oracle 12.2数据库报错ORA-28040解决办法

    今天新安装的oracle 12.2.0.1数据库,通过本地sqlplus远程登录12c数据库报错ora-28040,如下: ORA-28040: No matching authentication ...

  5. centos7.2 nfs安装配置

    nfs服务端 ip:192.168.1.16 1.yum -y install rpcbind nfs-utils 2.创建文件/etc/exports,内容如下 /mnt/ 192.168.1.0/ ...

  6. block diagonal matrix 直和 块对角矩阵 不完美 有缺陷 缩放 射影几何

    小结: 1.block diagonal matrix  直和 块对角矩阵 A block diagonal matrix is a block matrix that is a square mat ...

  7. Linux的磁盘系统和文件系统显示的文件大小为什么不一样(du指令和ls指令的区别)

    写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...

  8. Java与面向对象之随感(1)

    大一下学期上完了c++课程,当时自我感觉很良好,认为对面向对象编程已经是身经百战了,但是上了院里HuangYu老师的Java课之后,才发现自己对于面向对象的编程风格的理解只在皮毛,着实惭愧不已. 假设 ...

  9. Copycat - Overview

    Copycat’s primary role is as a framework for building highly consistent, fault-tolerant replicated s ...

  10. Mybatis批量insert 返回主键值和foreach标签详解

    Mybatis批量insert 返回主键 Mybatis从3.3.1版本开始,支持批量插入后返回主键ID.首先对于支持自增主键的数据库使用useGenerateKeys和keyProperty,对于不 ...