当前位置:   article > 正文

可以实现复合控件单选的radioButton_radiobutton 复合

radiobutton 复合

开发中经常会遇到无良UI设计出一些莫名其妙 极度奇葩的单选按钮。android系统原生的radioButton虽然可以修改button属性和drawable属性做一些改变,但是依然不能很好的满足需求。

这时一般会考虑用复合的布局,其中嵌套了radioButton。虽然形式上满足了需求,但是这种布局放进radioGroup中 无法实现单选。

下面是从网上搬运的代码,自定义radioGroup可以自由嵌套,并实现单选。感谢原创大神。本屌仅仅搬运过来做个记录。

  1. package com.example.minshengdemo.coustem;
  2. import java.util.ArrayList;
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.util.AttributeSet;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.CompoundButton;
  9. import android.widget.LinearLayout;
  10. import android.widget.RadioButton;
  11. /** 可以放多种布局控件,能找到radiobutton */
  12. public class MyRadioGroup extends LinearLayout {
  13. // holds the checked id; the selection is empty by default
  14. private int mCheckedId = -1;
  15. // tracks children radio buttons checked state
  16. private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
  17. // when true, mOnCheckedChangeListener discards events
  18. private boolean mProtectFromCheckedChange = false;
  19. private OnCheckedChangeListener mOnCheckedChangeListener;
  20. private PassThroughHierarchyChangeListener mPassThroughListener;
  21. // 存放当前的radioButton
  22. private ArrayList<RadioButton> radioButtons;
  23. public MyRadioGroup(Context context) {
  24. super(context);
  25. setOrientation(VERTICAL);
  26. init();
  27. }
  28. public MyRadioGroup(Context context, AttributeSet attrs) {
  29. super(context, attrs);
  30. init();
  31. }
  32. private void init() {
  33. mChildOnCheckedChangeListener = new CheckedStateTracker();
  34. mPassThroughListener = new PassThroughHierarchyChangeListener();
  35. super.setOnHierarchyChangeListener(mPassThroughListener);
  36. radioButtons = new ArrayList<RadioButton>();
  37. }
  38. @Override
  39. public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
  40. // the user listener is delegated to our pass-through listener
  41. mPassThroughListener.mOnHierarchyChangeListener = listener;
  42. }
  43. @Override
  44. protected void onFinishInflate() {
  45. super.onFinishInflate();
  46. // checks the appropriate radio button as requested in the XML file
  47. if (mCheckedId != -1) {
  48. mProtectFromCheckedChange = true;
  49. setCheckedStateForView(mCheckedId, true);
  50. mProtectFromCheckedChange = false;
  51. setCheckedId(mCheckedId);
  52. }
  53. }
  54. @Override
  55. public void addView(View child, int index, ViewGroup.LayoutParams params) {
  56. if (child instanceof RadioButton) {
  57. final RadioButton button = (RadioButton) child;
  58. radioButtons.add(button);
  59. if (button.isChecked()) {
  60. mProtectFromCheckedChange = true;
  61. if (mCheckedId != -1) {
  62. setCheckedStateForView(mCheckedId, false);
  63. }
  64. mProtectFromCheckedChange = false;
  65. setCheckedId(button.getId());
  66. }
  67. } else if (child instanceof ViewGroup) {// 如果是复合控件
  68. // 遍历复合控件
  69. ViewGroup vg = ((ViewGroup) child);
  70. setCheckedView(vg);
  71. }
  72. super.addView(child, index, params);
  73. }
  74. /** 查找复合控件并设置radiobutton */
  75. private void setCheckedView(ViewGroup vg) {
  76. int len = vg.getChildCount();
  77. for (int i = 0; i < len; i++) {
  78. if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
  79. final RadioButton button = (RadioButton) vg.getChildAt(i);
  80. // 添加到容器
  81. radioButtons.add(button);
  82. if (button.isChecked()) {
  83. mProtectFromCheckedChange = true;
  84. if (mCheckedId != -1) {
  85. setCheckedStateForView(mCheckedId, false);
  86. }
  87. mProtectFromCheckedChange = false;
  88. setCheckedId(button.getId());
  89. }
  90. } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
  91. ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
  92. setCheckedView(childVg);
  93. }
  94. }
  95. }
  96. /** 查找复合控件并设置id */
  97. private void setCheckedId(ViewGroup vg) {
  98. int len = vg.getChildCount();
  99. for (int i = 0; i < len; i++) {
  100. if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
  101. final RadioButton button = (RadioButton) vg.getChildAt(i);
  102. int id = button.getId();
  103. // generates an id if it's missing
  104. if (id == View.NO_ID) {
  105. id = button.hashCode();
  106. button.setId(id);
  107. }
  108. button.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  109. } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
  110. ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
  111. setCheckedId(childVg);
  112. }
  113. }
  114. }
  115. /** 查找radioButton控件 */
  116. public RadioButton findRadioButton(ViewGroup group) {
  117. RadioButton resBtn = null;
  118. int len = group.getChildCount();
  119. for (int i = 0; i < len; i++) {
  120. if (group.getChildAt(i) instanceof RadioButton) {
  121. resBtn = (RadioButton) group.getChildAt(i);
  122. } else if (group.getChildAt(i) instanceof ViewGroup) {
  123. resBtn = findRadioButton((ViewGroup) group.getChildAt(i));
  124. findRadioButton((ViewGroup) group.getChildAt(i));
  125. break;
  126. }
  127. }
  128. return resBtn;
  129. }
  130. /** 返回当前radiobutton控件的count */
  131. public int getRadioButtonCount() {
  132. return radioButtons.size();
  133. }
  134. /** 返回当前index的radio */
  135. public RadioButton getRadioButton(int index) {
  136. return radioButtons.get(index);
  137. }
  138. /**
  139. * <p>
  140. * Sets the selection to the radio button whose identifier is passed in
  141. * parameter. Using -1 as the selection identifier clears the selection;
  142. * such an operation is equivalent to invoking {@link #clearCheck()}.
  143. * </p>
  144. *
  145. * @param id
  146. * the unique id of the radio button to select in this group
  147. *
  148. * @see #getCheckedRadioButtonId()
  149. * @see #clearCheck()
  150. */
  151. public void check(int id) {
  152. // don't even bother
  153. if (id != -1 && (id == mCheckedId)) {
  154. return;
  155. }
  156. if (mCheckedId != -1) {
  157. setCheckedStateForView(mCheckedId, false);
  158. }
  159. if (id != -1) {
  160. setCheckedStateForView(id, true);
  161. }
  162. setCheckedId(id);
  163. }
  164. private void setCheckedId(int id) {
  165. mCheckedId = id;
  166. if (mOnCheckedChangeListener != null) {
  167. mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
  168. }
  169. }
  170. private void setCheckedStateForView(int viewId, boolean checked) {
  171. View checkedView = findViewById(viewId);
  172. if (checkedView != null && checkedView instanceof RadioButton) {
  173. ((RadioButton) checkedView).setChecked(checked);
  174. }
  175. }
  176. /**
  177. * <p>
  178. * Returns the identifier of the selected radio button in this group. Upon
  179. * empty selection, the returned value is -1.
  180. * </p>
  181. *
  182. * @return the unique id of the selected radio button in this group
  183. *
  184. * @see #check(int)
  185. * @see #clearCheck()
  186. */
  187. public int getCheckedRadioButtonId() {
  188. return mCheckedId;
  189. }
  190. /**
  191. * <p>
  192. * Clears the selection. When the selection is cleared, no radio button in
  193. * this group is selected and {@link #getCheckedRadioButtonId()} returns
  194. * null.
  195. * </p>
  196. *
  197. * @see #check(int)
  198. * @see #getCheckedRadioButtonId()
  199. */
  200. public void clearCheck() {
  201. check(-1);
  202. }
  203. /**
  204. * <p>
  205. * Register a callback to be invoked when the checked radio button changes
  206. * in this group.
  207. * </p>
  208. *
  209. * @param listener
  210. * the callback to call on checked state change
  211. */
  212. public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  213. mOnCheckedChangeListener = listener;
  214. }
  215. /**
  216. * {@inheritDoc}
  217. */
  218. @Override
  219. public LayoutParams generateLayoutParams(AttributeSet attrs) {
  220. return new MyRadioGroup.LayoutParams(getContext(), attrs);
  221. }
  222. /**
  223. * {@inheritDoc}
  224. */
  225. @Override
  226. protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
  227. return p instanceof MyRadioGroup.LayoutParams;
  228. }
  229. @Override
  230. protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
  231. return new LayoutParams(LayoutParams.WRAP_CONTENT,
  232. LayoutParams.WRAP_CONTENT);
  233. }
  234. /**
  235. * <p>
  236. * This set of layout parameters defaults the width and the height of the
  237. * children to {@link #WRAP_CONTENT} when they are not specified in the XML
  238. * file. Otherwise, this class ussed the value read from the XML file.
  239. * </p>
  240. *
  241. * <p>
  242. * See {@link android.R.styleable#LinearLayout_Layout LinearLayout
  243. * Attributes} for a list of all child view attributes that this class
  244. * supports.
  245. * </p>
  246. *
  247. */
  248. public static class LayoutParams extends LinearLayout.LayoutParams {
  249. /**
  250. * {@inheritDoc}
  251. */
  252. public LayoutParams(Context c, AttributeSet attrs) {
  253. super(c, attrs);
  254. }
  255. /**
  256. * {@inheritDoc}
  257. */
  258. public LayoutParams(int w, int h) {
  259. super(w, h);
  260. }
  261. /**
  262. * {@inheritDoc}
  263. */
  264. public LayoutParams(int w, int h, float initWeight) {
  265. super(w, h, initWeight);
  266. }
  267. /**
  268. * {@inheritDoc}
  269. */
  270. public LayoutParams(ViewGroup.LayoutParams p) {
  271. super(p);
  272. }
  273. /**
  274. * {@inheritDoc}
  275. */
  276. public LayoutParams(MarginLayoutParams source) {
  277. super(source);
  278. }
  279. /**
  280. * <p>
  281. * Fixes the child's width to
  282. * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the
  283. * child's height to
  284. * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not
  285. * specified in the XML file.
  286. * </p>
  287. *
  288. * @param a
  289. * the styled attributes set
  290. * @param widthAttr
  291. * the width attribute to fetch
  292. * @param heightAttr
  293. * the height attribute to fetch
  294. */
  295. @Override
  296. protected void setBaseAttributes(TypedArray a, int widthAttr,
  297. int heightAttr) {
  298. if (a.hasValue(widthAttr)) {
  299. width = a.getLayoutDimension(widthAttr, "layout_width");
  300. } else {
  301. width = WRAP_CONTENT;
  302. }
  303. if (a.hasValue(heightAttr)) {
  304. height = a.getLayoutDimension(heightAttr, "layout_height");
  305. } else {
  306. height = WRAP_CONTENT;
  307. }
  308. }
  309. }
  310. /**
  311. * <p>
  312. * Interface definition for a callback to be invoked when the checked radio
  313. * button changed in this group.
  314. * </p>
  315. */
  316. public interface OnCheckedChangeListener {
  317. /**
  318. * <p>
  319. * Called when the checked radio button has changed. When the selection
  320. * is cleared, checkedId is -1.
  321. * </p>
  322. *
  323. * @param group
  324. * the group in which the checked radio button has changed
  325. * @param checkedId
  326. * the unique identifier of the newly checked radio button
  327. */
  328. public void onCheckedChanged(MyRadioGroup group, int checkedId);
  329. }
  330. private class CheckedStateTracker implements
  331. CompoundButton.OnCheckedChangeListener {
  332. public void onCheckedChanged(CompoundButton buttonView,
  333. boolean isChecked) {
  334. // prevents from infinite recursion
  335. if (mProtectFromCheckedChange) {
  336. return;
  337. }
  338. mProtectFromCheckedChange = true;
  339. if (mCheckedId != -1) {
  340. setCheckedStateForView(mCheckedId, false);
  341. }
  342. mProtectFromCheckedChange = false;
  343. int id = buttonView.getId();
  344. setCheckedId(id);
  345. }
  346. }
  347. /**
  348. * <p>
  349. * A pass-through listener acts upon the events and dispatches them to
  350. * another listener. This allows the table layout to set its own internal
  351. * hierarchy change listener without preventing the user to setup his.
  352. * </p>
  353. */
  354. private class PassThroughHierarchyChangeListener implements
  355. ViewGroup.OnHierarchyChangeListener {
  356. private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
  357. public void onChildViewAdded(View parent, View child) {
  358. if (parent == MyRadioGroup.this && child instanceof RadioButton) {
  359. int id = child.getId();
  360. // generates an id if it's missing
  361. if (id == View.NO_ID) {
  362. id = child.hashCode();
  363. child.setId(id);
  364. }
  365. ((RadioButton) child)
  366. .setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  367. } else if (parent == MyRadioGroup.this
  368. && child instanceof ViewGroup) {// 如果是复合控件
  369. // 查找并设置id
  370. setCheckedId((ViewGroup) child);
  371. }
  372. if (mOnHierarchyChangeListener != null) {
  373. mOnHierarchyChangeListener.onChildViewAdded(parent, child);
  374. }
  375. }
  376. public void onChildViewRemoved(View parent, View child) {
  377. if (parent == MyRadioGroup.this && child instanceof RadioButton) {
  378. ((RadioButton) child).setOnCheckedChangeListener(null);
  379. } else if (parent == MyRadioGroup.this
  380. && child instanceof ViewGroup) {
  381. findRadioButton((ViewGroup) child).setOnCheckedChangeListener(
  382. null);
  383. }
  384. if (mOnHierarchyChangeListener != null) {
  385. mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
  386. }
  387. }
  388. }
  389. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/733706
推荐阅读
相关标签
  

闽ICP备14008679号