0、效果截图:
images/loading.gif' data-original="http://images2015.cnblogs.com/blog/522798/201605/522798-20160510171720280-1668691937.png" />
以上两个RadioGroup均使用FNRadioGroup实现。
1、控件代码:
1 public class FNRadioGroup extends ViewGroup { 2 3 /** 没有ID */ 4 private final static int NO_ID = -1; 5 6 /** 当前选中的子控件ID */ 7 private int mCheckedId = NO_ID; 8 9 /** 子控件选择改变** */ 10 private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener; 11 12 /** 为true时,不处理子控件选择事件 */ 13 private boolean mProtectFromCheckedChange = false; 14 15 /** 选择改变** */ 16 private OnCheckedChangeListener mOnCheckedChangeListener; 17 18 /** 子控件添加移除** */ 19 private PassThroughHierarchyChangeListener mPassThroughListener; 20 21 /** 子控件左边距 */ 22 private int childMarginLeft = 0; 23 24 /** 子控件右边距 */ 25 private int childMarginRight = 0; 26 27 /** 子控件上边距 */ 28 private int childMarginTop = 0; 29 30 /** 子控件下边距 */ 31 private int childMarginBottom = 0; 32 33 /** 子空间高度 */ 34 private int childHeight; 35 36 /** 37 * 默认构造方法 38 */ 39 public FNRadioGroup(Context context) { 40 super(context); 41 init(); 42 } 43 44 /** 45 * 46 */ 47 public FNRadioGroup(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 50 // 获取自定义属性checkedButton 51 TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.FNRadioGroup) ; 52 // 读取默认选中id 53 int value = attributes.getResourceId(R.styleable.FNRadioGroup_checkedButton, NO_ID); 54 if (value != NO_ID) { 55 // 如果为设置checkButton属性,保持默认值NO_ID 56 mCheckedId = value; 57 } 58 // 读取子控件左边距 59 childMarginLeft = attributes.getLayoutDimension(R.styleable.FNRadioGroup_childMarginLeft, childMarginLeft); 60 if (childMarginLeft < 0) { 61 childMarginLeft = 0; 62 } 63 // 读取子控件右边距 64 childMarginRight = attributes.getLayoutDimension(R.styleable.FNRadioGroup_childMarginRight, childMarginRight); 65 if (childMarginRight < 0) { 66 childMarginRight = 0; 67 } 68 // 读取子控件上边距 69 childMarginTop = attributes.getLayoutDimension(R.styleable.FNRadioGroup_childMarginTop, childMarginTop); 70 if (childMarginTop < 0) { 71 childMarginTop = 0; 72 } 73 // 读取子控件下边距 74 childMarginBottom = attributes.getLayoutDimension(R.styleable.FNRadioGroup_childMarginBottom, childMarginBottom); 75 if (childMarginBottom < 0) { 76 childMarginBottom = 0; 77 } 78 attributes.recycle(); 79 // 调用二级构造 80 init(); 81 } 82 83 /** 84 * 设置子控件边距 85 * @param l 左边距 86 * @param t 上边距 87 * @param r 右边距 88 * @param b 下边距 89 */ 90 public void setChildMargin(int l, int t, int r, int b) { 91 childMarginTop = t; 92 childMarginLeft = l; 93 childMarginRight = r; 94 childMarginBottom = b; 95 } 96 97 /** 98 * 选中子控件为id的组件为选中项 99 */100 public void check(int id) {101 if (id != -1 && (id == mCheckedId)) {102 return;103 }104 if (mCheckedId != -1) {105 setCheckedStateForView(mCheckedId, false);106 }107 if (id != -1) {108 setCheckedStateForView(id, true);109 }110 setCheckedId(id);111 }112 113 /**114 * 获取当前选中子控件的id115 * @return 当前选中子控件的id116 */117 public int getCheckedRadioButtonId() {118 return mCheckedId;119 }120 121 /**122 * 清除当前选中项123 */124 public void clearCheck() {125 check(-1);126 }127 128 /**129 * 设置选中改变监听130 * @param listener 选中改变监听131 */132 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {133 mOnCheckedChangeListener = listener;134 }135 136 /**137 * 布局参数138 */139 public static class LayoutParams extends ViewGroup.LayoutParams {140 /**141 * 142 * @param c 页面引用143 * @param attrs 144 */145 public LayoutParams(Context c, AttributeSet attrs) {146 super(c, attrs);147 }148 /**149 * 默认构造150 * @param w 宽度151 * @param h 高度152 */153 public LayoutParams(int w, int h) {154 super(w, h);155 }156 /**157 * 父传递构造158 * @param p ViewGroup.LayoutParams对象159 */160 public LayoutParams(ViewGroup.LayoutParams p) {161 super(p);162 }163 @Override164 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {165 if (a.hasValue(widthAttr)) {166 width = a.getLayoutDimension(widthAttr, "layout_width");167 } else {168 width = WRAP_CONTENT;169 }170 if (a.hasValue(heightAttr)) {171 height = a.getLayoutDimension(heightAttr, "layout_height");172 } else {173 height = WRAP_CONTENT;174 }175 }176 }177 178 /**179 * 项目选中改变**180 */181 public interface OnCheckedChangeListener {182 /**183 * 选中项目改变回调184 * @param group 组引用185 * @param checkedId 改变的ID186 */187 void onCheckedChanged(FNRadioGroup group, int checkedId);188 }189 190 /********************************************私有方法*******************************************/191 192 /**193 * 二级构造方法194 */195 private void init() {196 197 // 初始化子控件选择监听198 mChildOnCheckedChangeListener = new CheckedStateTracker();199 200 // 初始化子控件添加移除**201 mPassThroughListener = new PassThroughHierarchyChangeListener();202 // 设置子控件添加移除**203 super.setOnHierarchyChangeListener(mPassThroughListener);204 }205 @Override206 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {207 ViewGroup.LayoutParams params = getLayoutParams();208 int pl = getPaddingLeft();209 int pr = getPaddingRight();210 int pt = getPaddingTop();211 int pb = getPaddingBottom();212 // 获取视图宽度213 int width = MeasureSpec.getSize(widthMeasureSpec);214 measureChildren(widthMeasureSpec, heightMeasureSpec);215 // 计算Tag最大高度(以此作为所有tag的高度)216 childHeight = 0;217 for (int i = 0; i < getChildCount(); i++) {218 int cmh = getChildAt(i).getMeasuredHeight();219 if (cmh > childHeight) {220 childHeight = cmh;221 }222 }223 // 计算本视图224 if (params.height != LayoutParams.WRAP_CONTENT) {225 // 非内容匹配的情况下226 super.onMeasure(widthMeasureSpec, heightMeasureSpec);227 } else {228 // 计算视图高度229 int currentHeight = pt;230 int currentWidth = pl;231 for (int i = 0; i < getChildCount(); i++) {232 View child = getChildAt(i);233 int childWidth = child.getMeasuredWidth();234 // 本视图加入行中是否会超过视图宽度235 if (currentWidth + childWidth + childMarginLeft + childMarginRight > width - pl - pr) {236 // 累加行高读237 currentHeight += childMarginTop + childMarginBottom + childHeight;238 currentWidth = pl;239 currentWidth += childMarginLeft + childMarginRight + childWidth;240 } else {241 // 累加行宽度242 currentWidth += childMarginLeft + childMarginRight + childWidth;243 }244 }245 currentHeight += childMarginTop + childMarginBottom + childHeight + pb;246 super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(currentHeight, MeasureSpec.EXACTLY));247 }248 }249 @Override250 protected void onLayout(boolean changed, int l, int t, int r, int b) {251 int pl = getPaddingLeft();252 int pr = getPaddingRight();253 int pt = getPaddingTop();254 int pb = getPaddingBottom();255 int width = r - l;256 // 布局Tag视图257 int currentHeight = pt;258 int currentWidth = pl;259 for (int i=0; i < getChildCount(); i++) {260 View child = getChildAt(i);261 int childWidth = child.getMeasuredWidth();262 // 本视图加入行中是否会超过视图宽度263 if (currentWidth + childWidth + childMarginLeft + childMarginRight > width - pl - pr) {264 // 累加行高读265 currentHeight += childMarginTop + childMarginBottom + childHeight;266 currentWidth = pl;267 // 布局视图268 child.layout(currentWidth + childMarginLeft, currentHeight + childMarginTop,269 currentWidth + childMarginLeft + childWidth, currentHeight + childMarginTop + childHeight);270 currentWidth += childMarginLeft + childMarginRight + childWidth;271 } else {272 // 布局视图273 child.layout(currentWidth + childMarginLeft, currentHeight + childMarginTop,274 currentWidth + childMarginLeft + childWidth, currentHeight + childMarginTop + childHeight);275 // 累加行宽度276 currentWidth += childMarginLeft + childMarginRight + childWidth;277 }278 }279 }280 @Override281 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {282 // 设置子空间添加移除监听283 mPassThroughListener.mOnHierarchyChangeListener = listener;284 }285 @Override286 protected void onFinishInflate() {287 super.onFinishInflate();288 if (mCheckedId != NO_ID) {289 // 如果读取到选中项,设置并存储选中项290 mProtectFromCheckedChange = true;291 setCheckedStateForView(mCheckedId, true);292 mProtectFromCheckedChange = false;293 setCheckedId(mCheckedId);294 }295 }296 @Override297 public void addView(View child, int index, ViewGroup.LayoutParams params) {298 if (child instanceof RadioButton) {299 final RadioButton button = (RadioButton) child;300 if (button.isChecked()) {301 mProtectFromCheckedChange = true;302 if (mCheckedId != -1) {303 setCheckedStateForView(mCheckedId, false);304 }305 mProtectFromCheckedChange = false;306 setCheckedId(button.getId());307 }308 }309 310 super.addView(child, index, params);311 }312 private void setCheckedId(int id) {313 mCheckedId = id;314 if (mOnCheckedChangeListener != null) {315 mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);316 }317 }318 private void setCheckedStateForView(int viewId, boolean checked) {319 View checkedView = findViewById(viewId);320 if (checkedView != null && checkedView instanceof RadioButton) {321 ((RadioButton) checkedView).setChecked(checked);322 }323 }324 @Override325 public LayoutParams generateLayoutParams(AttributeSet attrs) {326 return new FNRadioGroup.LayoutParams(getContext(), attrs);327 }328 @Override329 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {330 return p instanceof RadioGroup.LayoutParams;331 }332 @Override333 protected LayoutParams generateDefaultLayoutParams() {334 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);335 }336 @Override337 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {338 super.onInitializeAccessibilityEvent(event);339 event.setClassName(RadioGroup.class.getName());340 }341 @Override342 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {343 super.onInitializeAccessibilityNodeInfo(info);344 info.setClassName(RadioGroup.class.getName());345 }346 private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {347 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {348 // prevents from infinite recursion349 if (mProtectFromCheckedChange) {350 return;351 }352 mProtectFromCheckedChange = true;353 if (mCheckedId != -1) {354 setCheckedStateForView(mCheckedId, false);355 }356 mProtectFromCheckedChange = false;357 int id = buttonView.getId();358 setCheckedId(id);359 }360 }361 private class PassThroughHierarchyChangeListener implements ViewGroup.OnHierarchyChangeListener {362 private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;363 public void onChildViewAdded(View parent, View child) {364 if (parent == FNRadioGroup.this && child instanceof RadioButton) {365 int id = child.getId();366 // generates an id if it's missing367 if (id == View.NO_ID) {368 id = generateViewId();369 child.setId(id);370 }371 ((RadioButton) child).setOnCheckedChangeListener(mChildOnCheckedChangeListener);372 }373 374 if (mOnHierarchyChangeListener != null) {375 mOnHierarchyChangeListener.onChildViewAdded(parent, child);376 }377 }378 public void onChildViewRemoved(View parent, View child) {379 if (parent == FNRadioGroup.this && child instanceof RadioButton) {380 ((RadioButton) child).setOnCheckedChangeListener(null);381 }382 if (mOnHierarchyChangeListener != null) {383 mOnHierarchyChangeListener.onChildViewRemoved(parent, child);384 }385 }386 }387 }
2、
1 <declare-styleable name="FNRadioGroup">2 <attr name="checkedButton" format="integer" />3 <attr name="childMarginLeft" format="dimension"/>4 <attr name="childMarginRight" format="dimension"/>5 <attr name="childMarginTop" format="dimension"/>6 <attr name="childMarginBottom" format="dimension"/>7 </declare-styleable>
3、使用方法说明:
使用方法与RadioGroup相同,使用RadioButton作为子控件,
如果要实现网格样式,需要为子控件设置固定宽度
如果需要实现交错模式,将子控件宽度设置为WRAP_CONTENT即可。
如果需要设置子控件外边距,调用FNRadioGroup的setChildMargin方法设置即可。
PS:更多问题欢迎与我联系,如果需要转载请评论~~
后记:
网友补充了另一种实现方式如下:
1 <RadioButton 2 android:id="@+id/money_1500_Rb" 3 style="@style/radio_button_activity" 4 android:layout_marginLeft="-340dp" 5 android:layout_marginTop="50dp" 6 android:background="@drawable/bg_edittext" 7 android:gravity="center" 8 android:paddingBottom="@dimen/padding_10" 9 android:paddingTop="@dimen/padding_10"10 android:text="2" />
利用margin同样可以实现简单的RadioGroup内组件换行,感谢分享~~~
原标题:【原创】可以换行的RadioGroup
关键词: