欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義RadioGroupX實(shí)現(xiàn)多行多列布局

 更新時(shí)間:2021年09月02日 13:15:41   作者:輝濤  
這篇文章主要為大家詳細(xì)介紹了Android自定義RadioGroupX實(shí)現(xiàn)多行多列布局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

今天在做新需求的時(shí)候,活動(dòng)有多個(gè)類型可以選擇,UI給的設(shè)計(jì)圖為多行多列排版,且單項(xiàng)選擇,細(xì)細(xì)想來,谷歌并沒有為我們提供類似的控件,初步設(shè)想使用RecyclerView實(shí)現(xiàn)多行多列布局,然后再用代碼控制邏輯部分,總感覺不太穩(wěn)妥,又想到讓UI小姐姐重新設(shè)計(jì)一番?感覺也不太穩(wěn)妥,這樣UI小姐姐就會(huì)認(rèn)為我菜,為了不讓別人覺得我菜,干脆自定義RadioGroupX實(shí)現(xiàn)多行多列布局。

思考

在工作中,面對(duì)一個(gè)功能,首先想到的是應(yīng)該怎樣實(shí)現(xiàn)完成它,然后再考慮究竟怎樣實(shí)現(xiàn)才更優(yōu)雅。正如前面提到,實(shí)現(xiàn)這種需求是可以用多種姿勢(shì)完成,比如使用RecyclerView,或者使用ConstraintLayout裝有多個(gè)TextView的布局,用代碼控制選項(xiàng)邏輯,在思考一番后,總感覺太生硬,不太優(yōu)雅,代碼量多也許容易出bug。于是通過閱讀谷歌為我們提供的RadioGroup源碼得出一些靈感,閱讀源碼往往能使自己大徹大悟。比如在RadioGroup中為什么只支持單行多列或者多行單列布局,主要原因是因?yàn)镽adioGroup extends LineLayout,所以導(dǎo)致了很多局限性。看到這里突然聯(lián)想到GridView支持多行多列布局,于是乎,模仿RadioGroup源碼自定義一個(gè)容器繼承GridView。

初識(shí)OnHierarchyChangeListener接口

OnHierarchyChangeListener接口位于ViewGroup java文件中,在日常工作中,幾乎不會(huì)用到,在developer官網(wǎng)文檔中給出了這樣的解釋:

工作中,我們對(duì)addView()和RemoveView()這兩個(gè)方法一定不陌生,其實(shí)我們?cè)诓僮鬟@兩個(gè)方法的時(shí)候就會(huì)觸發(fā)OnHierarchyChangeListener接口中的java void onChildViewAdded(View parent, View child)java void onChildViewRemoved(View parent, View child);兩個(gè)方法回調(diào),源碼中也給了詳細(xì)解釋。我們可以直接在源碼中閱讀注釋加以理解。

參照RadioGroup源碼定義內(nèi)部類

PassThroughHierarchyChangeListener

private inner class PassThroughHierarchyChangeListener :
        OnHierarchyChangeListener {
        private val mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
        @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        override fun onChildViewAdded(
            parent: View,
            child: View
        ) {
            if (parent == this@MultiLineRadioGroup && child is RadioButton) {
                var id = child.getId()
                // generates an id if it's missing
                if (id == View.NO_ID) {
                    id = View.generateViewId()
                    child.setId(id)
                }
                child.setOnCheckedChangeListener(
                    mChildOnCheckedChangeListener
                )
            }
            mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
        }

        /**
         * {@inheritDoc}
         */
        override fun onChildViewRemoved(parent: View, child: View) {
            if (parent == this@MultiLineRadioGroup && child is RadioButton) {
                child.setOnCheckedChangeListener(null)
            }
            mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
        }
    }

在上面重寫kotlin onChildViewAdded( parent: View, child: View )kotlinonChildViewRemoved(parent: View, child: View)兩個(gè)方法,我們著重關(guān)注onChildViewAdded方法,當(dāng)我們?cè)谌萜髦刑砑幼涌丶r(shí),有多少個(gè)子孩子該方法就會(huì)觸發(fā)多少次,我們?cè)诖藙?dòng)態(tài)設(shè)置子View的選中事件監(jiān)聽。

定義CheckedStateTracker實(shí)現(xiàn)

CompoundButton.OnCheckedChangeListener接口 

private inner  class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
        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 = buttonView.id
            setCheckedId(id)
        }
    }

在onCheckedChanged方法中處理子View也就是RadioButton的選中與取消事件,通過以上兩個(gè)步驟,基本完成了,View選中事件監(jiān)聽和事件處理邏輯

RadioGroupX完整代碼

class RadioGroupX: GridLayout {

    private var mProtectFromCheckedChange = false
    var mCheckedId = -1

    private val mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener = 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 {
        super.setOnHierarchyChangeListener(mPassThroughListener)
    }

    override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
        if (child is RadioButton) {
            if (child.isChecked) {
                mProtectFromCheckedChange = true
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false)
                }
                mProtectFromCheckedChange = false
                setCheckedId(child.id)
            }
        }
        super.addView(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 = mContext.getSystemService(
//                AutofillManager::class.java
//            )
//            afm?.notifyValueChanged(this)
//        }
    }

    private fun setCheckedStateForView(viewId: Int, checked: Boolean) {
        val checkedView = findViewById<View>(viewId)
        if (checkedView != null && checkedView is RadioButton) {
            checkedView.isChecked = checked
        }
    }

    private inner  class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
        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 = buttonView.id
            setCheckedId(id)
        }
    }

    fun setOnCheckedChangeListener(listener: OnCheckedChangeListener) {
        mOnCheckedChangeListener = listener
    }

    interface OnCheckedChangeListener {
        fun onCheckedChanged(group: RadioGroupX?, @IdRes checkedId: Int)
    }

    private inner class PassThroughHierarchyChangeListener :
        OnHierarchyChangeListener {
        private val mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
        @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        override fun onChildViewAdded(
            parent: View,
            child: View
        ) {
            if (parent == this@RadioGroupX && child is RadioButton) {
                var id = child.getId()
                // generates an id if it's missing
                if (id == View.NO_ID) {
                    id = View.generateViewId()
                    child.setId(id)
                }
                child.setOnCheckedChangeListener(
                    mChildOnCheckedChangeListener
                )
            }
            mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
        }

        /**
         * {@inheritDoc}
         */
        override fun onChildViewRemoved(parent: View, child: View) {
            if (parent == this@RadioGroupX && child is RadioButton) {
                child.setOnCheckedChangeListener(null)
            }
            mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
        }
    }

}

xml中使用 

<com.example.multilineradiogroupdemo.RadioGroupX
            android:layout_width="match_parent"
            android:columnCount="3"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/line">

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="數(shù)學(xué)" />

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="語(yǔ)文" />

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="地理" />

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="生物" />

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="計(jì)算機(jī)" />

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="化學(xué)" />


</com.example.multilineradiogroupdemo.RadioGroupX>

activity事件處理部分和使用RadioGroup原理一樣,照搬即可。

總結(jié)

通過上面短短幾步,我們基本完成了需求中的排版問題,如果不閱讀借鑒源碼中的思路,我想我是很難寫出來,至少不會(huì)在很短時(shí)間就完成需求設(shè)計(jì),所以工作我應(yīng)該做到更多的閱讀源碼,了解源碼中的設(shè)計(jì)思路和思想,這樣自己才能有所提高。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論