android自定义RadioGroup可以添加多种布局的实现方法
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) { //这里是我添加的代码
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);
}
然后在自定义的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);
}
}
}
}