android的RadioButton的使用历来都让人比较头疼,如在布局方面,图案、文字无法分别设置padding等,另外,低版本的android RadioGroup不支持换行排列的RadioButton(此bug在4.4以上貌似已经修复)
这里我自定义了一个VariedRadioButton,主要的功能优势有:
1.可以一步添加多个radio button,不需要在xml布局文件中进行多次罗列;
2.灵活布局:添加text、image的margin等属性,可以自由定义间隔;
3.灵活布局:自由定义image/text的前后顺序
4.灵活布局:自由设定radio button的orientation,支持横向和纵向
5.无需添加响应radio button的oncheckedchanged接口。在需要取值时,直接调用一行代码即可。
效果如下:

代码如下:
主界面:
1 package cn.carbs.variedradiobutton;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.view.View;
6 import android.widget.Button;
7 import cn.carbs.variedradiobutton.view.VariedRadioButton;
8
9 public class MainActivity extends Activity {
10
11 VariedRadioButton variedRadioButton;
12 Button button;
13 @Override
14 protected void onCreate(Bundle savedInstanceState) {
15 super.onCreate(savedInstanceState);
16 setContentView(R.layout.activity_main);
17 variedRadioButton = (VariedRadioButton)findViewById(R.id.v);
18 button = (Button)findViewById(R.id.button);
19 button.setOnClickListener(new View.OnClickListener() {
20
21 @Override
22 public void onClick(View v) {
23 variedRadioButton.setSelectedIndex(4);
24 }
25 });
26
27 variedRadioButton.setSelectedIndex(3);
28 }
29
30
31 }
自定义view的代码:
1 package cn.carbs.variedradiobutton.view;
2
3 import java.util.ArrayList;
4
5 import android.content.Context;
6 import android.content.res.TypedArray;
7 import android.graphics.drawable.Drawable;
8 import android.util.AttributeSet;
9 import android.util.Log;
10 import android.util.TypedValue;
11 import android.view.Gravity;
12 import android.view.View;
13 import android.widget.ImageView;
14 import android.widget.LinearLayout;
15 import android.widget.TextView;
16
17 import cn.carbs.variedradiobutton.R;
18 import cn.carbs.variedradiobutton.util.DisplayUtil;
19
20
21 public class VariedRadioButton extends LinearLayout implements View.OnClickListener{
22
23 private static final String TAG = "wang";
24
25 private static final int ORDER_IMAGE_FIRST = 0;
26 private static final int ORDER_TEXT_FIRST = 1;
27
28 private static final int DEFAULT_SELECTED_INDEX = 0;
29
30 private static final float DEFAULT_MARGIN = 0f;
31 private static final int DEFAULT_ORDER = ORDER_IMAGE_FIRST;
32 private static final int DEFAULT_ORIENTATION = LinearLayout.HORIZONTAL;
33 private static final int DEFAULT_NUM = 2;
34 private static final int DEFAULT_TEXT_COLOR = 0xff000000;
35 private static final float DEFAULT_TEXT_VIEW_TEXT_SIZE_SP = 16;
36
37 private static final int DEFAULT_TEXTS_RES = 0;
38 private static final int DEFAULT_IMAGE_RES = 0;
39
40 private Context mContext;
41 private int mDrawableBackgroundRadioSelected;
42 private int mDrawableBackgroundRadio;
43 private Drawable mDrawableBackgroundText;
44 private float mTextMarginLeft = DEFAULT_MARGIN;
45 private float mTextMarginRight = DEFAULT_MARGIN;
46 private float mTextMarginTop = DEFAULT_MARGIN;
47 private float mTextMarginBottom = DEFAULT_MARGIN;
48 private float mImageMarginLeft = DEFAULT_MARGIN;
49 private float mImageMarginRight = DEFAULT_MARGIN;
50 private float mImageMarginTop = DEFAULT_MARGIN;
51 private float mImageMarginBottom = DEFAULT_MARGIN;
52 private float mUnitMarginLeft = DEFAULT_MARGIN;
53 private float mUnitMarginRight = DEFAULT_MARGIN;
54 private float mUnitMarginTop = DEFAULT_MARGIN;
55 private float mUnitMarginBottom = DEFAULT_MARGIN;
56
57 private int mOrder = DEFAULT_ORDER;
58 private int mOrientation = DEFAULT_ORIENTATION;
59 private int mNum = DEFAULT_NUM;
60 private int mTextColor = DEFAULT_TEXT_COLOR;
61 private float mTextSize = DEFAULT_TEXT_VIEW_TEXT_SIZE_SP;
62 private int mTextsRes = DEFAULT_TEXTS_RES;
63 private String[] mTexts;
64 private ArrayList<ImageView> mImageViews = new ArrayList();
65 private ArrayList<TextView> mTextViews = new ArrayList();
66
67 private View mContentView = null;
68 private LinearLayout mContainer = null;
69
70 private Object mTagTextView = new Object();
71 private Object mTagImageView = new Object();
72
73 private int mSelectedIndex = DEFAULT_SELECTED_INDEX;
74
75 public VariedRadioButton(Context context) {
76 this(context, null);
77 }
78
79 public VariedRadioButton(Context context, AttributeSet attrs) {
80 this(context, attrs, 0);
81 }
82
83 public VariedRadioButton(Context context, AttributeSet attrs, int defStyle) {
84 super(context, attrs, defStyle);
85 mContext = context;
86 mContentView = inflate(context, R.layout.view_varied_radio_button, this);
87 mContainer = (LinearLayout)mContentView.findViewById(R.id.container);
88
89 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.variedRadioButton);
90
91 final int count = a.getIndexCount();
92 for (int i = 0; i < count; ++i) {
93 int attr = a.getIndex(i);
94 switch (attr) {
95 case R.styleable.variedRadioButton_backgroundRadioSelected:
96 mDrawableBackgroundRadioSelected = a.getResourceId(attr, DEFAULT_IMAGE_RES);
97 break;
98 case R.styleable.variedRadioButton_backgroundRadio:
99 mDrawableBackgroundRadio = a.getResourceId(attr, DEFAULT_IMAGE_RES);
100 break;
101 case R.styleable.variedRadioButton_backgroundText:
102 mDrawableBackgroundText = a.getDrawable(attr);
103 break;
104 case R.styleable.variedRadioButton_textMarginLeft:
105 mTextMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
106 break;
107 case R.styleable.variedRadioButton_textMarginRight:
108 mTextMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
109 break;
110 case R.styleable.variedRadioButton_textMarginTop:
111 mTextMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
112 break;
113 case R.styleable.variedRadioButton_textMarginBottom:
114 mTextMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
115 break;
116 case R.styleable.variedRadioButton_imageMarginLeft:
117 mImageMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
118 break;
119 case R.styleable.variedRadioButton_imageMarginRight:
120 mImageMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
121 break;
122 case R.styleable.variedRadioButton_imageMarginTop:
123 mImageMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
124 break;
125 case R.styleable.variedRadioButton_imageMarginBottom:
126 mImageMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
127 break;
128 case R.styleable.variedRadioButton_unitMarginLeft:
129 mUnitMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
130 break;
131 case R.styleable.variedRadioButton_unitMarginRight:
132 mUnitMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
133 break;
134 case R.styleable.variedRadioButton_unitMarginTop:
135 mUnitMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
136 break;
137 case R.styleable.variedRadioButton_unitMarginBottom:
138 mUnitMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
139 break;
140 case R.styleable.variedRadioButton_order:
141 mOrder = a.getInt(attr, DEFAULT_ORDER);
142 break;
143 case R.styleable.variedRadioButton_radioButtonNum:
144 mNum = a.getInt(attr, DEFAULT_NUM);
145 break;
146 case R.styleable.variedRadioButton_contentTextColor:
147 mTextColor = a.getColor(attr, DEFAULT_TEXT_COLOR);
148 break;
149 case R.styleable.variedRadioButton_contentTextSize:
150 mTextSize = DisplayUtil.px2sp(mContext, a.getDimensionPixelSize(attr, DisplayUtil.sp2px(mContext, DEFAULT_TEXT_VIEW_TEXT_SIZE_SP)));
151 break;
152 case R.styleable.variedRadioButton_optionsOrientation:
153 mOrientation = a.getInt(attr, DEFAULT_ORIENTATION);
154 break;
155 case R.styleable.variedRadioButton_texts:
156 mTextsRes = a.getResourceId(attr, DEFAULT_TEXTS_RES);
157 mTexts = mContext.getResources().getStringArray(mTextsRes);
158 break;
159 case R.styleable.variedRadioButton_selectedIndex:
160 mSelectedIndex = a.getInt(attr, DEFAULT_SELECTED_INDEX);
161 break;
162
163 }
164 }
165 a.recycle();
166
167 mContainer.setOrientation(mOrientation);
168 LinearLayout.LayoutParams paramsUnit = null;
169 if(mOrientation == LinearLayout.HORIZONTAL){
170 paramsUnit = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);
171 }else{
172 paramsUnit = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 0);
173 }
174
175 paramsUnit.weight = 1;
176 paramsUnit.leftMargin = (int)mUnitMarginLeft;
177 paramsUnit.rightMargin = (int)mUnitMarginRight;
178 paramsUnit.topMargin = (int)mUnitMarginTop;
179 paramsUnit.bottomMargin = (int)mUnitMarginBottom;
180
181 LinearLayout.LayoutParams paramsImageView = new LinearLayout.LayoutParams(
182 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
183 paramsImageView.leftMargin = (int)mImageMarginLeft;
184 paramsImageView.rightMargin = (int)mImageMarginRight;
185 paramsImageView.topMargin = (int)mImageMarginTop;
186 paramsImageView.bottomMargin = (int)mImageMarginBottom;
187
188 LinearLayout.LayoutParams paramsTextView = new LinearLayout.LayoutParams(
189 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
190 paramsTextView.leftMargin = (int)mTextMarginLeft;
191 paramsTextView.rightMargin = (int)mTextMarginRight;
192 paramsTextView.topMargin = (int)mTextMarginTop;
193 paramsTextView.bottomMargin = (int)mTextMarginBottom;
194
195 for(int n = 0; n < mNum; n++){
196
197 LinearLayout ll = new LinearLayout(mContext);
198 ll.setOrientation(LinearLayout.HORIZONTAL);
199 ll.setGravity(Gravity.CENTER_VERTICAL);
200
201 ImageView image = new ImageView(mContext);
202 image.setBackgroundResource(mDrawableBackgroundRadio);
203 image.setLayoutParams(paramsImageView);
204 image.setTag(mTagImageView);
205
206 TextView text = new TextView(mContext);
207 text.setGravity(Gravity.CENTER);
208 if(n < mTexts.length){
209 text.setText(mTexts[n]);
210 }
211 text.setLayoutParams(paramsTextView);
212 text.setTag(mTagTextView);
213 text.setTextSize(mTextSize);
214 text.setTextColor(mTextColor);
215
216 if(mOrder == ORDER_IMAGE_FIRST){
217 ll.addView(image);
218 ll.addView(text);
219 }else{
220 ll.addView(text);
221 ll.addView(image);
222 }
223 ll.setTag(n);
224 ll.setOnClickListener(this);
225
226 mImageViews.add(image);
227 mTextViews.add(text);
228 mContainer.addView(ll, paramsUnit);
229 }
230 mContainer.setWeightSum(mNum);
231 setSelectedIndex(mSelectedIndex);
232 }
233
234 public void setRadioButtonNum(int num){
235 mNum = num;
236 }
237
238 public void setTextsRes(int textsRes){
239 mTextsRes = textsRes;
240 mTexts = mContext.getResources().getStringArray(mTextsRes);
241 }
242
243 public void setTexts(String[] texts){
244 mTexts = texts;
245 }
246
247 public void setSelectedIndex(int selectedIndex){
248 if(selectedIndex >= 0 && selectedIndex < mNum){
249 refreshView(selectedIndex);
250 }else{
251
252 }
253 }
254
255 public int getSelectedIndex(){
256 return mSelectedIndex;
257 }
258
259 @Override
260 public void onClick(View v) {
261 Integer index = (Integer)v.getTag();
262 if(index != null){
263 refreshView(index);
264 }else{
265 throw new IllegalArgumentException("need to set a tag to LinearLayout element");
266 }
267 }
268
269 private void refreshView(int selectedIndex){
270 mSelectedIndex = selectedIndex;
271 LinearLayout clickedLL = null;
272 ImageView image = null;
273 for(int i = 0; i < mNum; i++){
274 clickedLL = (LinearLayout)this.findViewWithTag(i);
275 image = (ImageView)clickedLL.findViewWithTag(mTagImageView);
276 if(i == selectedIndex){
277 image.setBackgroundResource(mDrawableBackgroundRadioSelected);
278 }else{
279 image.setBackgroundResource(mDrawableBackgroundRadio);
280 }
281 }
282 }
283
284 }
布局文件:
activity_main.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:app="http://schemas.android.com/apk/res/cn.carbs.variedradiobutton" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:paddingBottom="@dimen/activity_vertical_margin" 7 android:paddingLeft="@dimen/activity_horizontal_margin" 8 android:paddingRight="@dimen/activity_horizontal_margin" 9 android:paddingTop="@dimen/activity_vertical_margin" > 10 11 <cn.carbs.variedradiobutton.view.VariedRadioButton 12 android:id="@+id/v" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" 15 android:background="#33333333" 16 android:text="@string/hello_world" 17 app:backgroundRadio="@drawable/button_unchecked" 18 app:backgroundRadioSelected="@drawable/button_checked" 19 app:backgroundText="#333333" 20 app:imageMarginLeft="30dp" 21 app:optionsOrientation="horizontal" 22 app:order="imageFirst" 23 app:radioButtonNum="5" 24 app:selectedIndex="1" 25 app:textMarginLeft="0dp" 26 app:texts="@array/city" /> 27 28 <Button 29 android:id="@+id/button" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:text="button" /> 33 34 </LinearLayout>
view_varied_radio_button.xml :
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/container" 4 android:orientation="horizontal" 5 android:layout_width="match_parent" 6 android:layout_height="wrap_content" 7 android:gravity="center" > 8 9 10 </LinearLayout>
自定义属性:
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <declare-styleable name="variedRadioButton"> 5 <attr name="backgroundRadio" /> 6 <attr name="backgroundRadioSelected" /> 7 <attr name="radioButtonNum" /> 8 <attr name="backgroundText" /> 9 <attr name="order" /> 10 <attr name="contentTextColor" /> 11 <attr name="contentTextSize" /> 12 <attr name="textMarginLeft" /> 13 <attr name="textMarginRight" /> 14 <attr name="textMarginTop" /> 15 <attr name="textMarginBottom" /> 16 <attr name="imageMarginLeft" /> 17 <attr name="imageMarginRight" /> 18 <attr name="imageMarginTop" /> 19 <attr name="imageMarginBottom" /> 20 <attr name="unitMarginLeft" /> 21 <attr name="unitMarginRight" /> 22 <attr name="unitMarginTop" /> 23 <attr name="unitMarginBottom" /> 24 <attr name="texts" /> 25 <attr name="optionsOrientation"> 26 <enum name="horizontal" value="0" /> 27 <enum name="vertical" value="1" /> 28 </attr> 29 <attr name="selectedIndex" /> 30 </declare-styleable> 31 32 <attr name="backgroundRadioSelected" format="reference|color" /> 33 <attr name="backgroundRadio" format="reference|color" /> 34 <attr name="radioButtonNum" format="reference|integer" /> 35 <attr name="backgroundText" format="reference|color" /> 36 <attr name="contentTextColor" format="reference|color" /> 37 <attr name="contentTextSize" format="reference|dimension" /> 38 <attr name="texts" format="reference" /> 39 <attr name="textMarginLeft" format="reference|dimension" /> 40 <attr name="textMarginRight" format="reference|dimension" /> 41 <attr name="textMarginTop" format="reference|dimension" /> 42 <attr name="textMarginBottom" format="reference|dimension" /> 43 <attr name="imageMarginLeft" format="reference|dimension" /> 44 <attr name="imageMarginRight" format="reference|dimension" /> 45 <attr name="imageMarginTop" format="reference|dimension" /> 46 <attr name="imageMarginBottom" format="reference|dimension" /> 47 48 <attr name="unitMarginLeft" format="reference|dimension" /> 49 <attr name="unitMarginRight" format="reference|dimension" /> 50 <attr name="unitMarginTop" format="reference|dimension" /> 51 <attr name="unitMarginBottom" format="reference|dimension" /> 52 53 <attr name="selectedIndex" format="reference|integer" /> 54 55 <attr name="order"> 56 <enum name="imageFirst" value="0" /> 57 <enum name="textFirst" value="1" /> 58 </attr> 59 60 </resources>
string资源:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">VariedRadioButton</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string-array name="city">
<item>中国</item>
<item>美国</item>
<item>俄罗斯</item>
<item>英国</item>
<item>德国</item>
</string-array>
</resources>
尺寸转换工具类:(此类是在网上找的资源)
1 package cn.carbs.variedradiobutton.util;
2
3 import android.content.Context;
4
5 /**
6 * dp、sp 转换为 px 的工具类
7 */
8 public class DisplayUtil {
9 /**
10 * 将px值转换为dip或dp值,保证尺寸大小不变
11 *
12 * @param pxValue
13 * @param scale
14 * @return
15 */
16 public static int px2dip(Context context, float pxValue) {
17 final float scale = context.getResources().getDisplayMetrics().density;
18 return (int) (pxValue / scale + 0.5f);
19 }
20
21 /**
22 * 将dip或dp值转换为px值,保证尺寸大小不变
23 *
24 * @param dipValue
25 * @param scale
26 * @return
27 */
28 public static int dip2px(Context context, float dipValue) {
29 final float scale = context.getResources().getDisplayMetrics().density;
30 return (int) (dipValue * scale + 0.5f);
31 }
32
33 /**
34 * 将px值转换为sp值,保证文字大小不变
35 *
36 * @param pxValue
37 * @param fontScale
38 * @return
39 */
40 public static int px2sp(Context context, float pxValue) {
41 final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
42 return (int) (pxValue / fontScale + 0.5f);
43 }
44
45 /**
46 * 将sp值转换为px值,保证文字大小不变
47 *
48 * @param spValue
49 * @param fontScale
50 * @return
51 */
52 public static int sp2px(Context context, float spValue) {
53 final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
54 return (int) (spValue * fontScale + 0.5f);
55 }
56 }
使用方法:
1.在xml布局文件中:由于用到了自定义属性,因此需要添加命名空间xmlns:app="http://schemas.android.com/apk/res/cn.carbs.variedradiobutton"
<cn.carbs.variedradiobutton.view.VariedRadioButton android:id="@+id/v" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#33333333" android:text="@string/hello_world" app:backgroundRadio="@drawable/button_unchecked" app:backgroundRadioSelected="@drawable/button_checked" app:backgroundText="#333333" app:imageMarginLeft="30dp" app:optionsOrientation="horizontal" app:order="imageFirst" app:radioButtonNum="5" app:selectedIndex="1" app:textMarginLeft="0dp" app:texts="@array/city" />
原理很简单:
VariedRadioButton继承了ViewGroup(LinearLayout),通过代码添加成对的imageview+textview来实现radiobutton的效果。主要属性的说明:
app:backgroundRadio 定义未被选中的radiobutton的背景
app:backgroundRadioSelected 定义已被选中的radiobutton的背景
app:backgroundText 定义textview的背景
app:imageMarginLeft 定义imageview距离左侧控件间距
app:order="imageFirst" imageview在左
app:order="textFirst" 则是textview在左
app:radioButtonNum="5" 一共包含多少个“radiobutton”
app:selectedIndex="1" 设置初始的选中的按钮,从0开始
app:texts="@array/city" 定义所有的“radiobutton”使用的string资源,如果array的length小于num,则后面的radiobutton的文字设置为空
来源:https://www.cnblogs.com/carbs/p/5142001.html