2023年6月27日发(作者:)
Android⾃定义RadioGroupX实现多⾏多列布局前⾔今天在做新需求的时候,活动有多个类型可以选择,UI给的设计图为多⾏多列排版,且单项选择,细细想来,⾕歌并没有为我们提供类似的控件,初步设想使⽤RecyclerView实现多⾏多列布局,然后再⽤代码控制逻辑部分,总感觉不太稳妥,⼜想到让UI⼩姐姐重新设计⼀番?感觉也不太稳妥,这样UI⼩姐姐就会认为我菜,为了不让別⼈觉得我菜,⼲脆⾃定义RadioGroupX实现多⾏多列布局。思考在⼯作中,⾯对⼀个功能,⾸先想到的是应该怎样实现完成它,然后再考虑究竟怎样实现才更优雅。正如前⾯提到,实现这种需求是可以⽤多种姿势完成,⽐如使⽤RecyclerView,或者使⽤ConstraintLayout装有多个TextView的布局,⽤代码控制选项逻辑,在思考⼀番后,总感觉太⽣硬,不太优雅,代码量多也许容易出bug。于是通过阅读⾕歌为我们提供的RadioGroup源码得出⼀些灵感,阅读源码往往能使⾃⼰⼤彻⼤悟。⽐如在RadioGroup中为什么只⽀持单⾏多列或者多⾏单列布局,主要原因是因为RadioGroup extends LineLayout,所以導致了很多局限性。看到这⾥突然联想到GridView⽀持多⾏多列布局,于是乎,模仿RadioGroup源码⾃定义⼀个容器继承GridView。初识OnHierarchyChangeListener接⼝OnHierarchyChangeListener接⼝位于ViewGroup java⽂件中,在⽇常⼯作中,⼏乎不会⽤到,在developer官⽹⽂档中给出了这样的解释:⼯作中,我们对addView()和RemoveView()这两个⽅法⼀定不陌⽣,其实我们在操作这两个⽅法的时候就会触发OnHierarchyChangeListener接⼝中的java void onChildViewAdded(View parent, View child)和java voidonChildViewRemoved(View parent, View child);两个⽅法回调,源码中也给了详细解释。我们可以直接在源码中阅读注释加以理解。参照RadioGroup源码定义内部类PassThroughHierarchyChangeListenerprivate inner class PassThroughHierarchyChangeListener : OnHierarchyChangeListener { private val mOnHierarchyChangeListener: OnHierarchyChangeListener? = null @RequiresApi(N__BEAN_MR1) override fun onChildViewAdded( parent: View, child: View ) { if (parent == this@MultiLineRadioGroup && child is RadioButton) { var id = () // generates an id if it's missing if (id == _ID) { id = teViewId() (id) } heckedChangeListener( mChildOnCheckedChangeListener ) } mOnHierarchyChangeListener?.onChildViewAdded(parent, child) } /** * {@inheritDoc} */ override fun onChildViewRemoved(parent: View, child: View) { if (parent == this@MultiLineRadioGroup && child is RadioButton) { heckedChangeListener(null) } mOnHierarchyChangeListener?.onChildViewRemoved(parent, child) } }在上⾯重写kotlin onChildViewAdded( parent: View, child: View )和kotlinonChildViewRemoved(parent: View, child: View)两个⽅法,我们着重关注onChildViewAdded⽅法,当我们在容器中添加⼦控件时,有多少个⼦孩⼦该⽅法就会触发多少次,我们在此动态设置⼦View的选中事件监听。定义CheckedStateTracker实现kedChangeListener接⼝
private inner class CheckedStateTracker : kedChangeListener { override fun onCheckedChanged( buttonView: CompoundButton, isChecked: Boolean ) { // prevents from infinite recursion if (mProtectFromCheckedChange) { return } mProtectFromCheckedChange = true if (mCheckedId != -1) { setCheckedStateForView(mCheckedId, false) } mProtectFromCheckedChange = false val id = setCheckedId(id) } }在onCheckedChanged⽅法中处理⼦View也就是RadioButton的选中与取消事件,通过以上两个步骤,基本完成了,View选中事件监听和事件处理逻辑RadioGroupX完整代码class RadioGroupX: GridLayout { private var mProtectFromCheckedChange = false var mCheckedId = -1 private val mChildOnCheckedChangeListener: kedChangeListener = CheckedStateTracker() private val mPassThroughListener: PassThroughHierarchyChangeListener = PassThroughHierarchyChangeListener() private var mOnCheckedChangeListener: OnCheckedChangeListener? = null constructor(context: Context?): this(context, null) constructor(context: Context?, attrs: AttributeSet?): this(context, attrs, 0) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) init { ierarchyChangeListener(mPassThroughListener) } override fun addView(child: View?, index: Int, params: Params?) { if (child is RadioButton) { if (ked) { mProtectFromCheckedChange = true if (mCheckedId != -1) { setCheckedStateForView(mCheckedId, false) } mProtectFromCheckedChange = false setCheckedId() } } w(child, index, params) } fun check(@IdRes id: Int) { // don't even bother if (id != -1 && id == mCheckedId) { return } if (mCheckedId != -1) { setCheckedStateForView(mCheckedId, false) } if (id != -1) { setCheckedStateForView(id, true) } setCheckedId(id) } private fun setCheckedId(@IdRes id: Int) { val changed = id != mCheckedId mCheckedId = id mOnCheckedChangeListener?.onCheckedChanged(this, mCheckedId)// if (changed) {// val afm: AutofillManager = temService(// AutofillManager::// )// afm?.notifyValueChanged(this)// } } private fun setCheckedStateForView(viewId: Int, checked: Boolean) { val checkedView = findViewById
发布者:admin,转转请注明出处:http://www.yc00.com/web/1687840847a49896.html
评论列表(0条)