写在前面的话
学而不思则罔,思而不学则殆。最近在做设置页的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,具体实现如下
- 定义要替换的Button布局文件 R.layout.preference_widget_button
- 定义buttonText属性
- 构造方法中通过setWidgetlayoutResource方法替换布局
- 继承Preference,实现onBindViewHolder方法,获取自定义布局中的控件
- 实现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 总结
看完上面的流程和代码,我们总结提炼一下实现过程五步法:
- 定义要替换的布局文件R.layout.preference_widget_button
- 定义所需的属性,如定义buttonText属性
- 构造方法中通过setWidgetlayoutResource方法替换布局
- 继承Preference,实现onBindViewHolder方法,获取自定义布局中的控件
- 实现setButtonText方法和点击监听
2、layout无法复用
2.2 分析
说完可以复用的场景,这里针对不能复用Preference布局的情况,这里我们以LoadingPreference举例进行说明,具体实现如下:
- 自定义layout为R.layout.loading_preference_layout:
- PreferenceScreen中定义LoadingPreference,声明属性android:layout为自定义的布局
- 继承Preference,实现onBindViewHolder方法,初始化需要用到的控件
- 设置属性
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 总结
看完上面的流程和代码,我们总结提炼一下实现过程四步法:
- 自定义layout
- xml中声明LoadingPreference属性android:layout为自定义的布局
- 继承Preference,实现onBindViewHolder方法,初始化需要用到的控件
- 设置属性,实现控件属性功能,需要更新执行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 |