Android随笔3:多行RadioGroup

一、简介


在开发某APP时,遇到这么一个问题:要使用RadioGroup来实现单选框效果,但是item太多,要分成两行来显示。 Android随笔3:多行RadioGroup_第1张图片
一开始我心想,这不是很简单吗,在xml布局里设置RadioGroup的orientation值为vertical,里面嵌套一个水平布局的LinearLayout,然后把RadioButton给它一个一个摆上去不就完事儿咯~so easy的事情,万万没想到。。。。。。
Android随笔3:多行RadioGroup_第2张图片
什么鬼??? 为什么没有单选效果了???
这个问题让我百思不得其解,直到我看到了RadioGroup的源码

二、源码分析

	@Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
    	// 下面这行就是关键中的关键了
        if (child instanceof RadioButton) {
            final RadioButton button = (RadioButton) child;
            if (button.isChecked()) {
                mProtectFromCheckedChange = true;
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;
                setCheckedId(button.getId());
            }
        }

        super.addView(child, index, params);
    }

让我们看看关键的判断:if (child instanceof RadioButton)
难怪没有了单选效果,因为我们在RadioGroup里面还嵌套了一层LinearLayout,无法进到判断中的代码块。
既然我们已经知道了为什么多行RadioGroup会没有单选效果,那么解决的方法也就呼之欲出了

三、实现RadioGroup

方法一: RadioGroup其实也是继承了LinearLayout来实现的,那么我们定义两个水平布局的RadioGroup,分别摆放其中的RadioGroup,然后设置相应的点击事件不就可以了吗?

xml布局

	<RadioGroup
            android:id="@+id/rg_one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"
            android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/rb_problem_speed_up_error"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/selector_radiobutton"
                    android:button="@null"
                    android:paddingStart="@dimen/dp_14"
                    android:paddingTop="@dimen/dp_6"
                    android:paddingEnd="@dimen/dp_14"
                    android:paddingBottom="@dimen/dp_6"
                    android:text="@string/speed_up_error"
                    android:textColor="@drawable/selector_radiobutton_text"
                    android:textSize="@dimen/sp_15" />

                <RadioButton
                    android:id="@+id/rb_problem_error_code"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="@dimen/dp_10"
                    android:background="@drawable/selector_radiobutton"
                    android:button="@null"
                    android:paddingStart="@dimen/dp_14"
                    android:paddingTop="@dimen/dp_6"
                    android:paddingEnd="@dimen/dp_14"
                    android:paddingBottom="@dimen/dp_6"
                    android:text="@string/error_code"
                    android:textColor="@drawable/selector_radiobutton_text"
                    android:textSize="@dimen/sp_15" />

                <RadioButton
                    android:id="@+id/rb_problem_charge"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="@dimen/dp_10"
                    android:background="@drawable/selector_radiobutton"
                    android:button="@null"
                    android:paddingStart="@dimen/dp_14"
                    android:paddingTop="@dimen/dp_6"
                    android:paddingEnd="@dimen/dp_14"
                    android:paddingBottom="@dimen/dp_6"
                    android:text="@string/charge"
                    android:textColor="@drawable/selector_radiobutton_text"
                    android:textSize="@dimen/sp_15" />

            RadioGroup>

            <RadioGroup
                android:id="@+id/rg_two"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_10">

                <RadioButton
                    android:id="@+id/rb_problem_others"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/selector_radiobutton"
                    android:button="@null"
                    android:paddingStart="@dimen/dp_14"
                    android:paddingTop="@dimen/dp_6"
                    android:paddingEnd="@dimen/dp_14"
                    android:paddingBottom="@dimen/dp_6"
                    android:text="@string/others"
                    android:textColor="@drawable/selector_radiobutton_text"
                    android:textSize="@dimen/sp_15" />

            RadioGroup>

具体实现:

		mGroupOne.setOnCheckedChangeListener(new OnMultiOneCheckChangeListener());
        mGroupTwo.setOnCheckedChangeListener(new OnMultiTwoCheckChangeListener());
	private class OnMultiOneCheckChangeListener implements RadioGroup.OnCheckedChangeListener {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId) {
                case R.id.rb_problem_speed_up_error:
                    if (mSpeedButton.isChecked()) {
                        mGroupTwo.clearCheck();
                    }
                    break;

                case R.id.rb_problem_error_code:
                    if (mErrorButton.isChecked()) {
                        mGroupTwo.clearCheck();
                    }
                    break;

                case R.id.rb_problem_charge:
                    if (mChargeButton.isChecked()) {
                        mGroupTwo.clearCheck();
                    }
                    break;
            }
        }
    }

    private class OnMultiTwoCheckChangeListener implements RadioGroup.OnCheckedChangeListener {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId) {
                case R.id.rb_problem_others:
                    if (mOthersButton.isChecked()) {
                        mGroupOne.clearCheck();
                    }
                    break;
            }
        }
    }

为两个RadioGroup分别设置监听,如果点击了其中一个RadioGroup的子button,清空另一个RadioGroup的勾选。
使用这种方法有很大的缺陷:如果不是两行,而是n行,就需要n个RadioGroup,并为这n个RadioRroup都设置监听。而且,每个RadioGroup中的子button都要做相应的勾选,清除其他RadioGroup勾选的操作,显得繁琐,不具有通用性。

方法二: 重写上面的addView代码,通过遍历的方式获取RadioGourp中,以及其子布局中的所有RadioButton,然后做相应的设置。

 	@Override
    public void addView(final View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof ViewGroup) {
            int childCount = ((ViewGroup) child).getChildCount();
            for (int i = 0; i < childCount; i++) {
                View view = ((ViewGroup) child).getChildAt(i);
                if (view instanceof RadioButton) {
                    final RadioButton button = (RadioButton) view;
                    button.setOnTouchListener(new OnTouchListener() {
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                        	// 设置button为当前选项
                            button.setChecked(true);
                            // 这个方法下面会分析,用于取消其他项的勾选
                            checkRadioButton(button);
                            if (mOnCheckedChangeListener != null) {
                                mOnCheckedChangeListener.onCheckedChanged(MultiRadioGroup.this, button.getId());
                            }
                            return true;
                        }
                    });
                }
            }
        }
        super.addView(child, index, params);
    }

其实也很简单,首先判断子view是不是ViewGroup,如果不是ViewGroup,直接调用原来的addView方法;如果是则遍历其中的子View,并设置Touch事件:设置当前button为勾选项,通过checkRadioButton方法,取消其他项的勾选。下面分析checkRadioButton方法。

	private void checkRadioButton(RadioButton radioButton) {
        View child;
        int radioCount = getChildCount();
        for (int i = 0; i < radioCount; i++) {
            child = getChildAt(i);
            if (child instanceof RadioButton) {
                RadioButton button = (RadioButton) child;
                if (button == radioButton) {
                    // do nothing
                } else {
                    button.setChecked(false);
                }
            } else if (child instanceof LinearLayout) {
                int childCount = ((LinearLayout) child).getChildCount();
                for (int j = 0; j < childCount; j++) {
                    View view = ((LinearLayout) child).getChildAt(j);
                    if (view instanceof RadioButton) {
                        RadioButton button = (RadioButton) view;
                        if (button == radioButton) {
                            // do nothing
                        } else {
                            button.setChecked(false);
                        }
                    }
                }
            }
        }
    }

其实就是通过遍历判断RadioButton是不是当前勾选的RadioButton,如果不是,setChecked为false。自此,我们就实现了多行RadioGroup,使用的方法和普通的RadioGroup一样。
Android随笔3:多行RadioGroup_第3张图片

xml布局

	<com.linjunyi.MultiRadioGroup
                android:id="@+id/rg_problem"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white"
                android:orientation="vertical"
                android:paddingStart="@dimen/dp_15"
                android:paddingTop="@dimen/dp_10"
                android:paddingEnd="@dimen/dp_0"
                android:paddingBottom="@dimen/dp_20">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="@dimen/dp_10"
                    android:text="@string/question_type"

                    android:textColor="@color/color_333333"
                    android:textSize="@dimen/sp_12" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <RadioButton
                        android:id="@+id/rb_problem_speed_up_error"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@drawable/selector_radiobutton"
                        android:button="@null"
                        android:paddingStart="@dimen/dp_14"
                        android:paddingTop="@dimen/dp_6"
                        android:paddingEnd="@dimen/dp_14"
                        android:paddingBottom="@dimen/dp_6"
                        android:text="@string/speed_up_error"
                        android:textColor="@drawable/selector_radiobutton_text"
                        android:textSize="@dimen/sp_15" />

                    <RadioButton
                        android:id="@+id/rb_problem_error_code"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="@dimen/dp_10"
                        android:background="@drawable/selector_radiobutton"
                        android:button="@null"
                        android:paddingStart="@dimen/dp_14"
                        android:paddingTop="@dimen/dp_6"
                        android:paddingEnd="@dimen/dp_14"
                        android:paddingBottom="@dimen/dp_6"
                        android:text="@string/error_code"
                        android:textColor="@drawable/selector_radiobutton_text"
                        android:textSize="@dimen/sp_15" />

                    <RadioButton
                        android:id="@+id/rb_problem_charge"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="@dimen/dp_10"
                        android:background="@drawable/selector_radiobutton"
                        android:button="@null"
                        android:paddingStart="@dimen/dp_14"
                        android:paddingTop="@dimen/dp_6"
                        android:paddingEnd="@dimen/dp_14"
                        android:paddingBottom="@dimen/dp_6"
                        android:text="@string/charge"
                        android:textColor="@drawable/selector_radiobutton_text"
                        android:textSize="@dimen/sp_15" />

                LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dp_10">

                    <RadioButton
                        android:id="@+id/rb_problem_others"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@drawable/selector_radiobutton"
                        android:button="@null"
                        android:paddingStart="@dimen/dp_14"
                        android:paddingTop="@dimen/dp_6"
                        android:paddingEnd="@dimen/dp_14"
                        android:paddingBottom="@dimen/dp_6"
                        android:text="@string/others"
                        android:textColor="@drawable/selector_radiobutton_text"
                        android:textSize="@dimen/sp_15" />

                LinearLayout>

            com.linjunyi.MultiRadioGroup>

四、总结

使用以上两种方法都能实现多行RadioGroup,但是第一种方法不具备通用性,一旦行数变多,或是其中RadioButton变多,都会让处理代码变得冗长繁琐。第二种方法通过自定义一个MultiRadioGroup,然后继承RadioGroup并重写其中的addView方法,不管有多少行,其中有多少RadioButton,都不需要额外的代码,可以像普通的RadioGroup一样使用。

五、源代码

package com.netease.na..weight;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import com.netease.common.core.utils.log.Logger;

/**
 * 多行RadioGroup
 *
 * @author [email protected]
 */
public class MultiRadioGroup extends RadioGroup {
    private static final String TAG = "MultiRadioGroup";
    private OnCheckedChangeListener mOnCheckedChangeListener;
    private int mCheckId = -1;

    public MultiRadioGroup(Context context) {
        super(context);
    }

    public MultiRadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeListener = listener;
    }

    @Override
    public void addView(final View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof LinearLayout) {
            int childCount = ((LinearLayout) child).getChildCount();
            for (int i = 0; i < childCount; i++) {
                View view = ((LinearLayout) child).getChildAt(i);
                if (view instanceof RadioButton) {
                    final RadioButton button = (RadioButton) view;
                    button.setOnTouchListener(new OnTouchListener() {
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            mCheckId = button.getId();
                            button.setChecked(true);
                            checkRadioButton(button);
                            if (mOnCheckedChangeListener != null) {
                                mOnCheckedChangeListener.onCheckedChanged(MultiRadioGroup.this, button.getId());
                            }
                            return true;
                        }
                    });
                }
            }
        }
        super.addView(child, index, params);
    }

    private void checkRadioButton(RadioButton radioButton) {
        View child;
        int radioCount = getChildCount();
        for (int i = 0; i < radioCount; i++) {
            child = getChildAt(i);
            if (child instanceof RadioButton) {
                RadioButton button = (RadioButton) child;
                if (button == radioButton) {
                    // do nothing
                } else {
                    button.setChecked(false);
                }
            } else if (child instanceof LinearLayout) {
                int childCount = ((LinearLayout) child).getChildCount();
                for (int j = 0; j < childCount; j++) {
                    View view = ((LinearLayout) child).getChildAt(j);
                    if (view instanceof RadioButton) {
                        RadioButton button = (RadioButton) view;
                        if (button == radioButton) {
                            // do nothing
                        } else {
                            button.setChecked(false);
                        }
                    }
                }
            }
        }
    }

    /**
     * 用于获取选中位置button的id
     */
    public int getCheckId() {
        return mCheckId;
    }
}

你可能感兴趣的:(安卓开发,自定义控件,UI控件)