可以换行的RadioGroup

来源:互联网 发布:网络主播年收入排行榜 编辑:程序博客网 时间:2024/06/10 01:39

0、效果截图:

以上两个RadioGroup均使用FNRadioGroup实现。


 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      * XML实例构造方法 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          * XML构造142          * @param c 页面引用143          * @param attrs XML属性集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、XML属性:

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方法设置即可。

转载地址:http://www.cnblogs.com/halfmanhuang/p/5478569.html
0 0