自定义android RadioButton View,添加较为灵活的布局处理方式

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