请选择 进入手机版 | 继续访问电脑版

[Android] Android自定义RadioGroupX实现多行多列布局

  [复制链接]
查看241 | 回复53 | 2021-9-13 03:46:40 | 显示全部楼层 |阅读模式

前言

本日 在做新需求的时间 ,活动有多个范例 可以选择,UI给的计划 图为多行多列排版,且单项选择,细细想来,谷歌并没有为我们提供类似 的控件,初步假想 利用 RecyclerView实现多行多列布局,然后再用代码控制逻辑部分,总感觉不太稳妥,又想到让UI小姐姐重新计划 一番?感觉也不太稳妥,如许 UI小姐姐就会以为 我菜,为了不让別人以为 我菜,干脆自定义RadioGroupX实现多行多列布局。

思考

在工作中,面对 一个功能,起首 想到的是应该怎样实现完成它,然后再思量 毕竟 怎样实现才更优雅。正如前面提到,实现这种需求是可以用多种姿势完成,比如利用 RecyclerView,或者利用 ConstraintLayout装有多个TextView的布局,用代码控制选项逻辑,在思考 一番后,总感觉太生硬,不太优雅,代码量多大概 轻易 出bug。于是通过阅读谷歌为我们提供的RadioGroup源码得出一些灵感,阅读源码通常 能使本身 大彻大悟。比如在RadioGroup中为什么只支持单行多列或者多行单列布局,告急 缘故原由 是由于 RadioGroup extends LineLayout,以是 導致了很多范围 性。看到这里忽然 遐想 到GridView支持多行多列布局,于是乎,模拟 RadioGroup源码自定义一个容器继承GridView。

初识OnHierarchyChangeListener接口

OnHierarchyChangeListener接口位于ViewGroup java文件中,在一样平常 工作中,几乎不会用到,在developer官网文档中给出了如许 的表明 :

Android自定义RadioGroupX实现多行多列布局

工作中,我们对addView()和RemoveView()这两个方法肯定 不陌生 ,实在 我们在操作这两个方法的时间 就会触发OnHierarchyChangeListener接口中的java void onChildViewAdded(View parent, View child)java void onChildViewRemoved(View parent, View child);两个方法回调,源码中也给了具体 表明 。我们可以直接在源码中阅读解释 加以明确 。

参照RadioGroup源码定义内部类

PassThroughHierarchyChangeListener

  1. private inner class PassThroughHierarchyChangeListener :
  2. OnHierarchyChangeListener {
  3. private val mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
  4. @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  5. override fun onChildViewAdded(
  6. parent: View,
  7. child: View
  8. ) {
  9. if (parent == this@MultiLineRadioGroup && child is RadioButton) {
  10. var id = child.getId()
  11. // generates an id if it's missing
  12. if (id == View.NO_ID) {
  13. id = View.generateViewId()
  14. child.setId(id)
  15. }
  16. child.setOnCheckedChangeListener(
  17. mChildOnCheckedChangeListener
  18. )
  19. }
  20. mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
  21. }
  22. /**
  23. * {@inheritDoc}
  24. */
  25. override fun onChildViewRemoved(parent: View, child: View) {
  26. if (parent == this@MultiLineRadioGroup && child is RadioButton) {
  27. child.setOnCheckedChangeListener(null)
  28. }
  29. mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
  30. }
  31. }
复制代码

在上面重写kotlin onChildViewAdded( parent: View, child: View )kotlinonChildViewRemoved(parent: View, child: View)两个方法,我们偏重 关注onChildViewAdded方法,当我们在容器中添加子控件时,有多少个子孩子该方法就会触发多少次,我们在此动态设置子View的选中变乱 监听。

定义CheckedStateTracker实现

CompoundButton.OnCheckedChangeListener接口 

  1. private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
  2. override fun onCheckedChanged(
  3. buttonView: CompoundButton,
  4. isChecked: Boolean
  5. ) { // prevents from infinite recursion
  6. if (mProtectFromCheckedChange) {
  7. return
  8. }
  9. mProtectFromCheckedChange = true
  10. if (mCheckedId != -1) {
  11. setCheckedStateForView(mCheckedId, false)
  12. }
  13. mProtectFromCheckedChange = false
  14. val id = buttonView.id
  15. setCheckedId(id)
  16. }
  17. }
复制代码

在onCheckedChanged方法中处理子View也就是RadioButton的选中与取消变乱 ,通过以上两个步骤,基本完成了,View选中变乱 监听和变乱 处理逻辑

RadioGroupX完备 代码

  1. class RadioGroupX: GridLayout {
  2. private var mProtectFromCheckedChange = false
  3. var mCheckedId = -1
  4. private val mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener = CheckedStateTracker()
  5. private val mPassThroughListener: PassThroughHierarchyChangeListener = PassThroughHierarchyChangeListener()
  6. private var mOnCheckedChangeListener: OnCheckedChangeListener? = null
  7. constructor(context: Context?): this(context, null)
  8. constructor(context: Context?, attrs: AttributeSet?): this(context, attrs, 0)
  9. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
  10. init {
  11. super.setOnHierarchyChangeListener(mPassThroughListener)
  12. }
  13. override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
  14. if (child is RadioButton) {
  15. if (child.isChecked) {
  16. mProtectFromCheckedChange = true
  17. if (mCheckedId != -1) {
  18. setCheckedStateForView(mCheckedId, false)
  19. }
  20. mProtectFromCheckedChange = false
  21. setCheckedId(child.id)
  22. }
  23. }
  24. super.addView(child, index, params)
  25. }
  26. fun check(@IdRes id: Int) { // don't even bother
  27. if (id != -1 && id == mCheckedId) {
  28. return
  29. }
  30. if (mCheckedId != -1) {
  31. setCheckedStateForView(mCheckedId, false)
  32. }
  33. if (id != -1) {
  34. setCheckedStateForView(id, true)
  35. }
  36. setCheckedId(id)
  37. }
  38. private fun setCheckedId(@IdRes id: Int) {
  39. val changed = id != mCheckedId
  40. mCheckedId = id
  41. mOnCheckedChangeListener?.onCheckedChanged(this, mCheckedId)
  42. // if (changed) {
  43. // val afm: AutofillManager = mContext.getSystemService(
  44. // AutofillManager::class.java
  45. // )
  46. // afm?.notifyValueChanged(this)
  47. // }
  48. }
  49. private fun setCheckedStateForView(viewId: Int, checked: Boolean) {
  50. val checkedView = findViewById<View>(viewId)
  51. if (checkedView != null && checkedView is RadioButton) {
  52. checkedView.isChecked = checked
  53. }
  54. }
  55. private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
  56. override fun onCheckedChanged(
  57. buttonView: CompoundButton,
  58. isChecked: Boolean
  59. ) { // prevents from infinite recursion
  60. if (mProtectFromCheckedChange) {
  61. return
  62. }
  63. mProtectFromCheckedChange = true
  64. if (mCheckedId != -1) {
  65. setCheckedStateForView(mCheckedId, false)
  66. }
  67. mProtectFromCheckedChange = false
  68. val id = buttonView.id
  69. setCheckedId(id)
  70. }
  71. }
  72. fun setOnCheckedChangeListener(listener: OnCheckedChangeListener) {
  73. mOnCheckedChangeListener = listener
  74. }
  75. interface OnCheckedChangeListener {
  76. fun onCheckedChanged(group: RadioGroupX?, @IdRes checkedId: Int)
  77. }
  78. private inner class PassThroughHierarchyChangeListener :
  79. OnHierarchyChangeListener {
  80. private val mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
  81. @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  82. override fun onChildViewAdded(
  83. parent: View,
  84. child: View
  85. ) {
  86. if (parent == this@RadioGroupX && child is RadioButton) {
  87. var id = child.getId()
  88. // generates an id if it's missing
  89. if (id == View.NO_ID) {
  90. id = View.generateViewId()
  91. child.setId(id)
  92. }
  93. child.setOnCheckedChangeListener(
  94. mChildOnCheckedChangeListener
  95. )
  96. }
  97. mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
  98. }
  99. /**
  100. * {@inheritDoc}
  101. */
  102. override fun onChildViewRemoved(parent: View, child: View) {
  103. if (parent == this@RadioGroupX && child is RadioButton) {
  104. child.setOnCheckedChangeListener(null)
  105. }
  106. mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
  107. }
  108. }
  109. }
复制代码

xml中利用  

  1. <com.example.multilineradiogroupdemo.RadioGroupX
  2. android:layout_width="match_parent"
  3. android:columnCount="3"
  4. android:layout_height="wrap_content"
  5. app:layout_constraintTop_toBottomOf="@id/line">
  6. <RadioButton
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:text="数学" />
  10. <RadioButton
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. android:text="语文" />
  14. <RadioButton
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:text="地理" />
  18. <RadioButton
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:text="生物" />
  22. <RadioButton
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:text="计算机" />
  26. <RadioButton
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="化学" />
  30. </com.example.multilineradiogroupdemo.RadioGroupX>
复制代码

activity变乱 处理部分和利用 RadioGroup原理一样,照搬即可。

总结

通过上面短短几步,我们基本完成了需求中的排版题目 ,假如 不阅读鉴戒 源码中的思绪 ,我想我是很难写出来,至少不会在很短时间就完成需求计划 ,以是 工作我应该做到更多的阅读源码,相识 源码中的计划 思绪 和头脑 ,如许 本身 才能有所进步 。

以上就是本文的全部内容,渴望 对大家的学习有所帮助,也渴望 大家多多支持脚本之家。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 阿甘cx1982 | 2021-9-17 13:41:16 | 显示全部楼层
我就搞不明白了,看帖回帖能死人么,居然只有我这么认真的在回帖!
回复

使用道具 举报

avatar 聚雅阁砚堂 | 2021-9-27 03:45:15 | 显示全部楼层
今天是个特别的日子,值得纪念!
回复

使用道具 举报

avatar 简0 | 2021-9-28 02:01:24 | 显示全部楼层
admin楼主的帖子提神醒脑啊!
回复

使用道具 举报

avatar 忆神姆原们 | 2021-10-5 04:13:07 | 显示全部楼层
感觉不错!
回复

使用道具 举报

avatar 天使粉粉魏 | 2021-10-7 02:12:43 | 显示全部楼层
最近精神病院在打折,admin楼主去看看吧?
回复

使用道具 举报

avatar 天寿保健护理彰 | 2021-10-8 01:01:16 | 显示全部楼层
青春不在了,青春痘还在!
回复

使用道具 举报

avatar 幸福341 | 2021-10-8 01:01:19 | 显示全部楼层
好东西,学习学习!
回复

使用道具 举报

avatar 没有昵称513 | 2021-10-10 03:54:16 | 显示全部楼层
我默默的回帖,从不声张!
回复

使用道具 举报

avatar 胡仔1 | 2021-10-10 07:20:59 | 显示全部楼层
支持楼上的!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则