需求:
界面上写一个两行三列的单选radiobutton组设置应用背景图,如下图
实现方法:
1、使用原始的RadioGroup可以实现布局,一个RadioGroup里两个LinearLayout分开radiobutton,但是会导致选项无法互斥。
2、Google官方的做法是写两个RadioGroup,但是要动态监听。(http://stackoverflow.com/questions/10425569/radiogroup-with-two-columns-which-have-ten-radiobuttons)
3、自定义一个RadioGroup。
4、用好 android:layout_marginLeft 和android:layout_marginTop这两条属性,调整好radiobutton的间距实现出多行多列的界面效果。(https://blog.csdn.net/mrzhang_happy/article/details/48436105)
之前写这个的人选择了写两行RadioGroup来实现,代码如下:
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int bgId = 0;
switch (checkedId) {
case R.id.setting_rb_rainbow:
rgScreenBg2.check(-1);
bgId = R.drawable.home_bg_rainbow;
break;
case R.id.setting_rb_indigo_blue:
rgScreenBg2.check(-1);
bgId = R.drawable.home_bg_indigo_blue;
break;
case R.id.setting_rb_black:
rgScreenBg2.check(-1);
bgId = R.drawable.home_bg_black;
break;
case R.id.setting_rb_red:
rgScreenBg1.check(-1);
bgId = R.drawable.home_bg_red;
break;
case R.id.setting_rb_blue:
rgScreenBg1.check(-1);
bgId = R.drawable.home_bg_blue;
break;
case R.id.setting_rb_green:
rgScreenBg1.check(-1);
bgId = R.drawable.home_bg_green;
break;
default:
break;
}
if(bgId != 0) {
AppPrefs.getInstance().setAppBackground(bgId);
getActivity().getWindow()
.setBackgroundDrawableResource(AppPrefs.getInstance().getAppBackground());
}
}
实现逻辑是这样的,点击第一行的radiobutton时清除第二行的选中项rgScreenBg2.check(-1),反之点击第二行时就执行rgScreenBg1.check(-1)。但是出现一个bug,选中项在第一行时去点击第二行的选项时,没有显示选中效果,反之选中项在第二行时也一样。
调试后,根本原因在于RadioGroup.check(-1)方法的执行,RadioGroup.check(-1)源码内多次调用了onCheckedChanged方法(具体源码分析请参考https://blog.csdn.net/qq_32452623/article/details/80474487)。 文章里面说用setChecked() 替代 setChecked(),但是不适用于我自己的问题。
解决方法:
我分析了它的执行顺序,举例子现在选中项为黑色,点击绿色的选项。onCheckedChanged第一次触发执行
case R.id.setting_rb_green: 然后执行 rgScreenBg1.check(-1);,此时不往下执行而去触发第二次onCheckedChanged监听,
case R.id.setting_rb_black: 然后又执行 rgScreenBg2.check(-1);。
所以出现了没有选中项的显示效果,但是虽然多次调用onCheckedChanged方法,但是最后一次的执行结果会是对的,具体可看源码分析。
个人使用心得:
1、onCheckedChanged 监听方法并不是监听radiobutton点击事件,这是一个常见误区,它只是RadioGroup中的radiobutton状态变化的监听。
/**
* Interface definition for a callback to be invoked when the checked
* radio button changed in this group.
*/
public interface OnCheckedChangeListener {
/**
* Called when the checked radio button has changed. When the
* selection is cleared, checkedId is -1.
*
* @param group the group in which the checked radio button has changed
* @param checkedId the unique identifier of the newly checked radio button
*/
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId);
}
2、我觉得这个也是谷歌当初的设计问题,并没有为这种多行多列单选的使用场景做一个特定的控件,而且RadioGroup本身也没有radiobutton点击的监听接口方法。
3、最后我在onCheckedChanged中加了个标记值isWrong解决了(骚操作),但是解决这个问题的根本方法,我建议还是选择自定义的RadioGroup比较好。
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int bgId = 0;
int index = 0;
switch (checkedId) {
case R.id.setting_rb_rainbow:
if (!isWrong) {
isWrong = true;
rgScreenBg2.check(-1);
}
bgId = R.drawable.home_bg_rainbow;
index = 0;
break;
case R.id.setting_rb_indigo_blue:
if (!isWrong) {
isWrong = true;
rgScreenBg2.check(-1);
}
bgId = R.drawable.home_bg_indigo_blue;
index = 1;
break;
case R.id.setting_rb_black:
if (!isWrong) {
isWrong = true;
rgScreenBg2.check(-1);
}
bgId = R.drawable.home_bg_black;
index = 2;
break;
case R.id.setting_rb_red:
if (!isWrong) {
isWrong = true;
rgScreenBg1.check(-1);
}
bgId = R.drawable.home_bg_red;
index = 3;
break;
case R.id.setting_rb_blue:
if (!isWrong) {
isWrong = true;
rgScreenBg1.check(-1);
}
bgId = R.drawable.home_bg_blue;
index = 4;
break;
case R.id.setting_rb_green:
if (!isWrong) {
isWrong = true;
rgScreenBg1.check(-1);
}
bgId = R.drawable.home_bg_green;
index = 5;
break;
default:
break;
}
if (bgId != 0 && !isWrong) {
AppPrefs.getInstance().setAppBackground(index);
getActivity().getWindow()
.setBackgroundDrawableResource(Constants.bgArray[index]);
}
isWrong = false;
}