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

效果如下:

代码如下:

主界面:

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

自定义android RadioButton View,添加较为灵活的布局处理方式的更多相关文章

  1. Android中View的layout mechanism(布局机制)

    layout mechanism Android中View的layout mechanism主要分为两个阶段:measure阶段和layout阶段.layout mechanism按照一定的顺序进行, ...

  2. android 给view添加阴影

    1.方法一: 使用 CardView 布局 <android.support.v7.widget.CardView xmlns:android="http://schemas.andr ...

  3. Android中View绘制优化之一---- 优化布局层次

    本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 前言,竟然是翻译,当然得弄的有板有眼. 照着大作家格式来咯 , - - . 译序 最近一直在做锁屏界面,之前也 ...

  4. Android ListView中添加不同的多种布局

    最近做项目要使用ListView加载不同的布局,由于自己写的代码不能贴出,故找了一篇自认为比较好的blog给分享出来,希望对用到此项技术的同学有点帮助. http://logc.at/2011/10/ ...

  5. Android中自定义样式与View的构造函数中的第三个参数defStyle的意义

    零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第 ...

  6. 【转】Android中View的绘制过程 onMeasure方法简述 附有自定义View例子

    Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android framework将会处理绘制过程,Activity只需提供它的布局的根节点. 绘制过程从布 ...

  7. Android中View的绘制过程 onMeasure方法简述 附有自定义View例子

    Android中View的绘制过程 onMeasure方法简述 附有自定义View例子 Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android fr ...

  8. Android 如何动态添加 View 并显示在指定位置。

    引子 最近,在做产品的需求的时候,遇到 PM 要求在某个按钮上添加一个新手引导动画,引导用户去点击.作为 RD,我哗啦啦的就写好相关逻辑了.自测完成后,提测,PM Review 效果. 看完后,PM ...

  9. android显示通知栏Notification以及自定义Notification的View

    遇到的最大的问题是监听不到用户清除通知栏的广播.所以是不能监听到的. 自定义通知栏的View,然后service运行时更改notification的信息. /** * Show a notificat ...

随机推荐

  1. iPad应用开发者的建议

    原文摘自Smashing Magazine<A Dad’s Plea To Developers of iPad Apps For Children> 我花了很长时间为孩子购买和测试iPa ...

  2. python通过ip获取地址

    # -*- coding: utf-8 -*- url = "http://ip.taobao.com/service/getIpInfo.php?ip=" #查找IP地址 def ...

  3. Linux 所有网卡统计查看小命令

    命令使用: [root@localhost home]# -v A1= 'BEGIN{print"---------------------------------------------- ...

  4. JSON.parse解析单引号名值对报错

    今天整理代码的时候发现JSON.pare解析时会报了一个错误,而且很难找原因,调试了几分钟没有成功,猜测可能是单双引号引起的错误.修改了单双引号后程序正常运行了,现在记录下这个bug. 关于JSON. ...

  5. SOA问题处理

    R12.1: How To Generate SOA Log For Debugging SOA Provider Issues (文档 ID 828753.1) 转到底部 In this Docum ...

  6. 【ASP.NET Web API教程】6.4 模型验证

    本文是Web API系列教程的第6.4小节 6.4 Model Validation 6.4 模型验证 摘自:http://www.asp.net/web-api/overview/formats-a ...

  7. UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别

    本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求: REQ1:一个项目内有多名项目成 ...

  8. 使用FEST-Swing测试GUI

          在Java中最常用的单元测试工具要算JUnit了.FEST-Swing是一个能够与JUnit集成的GUI测试框架.使用FEST-Swing可以更方便的对Swing进行一系列的测试.下面就说 ...

  9. Gradle中使用idea插件的一些实践

    如果你的项目使用了Gradle作为构建工具,那么你一定要使用Gradle来自动生成IDE的项目文件,无需再手动的将源代码导入到你的IDE中去了. 如果你使用的是eclipse,可以在build.gra ...

  10. Nhibernate基础使用教程以及简易封装

    1.Nhibernate简介 NHibernate是一个面向.NET环境的对象/关系数据库映射工具.对象/关系数据库映射(object/relational mapping,ORM)这个术语表示一种技 ...