Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 【原創】可以換行的RadioGroup,換行radiogroup

【原創】可以換行的RadioGroup,換行radiogroup

編輯:關於android開發

【原創】可以換行的RadioGroup,換行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             
 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內組件換行,感謝分享~~~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved