Kotlin实现RadioFlexboxGroup组件

来源:互联网 发布:软件著作权加急官费 编辑:程序博客网 时间:2024/06/15 12:56

如果使用过RadioGroup组件都是在,这个有很大的局限性,基本上设计师稿子一出来你就知道要自定义才可以实现的这些效果。
如果使用FlexBoxLayout就知道,这个东西还是挺好用了,如果还没使用过,不妨参考http://www.oschina.net/news/73442/google-flexbox-layout
这里我结合RadioGroup实现的单选效果加上FlexBoxLayout,简直太好用了,而且不需要写任何多余的代码,方便又好用,好了,废话不多说,先上效果图
这里写图片描述
当然我这里只列了基本效果,当然如果想要其他效果,可以充分发挥FlexBoxLayout布局优势
下边是布局文件xml:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.example.myproject.component.RadioFlexboxGroup        android:id="@+id/rg_1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:justifyContent="space_around"        app:flexWrap="wrap">        <RadioButton            android:id="@+id/radio_1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:checked="true"            android:text="不限" />        <RadioButton            android:id="@+id/radio_2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="精选项目" />        <RadioButton            android:id="@+id/radio_3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="转让项目" />    </com.example.myproject.component.RadioFlexboxGroup>    <com.example.myproject.component.RadioFlexboxGroup        android:id="@+id/rg_2"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginLeft="16dp"        android:layout_marginTop="16dp"        app:flexDirection="column"        app:justifyContent="space_around"        app:layout_constraintLeft_toRightOf="parent"        app:layout_constraintTop_toBottomOf="@+id/rg_1">        <RadioButton            android:id="@+id/radio_11"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textColor="@color/radio_text_coloraccent_select"            android:text="不限" />        <RadioButton            android:id="@+id/radio_12"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textColor="@color/radio_text_coloraccent_select"            android:text="精选项目" />        <RadioButton            android:id="@+id/radio_13"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textColor="@color/radio_text_coloraccent_select"            android:checked="true"            android:text="转让项目" />    </com.example.myproject.component.RadioFlexboxGroup>    <com.example.myproject.component.RadioFlexboxGroup        android:id="@+id/rg_3"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginLeft="32dp"        android:layout_marginRight="32dp"        android:layout_marginTop="16dp"        app:flexDirection="row_reverse"        app:justifyContent="space_between"        app:layout_constraintLeft_toRightOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toBottomOf="@+id/rg_2">        <RadioButton            android:id="@+id/radio_22"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/radio_select"            android:button="@null"            android:paddingBottom="4dp"            android:paddingLeft="16dp"            android:paddingRight="16dp"            android:paddingTop="4dp"            android:textColor="@color/radio_text_select"            android:text="\t\t不限\t\t" />        <RadioButton            android:id="@+id/radio_23"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/radio_select"            android:button="@null"            android:checked="true"            android:paddingBottom="4dp"            android:paddingLeft="16dp"            android:paddingRight="16dp"            android:paddingTop="4dp"            android:textColor="@color/radio_text_select"            android:text="精选项目" />        <RadioButton            android:id="@+id/radio_24"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/radio_select"            android:button="@null"            android:paddingBottom="4dp"            android:paddingLeft="16dp"            android:paddingRight="16dp"            android:paddingTop="4dp"            android:textColor="@color/radio_text_select"            android:text="转让项目" />    </com.example.myproject.component.RadioFlexboxGroup></android.support.constraint.ConstraintLayout>

Activity界面其实啥都没有:

class RadioFlexBoxGroupActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_radio_group)    }}

最后就是组件的代码:

class RadioFlexboxGroup : FlexboxLayout {    /**     *     * Returns the identifier of the selected radio button in this group.     * Upon empty selection, the returned value is -1.     * @return the unique id of the selected radio button in this group     * *     * @attr ref android.R.styleable#RadioGroup_checkedButton     * *     * @see .check     * @see .clearCheck     */    var mCheckedId = -1    var checkedRadioButtonId = -1        @IdRes        get() = mCheckedId        private set    // tracks children radio buttons checked state    private var mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null    // when true, mOnCheckedChangeListener discards events    private var mProtectFromCheckedChange = false    private var mOnCheckedChangeListener: OnCheckedChangeListener? = null    private var mPassThroughListener: PassThroughHierarchyChangeListener? = null    constructor(context: Context?) : super(context)    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)    init {        init()    }    private fun init() {        mChildOnCheckedChangeListener = CheckedStateTracker()        mPassThroughListener = PassThroughHierarchyChangeListener()        super.setOnHierarchyChangeListener(mPassThroughListener)    }    /**     * {@inheritDoc}     */    override fun setOnHierarchyChangeListener(listener: ViewGroup.OnHierarchyChangeListener?) {        // the user listener is delegated to our pass-through listener        mPassThroughListener?.mOnHierarchyChangeListener = listener    }    /**     * {@inheritDoc}     */    override fun onFinishInflate() {        super.onFinishInflate()        // checks the appropriate radio button as requested in the XML file        if (mCheckedId != -1) {            mProtectFromCheckedChange = true            setCheckedStateForView(mCheckedId, true)            mProtectFromCheckedChange = false            setCheckedId(mCheckedId)        }    }    override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {        if (child is RadioButton) {            val button = child            if (button.isChecked) {                mProtectFromCheckedChange = true                if (mCheckedId != -1) {                    setCheckedStateForView(mCheckedId, false)                }                mProtectFromCheckedChange = false                setCheckedId(button.id)            }        }        super.addView(child, index, params)    }    /**     *     * Sets the selection to the radio button whose identifier is passed in     * parameter. Using -1 as the selection identifier clears the selection;     * such an operation is equivalent to invoking [.clearCheck].     * @param id the unique id of the radio button to select in this group     * *     * @see .getMCheckedId     * @see .clearCheck     */    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) {        mCheckedId = id        if (mOnCheckedChangeListener != null) {            mOnCheckedChangeListener?.onCheckedChanged(this, mCheckedId)        }    }    private fun setCheckedStateForView(viewId: Int, checked: Boolean) {        val checkedView = findViewById<RadioButton>(viewId)        if (checkedView != null) {            checkedView.isChecked = checked        }    }    /**     *     * Clears the selection. When the selection is cleared, no radio button     * in this group is selected and [.getMCheckedId] returns     * null.     * @see .check     * @see .getMCheckedId     */    fun clearCheck() {        check(-1)    }    /**     *     * Register a callback to be invoked when the checked radio button     * changes in this group.     * @param listener the callback to call on checked state change     */    fun setOnCheckedChangeListener(listener: OnCheckedChangeListener) {        mOnCheckedChangeListener = listener    }    /**     * {@inheritDoc}     */    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {        return RadioFlexboxGroup.LayoutParams(context, attrs)    }    /**     * {@inheritDoc}     */    override fun checkLayoutParams(p: ViewGroup.LayoutParams?): Boolean {        return p is LayoutParams    }    override fun generateDefaultLayoutParams(): LayoutParams {        return LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)    }    override fun getAccessibilityClassName(): CharSequence {        return RadioFlexboxGroup::class.java.name    }    /**     *     * This set of layout parameters defaults the width and the height of     * the children to [.WRAP_CONTENT] when they are not specified in the     * XML file. Otherwise, this class ussed the value read from the XML file.     *     *     *     * See     * for a list of all child view attributes that this class supports.     */    class LayoutParams : FlexboxLayout.LayoutParams {        /**         * {@inheritDoc}         */        constructor(c: Context?, attrs: AttributeSet?) : super(c, attrs)        /**         * {@inheritDoc}         */        constructor(w: Int, h: Int) : super(w, h)        /**         * {@inheritDoc}         */        constructor(p: ViewGroup.LayoutParams?) : super(p)        /**         * {@inheritDoc}         */        constructor(source: ViewGroup.MarginLayoutParams?) : super(source)        /**         *         * Fixes the child's width to         * [ViewGroup.LayoutParams.WRAP_CONTENT] and the child's         * height to  [ViewGroup.LayoutParams.WRAP_CONTENT]         * when not specified in the XML file.         * @param a          the styled attributes set         * *         * @param widthAttr  the width attribute to fetch         * *         * @param heightAttr the height attribute to fetch         */        override fun setBaseAttributes(a: TypedArray,                                       widthAttr: Int, heightAttr: Int) {            if (a.hasValue(widthAttr)) {                width = a.getLayoutDimension(widthAttr, "layout_width")            } else {                width = ViewGroup.LayoutParams.WRAP_CONTENT            }            if (a.hasValue(heightAttr)) {                height = a.getLayoutDimension(heightAttr, "layout_height")            } else {                height = ViewGroup.LayoutParams.WRAP_CONTENT            }        }    }    /**     *     * Interface definition for a callback to be invoked when the checked     * radio button changed in this group.     */    interface OnCheckedChangeListener {        /**         *         * Called when the checked radio button has changed. When the         * selection is cleared, checkedId is -1.         * @param group     the group in which the checked radio button has changed         * *         * @param checkedId the unique identifier of the newly checked radio button         */        fun onCheckedChanged(group: RadioFlexboxGroup, @IdRes checkedId: Int)    }    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)        }    }    /**     *     * A pass-through listener acts upon the events and dispatches them     * to another listener. This allows the table layout to set its own internal     * hierarchy change listener without preventing the user to setup his.     */    private inner class PassThroughHierarchyChangeListener : ViewGroup.OnHierarchyChangeListener {        var mOnHierarchyChangeListener: ViewGroup.OnHierarchyChangeListener? = null        /**         * {@inheritDoc}         */        override fun onChildViewAdded(parent: View, child: View) {            if (parent === this@RadioFlexboxGroup && child is RadioButton) {                var id = child.getId()                // generates an id if it's missing                if (id == View.NO_ID) {                    id = generateViewId()                    child.setId(id)                }                child.setOnCheckedChangeListener(                        mChildOnCheckedChangeListener)            }            mOnHierarchyChangeListener?.onChildViewAdded(parent, child)        }        /**         * {@inheritDoc}         */        override fun onChildViewRemoved(parent: View, child: View) {            if (parent === this@RadioFlexboxGroup && child is RadioButton) {                child.setOnCheckedChangeListener(null)            }            mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)        }    }    companion object {        private val sNextGeneratedId = AtomicInteger(1)        fun generateViewId(): Int {            while (true) {                val result = sNextGeneratedId.get()                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.                var newValue = result + 1                if (newValue > 0x00FFFFFF) newValue = 1 // Roll over to 1, not 0.                if (sNextGeneratedId.compareAndSet(result, newValue)) {                    return result                }            }        }    }}

其实我这里是继承FlexBoxLayout实现的效果,所有拥有FLexBoxLayout的所有属性效果,整体还是很简单的
github地址:https://github.com/ttarfall/RadioFlexboxGroup

原创粉丝点击