android自带的RadioGroup是继承自LinearLayout,如果布局的时候不是直接写radiobutton,即radiobutton外面还包了一层容器,这时分组是不成功的,因为查找不到radiobutton,如果要实现这种效果呢,于是看了RadioGroup的源码,发现问题在于addView方法和自定义的PassThroughHierarchyChangeListener;

下面就这两个地方动手脚,先拷贝源码,然后去掉RadioGroup(Context context, AttributeSet attrs) 中的方法,我新增了一个方法,查找viewGroup控件中的radioButton,

/** 查找radioButton控件 */
public RadioButton findRadioButton(ViewGroup group) {
RadioButton resBtn = null;
int len = group.getChildCount();
for (int i = 0; i < len; i++) {
if (group.getChildAt(i) instanceof RadioButton) {
resBtn = (RadioButton) group.getChildAt(i);
} else if (group.getChildAt(i) instanceof ViewGroup) {
findRadioButton((ViewGroup) group.getChildAt(i));
}
}
return resBtn;
}

addView的实现如下:

 @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());
}
} else if (child instanceof ViewGroup) { //这里是我添加的代码
14 final RadioButton button = findRadioButton((ViewGroup) child);
15 if (button.isChecked()) {
16 mProtectFromCheckedChange = true;
17 if (mCheckedId != -1) {
18 setCheckedStateForView(mCheckedId, false);
19 }
20 mProtectFromCheckedChange = false;
21 setCheckedId(button.getId());
22 }
23 } super.addView(child, index, params);
}

然后在自定义的PassThroughHierarchyChangeListener中修改:

private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; public void onChildViewAdded(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
int id = child.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = child.hashCode();
child.setId(id);
}
((RadioButton) child)
.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (parent == MyRadioGroup.this //这里是我添加的代码
&& child instanceof ViewGroup) {
RadioButton btn = findRadioButton((ViewGroup) child);
int id = btn.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = btn.hashCode();
btn.setId(id);
}
btn.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
} public void onChildViewRemoved(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeListener(null);
} else if (parent == MyRadioGroup.this
&& child instanceof ViewGroup) {
findRadioButton((ViewGroup) child).setOnCheckedChangeListener(
null);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}

做好了上面的步骤,下面布局就可以按照自己的意思来了,并且分组效果也不会影响

下面我的布局文件demo:

 <com.huaheng.wy.view.MyRadioGroup
android:id="@+id/rg"
android:layout_width="fill_parent"
android:layout_height="65dp"
android:layout_alignParentBottom="true"
android:background="@drawable/yst_i_bottom"
android:orientation="horizontal" > <!-- 首页 --> <FrameLayout
android:id="@+id/left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1" > <RadioButton
android:id="@+id/rbtn_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@null"
android:button="@null"
android:drawableTop="@drawable/yst_i_bottomhome_selector"
android:gravity="center_horizontal"
android:text="首页"
android:textColor="@color/white"
android:textSize="@dimen/font_5" /> <Button
android:id="@+id/btn_home_point"
android:layout_width="21dp"
android:layout_height="20dp"
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginTop="0dp"
android:background="@drawable/notice_bg"
android:text="1"
android:textColor="@color/white"
android:textSize="@dimen/font_6"
android:visibility="gone" />
</FrameLayout>
<!-- 历史 --> <RadioButton
android:id="@+id/rbtn_his"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:background="@null"
android:button="@null"
android:drawableTop="@drawable/yst_i_bottomhis_selector"
android:gravity="center_horizontal"
android:text="历史"
android:textColor="@color/white"
android:textSize="@dimen/font_5" /> <!-- 扫描 --> <Button
android:id="@+id/rbtn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/scan_selector"
android:button="@null"
android:gravity="center_horizontal|bottom"
android:paddingBottom="7dp"
android:text="扫描"
android:textColor="@color/white"
android:textSize="@dimen/font_5" /> <RadioButton
android:id="@+id/rbtn_seek"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:background="@null"
android:button="@null"
android:drawableTop="@drawable/yst_i_bottomseek_selector"
android:gravity="center_horizontal"
android:text="搜索"
android:textColor="@color/white"
android:textSize="@dimen/font_5" /> <RadioButton
android:id="@+id/rbtn_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:background="@null"
android:button="@null"
android:drawableTop="@drawable/yst_i_bottommore_selector"
android:gravity="center_horizontal"
android:text="更多"
android:textColor="@color/white"
android:textSize="@dimen/font_5" />
</com.huaheng.wy.view.MyRadioGroup>

所有实现代码都在这里,下面我就把自定义的radioGroup的代码,完整的贴出来

/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package com.huaheng.wy.view; import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.RadioButton; public class MyRadioGroup extends LinearLayout {
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
// tracks children radio buttons checked state
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
// when true, mOnCheckedChangeListener discards events
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener; public MyRadioGroup(Context context) {
super(context);
setOrientation(VERTICAL);
init();
} public MyRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} private void init() {
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
} @Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
// the user listener is delegated to our pass-through listener
mPassThroughListener.mOnHierarchyChangeListener = listener;
} @Override
protected void onFinishInflate() {
super.onFinishInflate(); // checks the appropriate radio button as requested in the XML file
if (mCheckedId != -1) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
} @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());
}
} else if (child instanceof ViewGroup) {
final RadioButton button = findRadioButton((ViewGroup) child);
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} super.addView(child, index, params);
} /** 查找radioButton控件 */
public RadioButton findRadioButton(ViewGroup group) {
RadioButton resBtn = null;
int len = group.getChildCount();
for (int i = 0; i < len; i++) {
if (group.getChildAt(i) instanceof RadioButton) {
resBtn = (RadioButton) group.getChildAt(i);
} else if (group.getChildAt(i) instanceof ViewGroup) {
findRadioButton((ViewGroup) group.getChildAt(i));
}
}
return resBtn;
} /**
* <p>
* Sets the selection to the radio button whose identifier is passed in
* parameter. Using -1 as the selection identifier clears the selection;
* such an operation is equivalent to invoking {@link #clearCheck()}.
* </p>
*
* @param id
* the unique id of the radio button to select in this group
*
* @see #getCheckedRadioButtonId()
* @see #clearCheck()
*/
public void check(int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
} if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
} if (id != -1) {
setCheckedStateForView(id, true);
} setCheckedId(id);
} private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
} private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof RadioButton) {
((RadioButton) checkedView).setChecked(checked);
}
} /**
* <p>
* Returns the identifier of the selected radio button in this group. Upon
* empty selection, the returned value is -1.
* </p>
*
* @return the unique id of the selected radio button in this group
*
* @see #check(int)
* @see #clearCheck()
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
} /**
* <p>
* Clears the selection. When the selection is cleared, no radio button in
* this group is selected and {@link #getCheckedRadioButtonId()} returns
* null.
* </p>
*
* @see #check(int)
* @see #getCheckedRadioButtonId()
*/
public void clearCheck() {
check(-1);
} /**
* <p>
* Register a callback to be invoked when the checked radio button changes
* in this group.
* </p>
*
* @param listener
* the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
} /**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyRadioGroup.LayoutParams(getContext(), attrs);
} /**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MyRadioGroup.LayoutParams;
} @Override
protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
} /**
* <p>
* This set of layout parameters defaults the width and the height of the
* children to {@link #WRAP_CONTENT} when they are not specified in the XML
* file. Otherwise, this class ussed the value read from the XML file.
* </p>
*
* <p>
* See {@link android.R.styleable#LinearLayout_Layout LinearLayout
* Attributes} for a list of all child view attributes that this class
* supports.
* </p>
*
*/
public static class LayoutParams extends LinearLayout.LayoutParams {
/**
* {@inheritDoc}
*/
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
} /**
* {@inheritDoc}
*/
public LayoutParams(int w, int h) {
super(w, h);
} /**
* {@inheritDoc}
*/
public LayoutParams(int w, int h, float initWeight) {
super(w, h, initWeight);
} /**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
} /**
* {@inheritDoc}
*/
public LayoutParams(MarginLayoutParams source) {
super(source);
} /**
* <p>
* Fixes the child's width to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the
* child's height to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not
* specified in the XML file.
* </p>
*
* @param a
* the styled attributes set
* @param widthAttr
* the width attribute to fetch
* @param heightAttr
* the height attribute to fetch
*/
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr,
int heightAttr) { if (a.hasValue(widthAttr)) {
width = a.getLayoutDimension(widthAttr, "layout_width");
} else {
width = WRAP_CONTENT;
} if (a.hasValue(heightAttr)) {
height = a.getLayoutDimension(heightAttr, "layout_height");
} else {
height = WRAP_CONTENT;
}
}
} /**
* <p>
* Interface definition for a callback to be invoked when the checked radio
* button changed in this group.
* </p>
*/
public interface OnCheckedChangeListener {
/**
* <p>
* Called when the checked radio button has changed. When the selection
* is cleared, checkedId is -1.
* </p>
*
* @param group
* the group in which the checked radio button has changed
* @param checkedId
* the unique identifier of the newly checked radio button
*/
public void onCheckedChanged(MyRadioGroup group, int checkedId);
} private class CheckedStateTracker implements
CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
} mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false; int id = buttonView.getId();
setCheckedId(id);
}
} /**
* <p>
* A pass-through listener acts upon the events and dispatches them to
* another listener. This allows the table layout to set its own internal
* hierarchy change listener without preventing the user to setup his.
* </p>
*/
private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; public void onChildViewAdded(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
int id = child.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = child.hashCode();
child.setId(id);
}
((RadioButton) child)
.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (parent == MyRadioGroup.this
&& child instanceof ViewGroup) {
RadioButton btn = findRadioButton((ViewGroup) child);
int id = btn.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = btn.hashCode();
btn.setId(id);
}
btn.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
} public void onChildViewRemoved(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeListener(null);
} else if (parent == MyRadioGroup.this
&& child instanceof ViewGroup) {
findRadioButton((ViewGroup) child).setOnCheckedChangeListener(
null);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
}

MyRadioGroup

android自定义RadioGroup实现可以添加多种布局的更多相关文章

  1. Android开发之ListView添加多种布局效果演示

    在这个案例中展示的新闻列表,使用到ListView控件,然后在适配器中添加多种布局效果,这里通过重写BaseAdapter类中的 getViewType()和getItemViewType()来做判断 ...

  2. Android 在程序中动态添加 View 布局或控件

    有时我们需要在程序中动态添加布局或控件等,下面用程序来展示一下相应的方法: 1.addView 添加View到布局容器 2.removeView 在布局容器中删掉已有的View 3.LayoutPar ...

  3. android 自定义radiogroup的两种方式

    这里先备注下 listview+radiobutton实现  浅显易懂 http://www.haolizi.net/example/view_3312.html 在radiogoup原生态源码的基础 ...

  4. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  5. Recyclerview添加头布局和尾布局,点击效果

    简介: 本篇博客主要包括recyclerview添加多种布局以及添加头布局和尾布局,还有item点击事件 思路: 主要重写Recyclerview.Adapter中的一些方法 1.public int ...

  6. 2.Android 自定义通用的Item布局

    转载:http://www.jianshu.com/p/e7ba4884dcdd BaseItemLayout 简介 在工作中经常会遇到下面的一些布局,如图标红处: 05.png 07.png 08. ...

  7. Android RecycleView多种布局实现(工厂模式)

    RecycleView是个很常用的控件,很多APP中都可以看到它的身影,同时它也是个很难用的控件,主要就难在多种布局的实现. 在<第一行代码—Android>这本书里边有个RecycleV ...

  8. React Native之(支持iOS与Android)自定义单选按钮(RadioGroup,RadioButton)

    React Native之(支持iOS与Android)自定义单选按钮(RadioGroup,RadioButton) 一,需求与简单介绍 在开发项目时发现RN没有给提供RadioButton和Rad ...

  9. Android自定义视图一:扩展现有的视图,添加新的XML属性

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

随机推荐

  1. Oracle数据创建表空间

    一.直接在服务器端通过sqlplus命令行创建: 如果您用的是Linux系统,那么Oracle用户名为oracle.同时,您是在oracle服务器上操作. 如果是在Windows系统下, 请先点击“开 ...

  2. MySQL group_concat 1024 大小

    1. GROUP_CONCAT有个最大长度的限制,超过最大长度就会被截断掉,你可以通过下面的语句获得: SELECT @@global.group_concat_max_len; show varia ...

  3. 网站标题ico那些事

    浏览器打开一个网页都会有一个标题,用来显示当前页面的相关内容,如网站名称或者一篇文章的大标题,而定义它应该显示啥的话完全由HTML中title标签的内容决定. 如我们的大博客园:

  4. MySQL基础操作(一)

    1. 连接数据库mysql -h localhost -u root -p 123456 2. 查看数据库版本select version(); 3. 查看数据库支持的存储引擎和当前默认存储引擎sho ...

  5. HOWTO - 压缩封装的Setup.exe(纯MSI)安装包获取运行Log

    在InstallShield中我们有时即使选择Basic MSI工程,但有时还是将MSI封装到Setup.exe中交付. 那么出现运行异常如何获取Log,可以用下面的方法: Setup.exe /v& ...

  6. 【干货】个人工作文档节选:XAML MVVM 框架易用性细节优化Tips

    1    易用性细节优化 1.1 代码片段 在ViewModel内,会有大量重复性的在Property set中激发 INotifyPropertyChanged.PropertyChanged 事件 ...

  7. Redis 数据结构之Keys

    这是Redis官方文档的keys列表 (1) set  key value--设置某个键为某个值 (2) get key -- 获取设置的值 (3)del key -- 删除设置的键 (4)expir ...

  8. Hello Kraken.js!

      前言 kraken.js 由paypal 公司开源的一个用于快速开发基于Express.js框架应用的快速开发工具, 因为kraken 并没有在Express.js基础上更改多少东西,只是在原来的 ...

  9. 40页PPT勾画“互联网颠覆性思维”----诠释互联网思维

    本文PPT内容涉及移动互联网的三个分支——移动电商.在线教育和新媒体. 不同领域一直是可以相互借鉴.相互渗透.相互学习的,在盈利模式和思维方式上有很多是共通的.  

  10. nginx location 的配置

    一.基本语法:location [=|~|~*|^~] /uri/ { … } 二.分类: 1.基本location:以“ = ”或“ ^~ ”为前缀或者没有任何前缀的 /uri/ 2.正则locat ...