【UI篇1】自定义Preference

写在前面的话
学而不思则罔,思而不学则殆。最近在做设置页的UI调整,总会遇到需要自定义Preference的情况,现将自定义Preference的一些思考总结如下。既方面后续查阅,也与大家交流一下自己的看法,希望通过交流可以有更大的进步。

关于自定义Preference,一般会遇到两种场景:一种是可以复用原生Preference布局;另一种是layout完全无法复用,需要自己定义layout。下面分别针对这两种情况给出自己的总结。

1、可复用Preference布局

1.1 分析

针对可复用Preference布局的情况,首先我们看下preference/preference/res/layout/preference.xml 的布局如下所示,此处可以替换的布局id为widget_frame,Preference类提供了方法setWidgetLayoutResource(int widgetLayoutResid)来设置该布局。




    
        
    

    

        

        

    

    
    
    


这里我们用一个ButtonPreference来举例,用Button填充widget_frame来实现ButtonPreference,实现功能:设置ButtonPreference中Button上的Text,具体实现如下

  1. 定义要替换的Button布局文件 R.layout.preference_widget_button

  1. 定义buttonText属性

    

  1. 构造方法中通过setWidgetlayoutResource方法替换布局
  2. 继承Preference,实现onBindViewHolder方法,获取自定义布局中的控件
  3. 实现setButtonText方法和点击监听
class ButtonPreference : Preference {
    private var button: Button? = null
    private var buttonText: CharSequence? = ""
    private var buttonClickListener: OnButtonClickListener? = null

    interface OnButtonClickListener {
        fun onButtonClick(view: View?)
    }

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(
        context,
        attrs,
        R.attr.PreferenceStyle
    )

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(
        context,
        attrs,
        defStyleAttr,
        0
    )

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr) {
       //替换布局
        widgetLayoutResource = R.layout.preference_widget_button
        val a = context.obtainStyledAttributes(
            attrs,
            R.styleable.ButtonPreference,
            defStyleAttr,
            defStyleRes
        )
        //获取属性
        buttonText = a.getText(R.styleable.ButtonPreference_buttonText)
        a.recycle()
    }

    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)
        //初始化控件
        button = holder.findViewById(R.id.buttonWidget) as? Button
        button?.apply {
            setOnClickListener { v -> buttonClickListener?.onButtonClick(v) }
            if (!buttonText.isNullOrEmpty()) {
                text = buttonText
            }
        }
    }

    fun setButtonText(text: CharSequence?) {
        Log.d("setButtonText", "text=$text")
        if (!TextUtils.equals(text, buttonText)) {
            buttonText = text
            notifyChanged()
        }
    }

    fun setOnButtonClickListener(listener: OnButtonClickListener) {
        buttonClickListener = listener
    }
}

1.2 总结

看完上面的流程和代码,我们总结提炼一下实现过程五步法:

  1. 定义要替换的布局文件R.layout.preference_widget_button
  2. 定义所需的属性,如定义buttonText属性
  3. 构造方法中通过setWidgetlayoutResource方法替换布局
  4. 继承Preference,实现onBindViewHolder方法,获取自定义布局中的控件
  5. 实现setButtonText方法和点击监听

2、layout无法复用

2.2 分析

说完可以复用的场景,这里针对不能复用Preference布局的情况,这里我们以LoadingPreference举例进行说明,具体实现如下:

  1. 自定义layout为R.layout.loading_preference_layout:


    

    

    

    


  1. PreferenceScreen中定义LoadingPreference,声明属性android:layout为自定义的布局


  1. 继承Preference,实现onBindViewHolder方法,初始化需要用到的控件
  2. 设置属性
class LoadingPreference : Preference {
    private var loadingView: LoadingView? = null
    private var assignment: TextView? = null

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?)
            : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
            : this(context, attrs, defStyleAttr, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int)
            : super(context, attrs, defStyleAttr)

    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)
        loadingView = holder.findViewById(R.id.loadingView) as LoadingView
        assignment = holder.findViewById(R.id.assignment) as TextView
    }

    fun setLoadingViewVisibility(visibility: Int){
        loadingView?.visibility = visibility
    }

    fun updateFinish(){
        setAssignment("已更新")
    }
}

2.2 总结

看完上面的流程和代码,我们总结提炼一下实现过程四步法:

  1. 自定义layout
  2. xml中声明LoadingPreference属性android:layout为自定义的布局
  3. 继承Preference,实现onBindViewHolder方法,初始化需要用到的控件
  4. 设置属性,实现控件属性功能,需要更新执行notifyChaned方法

3. 扩展

3.1 扩展xml属性

以上面的ButtonPreference举例,上面定义buttonText属性后,在xml中即可以用到

属性名称 描述 类型 示例
buttonText 设置按钮的文字内容 string app:buttonText="上传"
jump_mark 资源设置 reference app:jump_mark="@drawable/next"

3.2 扩展api

以上面的ButtonPreference举例,上面定义的fun setButtonText(text: Charsequence?)即为扩展api

4. Preference的种类

切记:在造轮子之前,我们一定要了解是否有该轮子,切莫乱造轮子,复用一堆垃圾代码,下面列出目前api中有的Preference

种类
Preference
CheckBoxPreference
ListPreference
SeekBarPreference
DialogPreference
SwitchPreference
DropDownPreference
EditTextPreference
MultiSelectListPreference
TwoStatePreference

你可能感兴趣的:(【UI篇1】自定义Preference)