zoukankan      html  css  js  c++  java
  • 【原创】可以换行的RadioGroup

    0、效果截图:

    以上两个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      * 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      * 获取当前选中子控件的id
    115      * @return 当前选中子控件的id
    116      */
    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         @Override
    164         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 改变的ID
    186          */
    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     @Override
    206     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     @Override
    250     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     @Override
    281     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
    282         // 设置子空间添加移除监听
    283         mPassThroughListener.mOnHierarchyChangeListener = listener;
    284     }
    285     @Override
    286     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     @Override
    297     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     @Override
    325     public LayoutParams generateLayoutParams(AttributeSet attrs) {
    326         return new FNRadioGroup.LayoutParams(getContext(), attrs);
    327     }
    328     @Override
    329     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    330         return p instanceof RadioGroup.LayoutParams;
    331     }
    332     @Override
    333     protected LayoutParams generateDefaultLayoutParams() {
    334         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    335     }
    336     @Override
    337     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    338         super.onInitializeAccessibilityEvent(event);
    339         event.setClassName(RadioGroup.class.getName());
    340     }
    341     @Override
    342     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 recursion
    349             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 missing
    367                 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方法设置即可。

    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内组件换行,感谢分享~~~

  • 相关阅读:
    几个常见的在线评测系统,及我的点评
    信息学奥赛培训教材推荐
    致,青春
    文明小博客,管理员及网址列表
    NOIP2013,复赛及同步赛,报名及比赛,专题页面
    浅谈浏览器兼容性问题-(1)产生、看待与思考
    前端经典笔试题(腾讯前端,三栏布局)
    浅谈web语义化
    浅谈表现与数据分离
    浅谈w3c标准
  • 原文地址:https://www.cnblogs.com/halfmanhuang/p/5478569.html
Copyright © 2011-2022 走看看