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

时间:2022-05-09 02:08:02

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接口。在需要取值时,直接调用一行代码即可。

效果如下:

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

代码如下:

主界面:

 package cn.carbs.variedradiobutton;

 import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import cn.carbs.variedradiobutton.view.VariedRadioButton; public class MainActivity extends Activity { VariedRadioButton variedRadioButton;
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
variedRadioButton = (VariedRadioButton)findViewById(R.id.v);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
variedRadioButton.setSelectedIndex(4);
}
}); variedRadioButton.setSelectedIndex(3);
} }

自定义view的代码:

 package cn.carbs.variedradiobutton.view;

 import java.util.ArrayList;

 import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import cn.carbs.variedradiobutton.R;
import cn.carbs.variedradiobutton.util.DisplayUtil; public class VariedRadioButton extends LinearLayout implements View.OnClickListener{ private static final String TAG = "wang"; private static final int ORDER_IMAGE_FIRST = 0;
private static final int ORDER_TEXT_FIRST = 1; private static final int DEFAULT_SELECTED_INDEX = 0; private static final float DEFAULT_MARGIN = 0f;
private static final int DEFAULT_ORDER = ORDER_IMAGE_FIRST;
private static final int DEFAULT_ORIENTATION = LinearLayout.HORIZONTAL;
private static final int DEFAULT_NUM = 2;
private static final int DEFAULT_TEXT_COLOR = 0xff000000;
private static final float DEFAULT_TEXT_VIEW_TEXT_SIZE_SP = 16; private static final int DEFAULT_TEXTS_RES = 0;
private static final int DEFAULT_IMAGE_RES = 0; private Context mContext;
private int mDrawableBackgroundRadioSelected;
private int mDrawableBackgroundRadio;
private Drawable mDrawableBackgroundText;
private float mTextMarginLeft = DEFAULT_MARGIN;
private float mTextMarginRight = DEFAULT_MARGIN;
private float mTextMarginTop = DEFAULT_MARGIN;
private float mTextMarginBottom = DEFAULT_MARGIN;
private float mImageMarginLeft = DEFAULT_MARGIN;
private float mImageMarginRight = DEFAULT_MARGIN;
private float mImageMarginTop = DEFAULT_MARGIN;
private float mImageMarginBottom = DEFAULT_MARGIN;
private float mUnitMarginLeft = DEFAULT_MARGIN;
private float mUnitMarginRight = DEFAULT_MARGIN;
private float mUnitMarginTop = DEFAULT_MARGIN;
private float mUnitMarginBottom = DEFAULT_MARGIN; private int mOrder = DEFAULT_ORDER;
private int mOrientation = DEFAULT_ORIENTATION;
private int mNum = DEFAULT_NUM;
private int mTextColor = DEFAULT_TEXT_COLOR;
private float mTextSize = DEFAULT_TEXT_VIEW_TEXT_SIZE_SP;
private int mTextsRes = DEFAULT_TEXTS_RES;
private String[] mTexts;
private ArrayList<ImageView> mImageViews = new ArrayList();
private ArrayList<TextView> mTextViews = new ArrayList(); private View mContentView = null;
private LinearLayout mContainer = null; private Object mTagTextView = new Object();
private Object mTagImageView = new Object(); private int mSelectedIndex = DEFAULT_SELECTED_INDEX; public VariedRadioButton(Context context) {
this(context, null);
} public VariedRadioButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public VariedRadioButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
mContentView = inflate(context, R.layout.view_varied_radio_button, this);
mContainer = (LinearLayout)mContentView.findViewById(R.id.container); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.variedRadioButton); final int count = a.getIndexCount();
for (int i = 0; i < count; ++i) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.variedRadioButton_backgroundRadioSelected:
mDrawableBackgroundRadioSelected = a.getResourceId(attr, DEFAULT_IMAGE_RES);
break;
case R.styleable.variedRadioButton_backgroundRadio:
mDrawableBackgroundRadio = a.getResourceId(attr, DEFAULT_IMAGE_RES);
break;
case R.styleable.variedRadioButton_backgroundText:
mDrawableBackgroundText = a.getDrawable(attr);
break;
case R.styleable.variedRadioButton_textMarginLeft:
mTextMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_textMarginRight:
mTextMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_textMarginTop:
mTextMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_textMarginBottom:
mTextMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_imageMarginLeft:
mImageMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_imageMarginRight:
mImageMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_imageMarginTop:
mImageMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_imageMarginBottom:
mImageMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_unitMarginLeft:
mUnitMarginLeft = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_unitMarginRight:
mUnitMarginRight = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_unitMarginTop:
mUnitMarginTop = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_unitMarginBottom:
mUnitMarginBottom = a.getDimension(attr, DEFAULT_MARGIN);
break;
case R.styleable.variedRadioButton_order:
mOrder = a.getInt(attr, DEFAULT_ORDER);
break;
case R.styleable.variedRadioButton_radioButtonNum:
mNum = a.getInt(attr, DEFAULT_NUM);
break;
case R.styleable.variedRadioButton_contentTextColor:
mTextColor = a.getColor(attr, DEFAULT_TEXT_COLOR);
break;
case R.styleable.variedRadioButton_contentTextSize:
mTextSize = DisplayUtil.px2sp(mContext, a.getDimensionPixelSize(attr, DisplayUtil.sp2px(mContext, DEFAULT_TEXT_VIEW_TEXT_SIZE_SP)));
break;
case R.styleable.variedRadioButton_optionsOrientation:
mOrientation = a.getInt(attr, DEFAULT_ORIENTATION);
break;
case R.styleable.variedRadioButton_texts:
mTextsRes = a.getResourceId(attr, DEFAULT_TEXTS_RES);
mTexts = mContext.getResources().getStringArray(mTextsRes);
break;
case R.styleable.variedRadioButton_selectedIndex:
mSelectedIndex = a.getInt(attr, DEFAULT_SELECTED_INDEX);
break; }
}
a.recycle(); mContainer.setOrientation(mOrientation);
LinearLayout.LayoutParams paramsUnit = null;
if(mOrientation == LinearLayout.HORIZONTAL){
paramsUnit = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);
}else{
paramsUnit = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 0);
} paramsUnit.weight = 1;
paramsUnit.leftMargin = (int)mUnitMarginLeft;
paramsUnit.rightMargin = (int)mUnitMarginRight;
paramsUnit.topMargin = (int)mUnitMarginTop;
paramsUnit.bottomMargin = (int)mUnitMarginBottom; LinearLayout.LayoutParams paramsImageView = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
paramsImageView.leftMargin = (int)mImageMarginLeft;
paramsImageView.rightMargin = (int)mImageMarginRight;
paramsImageView.topMargin = (int)mImageMarginTop;
paramsImageView.bottomMargin = (int)mImageMarginBottom; LinearLayout.LayoutParams paramsTextView = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
paramsTextView.leftMargin = (int)mTextMarginLeft;
paramsTextView.rightMargin = (int)mTextMarginRight;
paramsTextView.topMargin = (int)mTextMarginTop;
paramsTextView.bottomMargin = (int)mTextMarginBottom; for(int n = 0; n < mNum; n++){ LinearLayout ll = new LinearLayout(mContext);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.setGravity(Gravity.CENTER_VERTICAL); ImageView image = new ImageView(mContext);
image.setBackgroundResource(mDrawableBackgroundRadio);
image.setLayoutParams(paramsImageView);
image.setTag(mTagImageView); TextView text = new TextView(mContext);
text.setGravity(Gravity.CENTER);
if(n < mTexts.length){
text.setText(mTexts[n]);
}
text.setLayoutParams(paramsTextView);
text.setTag(mTagTextView);
text.setTextSize(mTextSize);
text.setTextColor(mTextColor); if(mOrder == ORDER_IMAGE_FIRST){
ll.addView(image);
ll.addView(text);
}else{
ll.addView(text);
ll.addView(image);
}
ll.setTag(n);
ll.setOnClickListener(this); mImageViews.add(image);
mTextViews.add(text);
mContainer.addView(ll, paramsUnit);
}
mContainer.setWeightSum(mNum);
setSelectedIndex(mSelectedIndex);
} public void setRadioButtonNum(int num){
mNum = num;
} public void setTextsRes(int textsRes){
mTextsRes = textsRes;
mTexts = mContext.getResources().getStringArray(mTextsRes);
} public void setTexts(String[] texts){
mTexts = texts;
} public void setSelectedIndex(int selectedIndex){
if(selectedIndex >= 0 && selectedIndex < mNum){
refreshView(selectedIndex);
}else{ }
} public int getSelectedIndex(){
return mSelectedIndex;
} @Override
public void onClick(View v) {
Integer index = (Integer)v.getTag();
if(index != null){
refreshView(index);
}else{
throw new IllegalArgumentException("need to set a tag to LinearLayout element");
}
} private void refreshView(int selectedIndex){
mSelectedIndex = selectedIndex;
LinearLayout clickedLL = null;
ImageView image = null;
for(int i = 0; i < mNum; i++){
clickedLL = (LinearLayout)this.findViewWithTag(i);
image = (ImageView)clickedLL.findViewWithTag(mTagImageView);
if(i == selectedIndex){
image.setBackgroundResource(mDrawableBackgroundRadioSelected);
}else{
image.setBackgroundResource(mDrawableBackgroundRadio);
}
}
} }

布局文件:

activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/cn.carbs.variedradiobutton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" > <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" /> <Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button" /> </LinearLayout>

view_varied_radio_button.xml :

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" > </LinearLayout>

自定义属性:

 <?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="variedRadioButton">
<attr name="backgroundRadio" />
<attr name="backgroundRadioSelected" />
<attr name="radioButtonNum" />
<attr name="backgroundText" />
<attr name="order" />
<attr name="contentTextColor" />
<attr name="contentTextSize" />
<attr name="textMarginLeft" />
<attr name="textMarginRight" />
<attr name="textMarginTop" />
<attr name="textMarginBottom" />
<attr name="imageMarginLeft" />
<attr name="imageMarginRight" />
<attr name="imageMarginTop" />
<attr name="imageMarginBottom" />
<attr name="unitMarginLeft" />
<attr name="unitMarginRight" />
<attr name="unitMarginTop" />
<attr name="unitMarginBottom" />
<attr name="texts" />
<attr name="optionsOrientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<attr name="selectedIndex" />
</declare-styleable> <attr name="backgroundRadioSelected" format="reference|color" />
<attr name="backgroundRadio" format="reference|color" />
<attr name="radioButtonNum" format="reference|integer" />
<attr name="backgroundText" format="reference|color" />
<attr name="contentTextColor" format="reference|color" />
<attr name="contentTextSize" format="reference|dimension" />
<attr name="texts" format="reference" />
<attr name="textMarginLeft" format="reference|dimension" />
<attr name="textMarginRight" format="reference|dimension" />
<attr name="textMarginTop" format="reference|dimension" />
<attr name="textMarginBottom" format="reference|dimension" />
<attr name="imageMarginLeft" format="reference|dimension" />
<attr name="imageMarginRight" format="reference|dimension" />
<attr name="imageMarginTop" format="reference|dimension" />
<attr name="imageMarginBottom" format="reference|dimension" /> <attr name="unitMarginLeft" format="reference|dimension" />
<attr name="unitMarginRight" format="reference|dimension" />
<attr name="unitMarginTop" format="reference|dimension" />
<attr name="unitMarginBottom" format="reference|dimension" /> <attr name="selectedIndex" format="reference|integer" /> <attr name="order">
<enum name="imageFirst" value="0" />
<enum name="textFirst" value="1" />
</attr> </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>

尺寸转换工具类:(此类是在网上找的资源)

 package cn.carbs.variedradiobutton.util;

 import android.content.Context;

 /**
* dp、sp 转换为 px 的工具类
*/
public class DisplayUtil {
/**
* 将px值转换为dip或dp值,保证尺寸大小不变
*
* @param pxValue
* @param scale
* @return
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
} /**
* 将dip或dp值转换为px值,保证尺寸大小不变
*
* @param dipValue
* @param scale
* @return
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
} /**
* 将px值转换为sp值,保证文字大小不变
*
* @param pxValue
* @param fontScale
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
} /**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @param fontScale
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}

使用方法:

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的文字设置为空