ListView作为Androd开发中最常用又最“多事”的组件,本人也是既爱之又“恨”之。
今天有一个需求:(测试题库中常用到的单选题,多选题等)
1、安卓自带的RadioButton,CheckBox太丑了,特别是当选项中出现大图片,ABCD之类的也得加到按钮后面,混合到一起,那是简直没法看了。所以,需要重新绘制RadioButton和CheckBox。
2、选项个数不确定,所以需要动态添加。
综上说述,决定用ListView+ListViewAdapter(自定义的,继承与BaseAdapter)来解决这个问题。
先上效果图:
思路:
看到上图样式,我们首先应该怎么做呢?因为,它有选中和不选中两个状态,第一个想到的就是:创建样式选择器selector以及做一下上图中的按钮背景图片。
如下代码:(其中,custom_rbutton_checked和custom_rbutton_unchecked分别是选中以及不选中时的按钮背景图)
<?xml version="1.0" encoding="utf-8"?> <!-- 单选按钮背景自定义效果 --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/custom_rbutton_checked" android:state_checked="true" android:state_enabled="true" /> <item android:drawable="@drawable/custom_rbutton_unchecked" android:state_checked="false" android:state_enabled="true" /> </selector>
然后在style.xml中添加相关RadioButton的样式。如下:(将背景设置为选择器的样式)
<style name="custom_radio_styles"> <item name="android:background">@drawable/selector_radiobutton_bg</item> <item name="android:gravity">center</item> </style>
接下来我们给相关布局文件中的RadioButton添加style样式即可。如下:
<RadioButton android:id="@+id/radiobuttonlist_singleChoiceButton" style="@style/custom_radio_styles" android:layout_width="30dp" android:layout_height="30dp" android:layout_marginRight="10dp" android:button="@null" android:focusable="false" android:clickable="false" android:focusableInTouchMode="false" android:text="A" />其中,
android:button="@null"
可以把RadioButton的自带按钮样式去掉。
android:focusable="false" android:clickable="false" android:focusableInTouchMode="false"
上面的可以解决ListView中添加Button或者CheckBox等时造成的ListView的onItemClickListener监听事件没反应的问题。(说白了就是焦点获取不到)
至此,按钮样式已经完成。下面我们开始做一下和ListView相关的操作。
首先,如果向ListView中添加子项,毋庸置疑,第一个想到的是不是数据适配器(ArrayAdapter,SimpleAdapter,BaseAdapter等)呢?这个大家应该都是这么想的,那么问题来了,用现有的SimpleAdapter适配器能够实现我们想要的展示出来的效果,但是想要的状态改变和数据改变,我们要怎么获取呢?想了想,行不通,那怎么办?那么我们自定义一个数据适配器就可以了,继承BaseAdapter。
如下图:RadioButtonListViewAdapter.java(单选题的数据适配器)
public class RadioButtonListViewAdapter extends BaseAdapter { private List<ItemBean> mData; private Context mContext; private HashMap<String,Boolean> rButtonStates = new HashMap<String,Boolean>(); public RadioButtonListViewAdapter(Context mContext, List<ItemBean> mData, HashMap<String, Boolean> states) { this.mContext = mContext; this.mData = mData; this.rButtonStates = states; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_radiobuttonlist, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final ItemBean itemObj = mData.get(position); holder.textView.setText(itemObj.getText()); holder.radioButton.setText(itemObj.getBtnText()); boolean res = false; if(getStates(position) == null || getStates(position) == false)//判断当前位置的radiobutton点击状态 { res = false; setStates(position, false); }else{ res = true; } holder.radioButton.setChecked(res); return convertView; } //用于在activity中重置所有的radiobutton的状态 public void clearStates(int position){ // 重置,确保最多只有一项被选中 for(String key:rButtonStates.keySet()){ rButtonStates.put(key, false); } rButtonStates.put(String.valueOf(position), true); } //用于获取状态值 public Boolean getStates(int position){ return rButtonStates.get(String.valueOf(position)); } //设置状态值 public void setStates(int position, boolean isChecked){ rButtonStates.put(String.valueOf(position), false); } private class ViewHolder { private RadioButton radioButton; private TextView textView; public ViewHolder(View convertView) { radioButton = convertView.findViewById(R.id.radiobuttonlist_singleChoiceButton); textView = convertView.findViewById(R.id.radiobuttonlist_singleChoiceContent); } } }
像其中的一些类以及item布局文件,大家可以自己定义即可。主要理解思路。
适配器整好了,那么我们现在在MainActivity中进行调用即可。
如下:(这是一个单选题选项动态添加的方法,在你需要的位置调用一下即可。)
//单选题选项添加 public void singleChoiceQuestionsAutoAddOption(int addNum, String userAnswer){ singleChoiceQuestionData = new ArrayList<ItemBean>(); HashMap<String, Boolean> optionStates = new HashMap<String, Boolean>(); for (int i = 0; i < addNum; i++) { if (userAnswer.equals(optionTags[i])) { optionStates.put(String.valueOf(i), true); } else { optionStates.put(String.valueOf(i), false); } } for (int i = 0; i < addNum; i++){ ItemBean itemBean = new ItemBean(); itemBean.setBtnText(optionTags[i]); itemBean.setText(singleChoiceQuestionsOptionContents[i]); singleChoiceQuestionData.add(itemBean); } singleChoiceQuestionsAdapter = new RadioButtonListViewAdapter(this , singleChoiceQuestionData, optionStates); singleChoiceQuestionsAdapter.notifyDataSetChanged(); rbList.setAdapter(singleChoiceQuestionsAdapter); }
注意:因为做的是测试题库程序,所以用户的答案也要做保存以及切换题目的时候的用户答案显示。
最后,还要实现ListView的onItemClickListener的监听事件。
如下:
public AdapterView.OnItemClickListener singleChoiceAndMultipleChoiceQuestionsListviewOnItemClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { switch (questionType) { case 0:{ singleChoiceQuestionsTransfer(view, position); userAnswers[0] = optionTags[position]; break; } case 1:{ multipleChoiceQuestionsTransfer(view, position); String userAnswer = userAnswers[1]; String whichOption = optionTags[position]; if (userAnswer.indexOf(whichOption) >= 0){ userAnswer = userAnswer.replace(whichOption, ""); } else { userAnswer = (userAnswer + whichOption).trim(); } userAnswers[1] = userAnswer; break; } default:break; } } }; private void singleChoiceQuestionsTransfer(View view, int position) { RadioButton radioButton = (RadioButton) view.findViewById(R.id.radiobuttonlist_singleChoiceButton); //每次选择一个item时都要清除所有的状态,防止出现多个被选中 singleChoiceQuestionsAdapter.clearStates(position); radioButton.setChecked(singleChoiceQuestionsAdapter.getStates(position)); //刷新数据,调用getView刷新ListView singleChoiceQuestionsAdapter.notifyDataSetChanged(); }
再重申一遍,代码已经很详细了,以理解为主,希望对大家有所帮助。大家互相学习,互相提升。
完整Demo地址:https://download.csdn.net/download/lpcrazyboy/10465373