当前位置:   article > 正文

android自定义RadioGroup实现继承多种布局_android radiogroup 布局

android radiogroup 布局

       android自带的RadioGroup是继承自LinearLayout,如果布局的时候不是直接在RadioGroup中radiobutton,即radiobutton的父布局不是RadioGroup,这时是查找不到radiobutton的。为了更方便布局,往往radioButton的外面还会包裹一层布局的。如果要实现这种效果,可以自定义一个RadioGroup,于是看了RadioGroup的源码,发现问题在于addView方法和自定义的PassThroughHierarchyChangeListener;

LinearLayout,ConstraintLayout等布局都是继承自viewGroup,要查询它们下面的RadioButton,则在addView方法中增加查询代码。

先拷贝源码,然后去掉RadioGroup(Context context, AttributeSet attrs) 中的方法,新增了一个方法,查找viewGroup控件中的radioButton。

  1. /** 查找radioButton控件
  2. 因为往往不是只有一个RadioButton,所以返回一个集合,并且当布局下没有RadioButton时后面也不会报错*/
  3. public List<RadioButton> findRadioButton(ViewGroup group) {
  4. List<RadioButton> resBtns=new ArrayList<>();
  5. int len = group.getChildCount();
  6. for (int i = 0; i < len; i++) {
  7. if (group.getChildAt(i) instanceof RadioButton) {
  8. resBtns.add((RadioButton) group.getChildAt(i));
  9. } else if (group.getChildAt(i) instanceof ViewGroup) {
  10. findRadioButton((ViewGroup) group.getChildAt(i));
  11. }
  12. }
  13. return resBtns;
  14. }

然后在addView方法中使用


  1. @Override
  2. public void addView(View child, int index, ViewGroup.LayoutParams params) {
  3. if (child instanceof RadioButton) {
  4. final RadioButton button = (RadioButton) child;
  5. if (button.isChecked()) {
  6. mProtectFromCheckedChange = true;
  7. if (mCheckedId != -1) {
  8. setCheckedStateForView(mCheckedId, false);
  9. }
  10. mProtectFromCheckedChange = false;
  11. setCheckedId(button.getId());
  12. }
  13. } else if (child instanceof ViewGroup) {//自己的,可查询viewGroup内部的radioButton
  14. final List<RadioButton> buttons = findRadioButton((ViewGroup) child);
  15. for(RadioButton button:buttons) {
  16. if (button.isChecked()) {
  17. mProtectFromCheckedChange = true;
  18. if (mCheckedId != -1) {
  19. setCheckedStateForView(mCheckedId, false);
  20. }
  21. mProtectFromCheckedChange = false;
  22. setCheckedId(button.getId());
  23. }
  24. }
  25. }
  26. super.addView(child, index, params);
  27. }

可以查询到radioButton了,还需要对其进行点击,移除等监听方法、


  1. private class PassThroughHierarchyChangeListener implements
  2. ViewGroup.OnHierarchyChangeListener {
  3. private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
  4. public void onChildViewAdded(View parent, View child) {
  5. if (parent == MyRadioGroup.this && child instanceof RadioButton) {
  6. int id = child.getId();
  7. // generates an id if it's missing
  8. if (id == View.NO_ID) {
  9. id = child.hashCode();
  10. child.setId(id);
  11. }
  12. ((RadioButton) child)
  13. .setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  14. } else if (parent == MyRadioGroup.this
  15. && child instanceof ViewGroup) {
  16. List<RadioButton> btns = findRadioButton((ViewGroup) child);
  17. for(RadioButton btn:btns) {
  18. int id = btn.getId();
  19. // generates an id if it's missing
  20. if (id == View.NO_ID) {
  21. id = btn.hashCode();
  22. btn.setId(id);
  23. }
  24. btn.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  25. }
  26. }
  27. if (mOnHierarchyChangeListener != null) {
  28. mOnHierarchyChangeListener.onChildViewAdded(parent, child);
  29. }
  30. }
  31. public void onChildViewRemoved(View parent, View child) {
  32. if (parent == MyRadioGroup.this && child instanceof RadioButton) {
  33. ((RadioButton) child).setOnCheckedChangeListener(null);
  34. } else if (parent == MyRadioGroup.this
  35. && child instanceof ViewGroup) {
  36. List<RadioButton> btns=findRadioButton((ViewGroup) child);
  37. for(RadioButton btn:btns){
  38. btn.setOnCheckedChangeListener(
  39. null);
  40. }
  41. }
  42. if (mOnHierarchyChangeListener != null) {
  43. mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
  44. }
  45. }
  46. }
其他的可以按照自己的意思来完成布局。这里我继承的是ConstraintLayout,也可以继承其他的布局,看需要。


下面是全部的代码

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



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

闽ICP备14008679号