
### 官方基本用法:https://developer.android.google.cn/guide/topics/ui/settings
效果演示:
Preference需要配合PreferenceFragmentCompat使用。
所以和平时在Activity中使用Fragment没有区别,只是布局文件不是从layout文件夹去渲染而是从xml文件夹。
列表的展示是用了RecyclerView实现的,Apdater是PreferenceGroupAdpater,VH是PreferenceViewHolder
Preference要关心的地方,基本布局文件是什么?,扩展布局文件是什么,怎么设置点击事件的呢?,怎么监听值的变化的呢?,我该如何自定义呢?等…
准备动手!准备动手!先来看一下Preference默认的布局:
使用AS定位到文件:R.layout.preference
先从UI动手:由于是RecyclerView实现的,还是很清晰的,先进入PreferenceGroupAdapter
先看数据源、三大重写大方法onCreateViewHolder()、onBindViewHolder()、getItemCount()
public class PreferenceGroupAdapter extends RecyclerView.Adapterimplements Preference.OnPreferenceChangeInternalListener, PreferenceGroup.PreferencePositionCallback { // ... // 数据集 private List mPreferences; // 可见的Preference private List mVisiblePreferences; @Override public int getItemCount() { return mVisiblePreferences.size(); } // onCreateViewHolder() @Override @NonNull public PreferenceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 先获取Preference的资源描述类,这个类内部存储了一个Preference的Class名,基本布局资源id,扩展布 // 局资源id final PreferenceResourceDescriptor descriptor = mPreferenceResourceDescriptors.get( viewType); final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); // 设置被选中时的水波纹 TypedArray a = parent.getContext().obtainStyledAttributes(null, R.styleable.BackgroundStyle); Drawable background = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground); if (background == null) { background = AppCompatResources.getDrawable(parent.getContext(), android.R.drawable.list_selector_background); } a.recycle(); // 构造VH,看,这里inflate的是decriptor.mLoutRestId,上面说了,这个值保存的是Prefercen的及颁布布局,也就是对应着xml中的layout属性 final View view = inflater.inflate(descriptor.mLayoutResId, parent, false); if (view.getBackground() == null) { ViewCompat.setBackground(view, background); } // 这里判断如果设置了widgetLayout属性,这里会同时渲染 final ViewGroup widgetframe = view.findViewById(android.R.id.widget_frame); if (widgetframe != null) { if (descriptor.mWidgetLayoutResId != 0) { inflater.inflate(descriptor.mWidgetLayoutResId, widgetframe); } else { widgetframe.setVisibility(View.GONE); } } return new PreferenceViewHolder(view); } // 绑定数据-----------------> Preference.onBindViewHolder() @Override public void onBindViewHolder(@NonNull PreferenceViewHolder holder, int position) { final Preference preference = getItem(position); preference.onBindViewHolder(holder); } // ... }
走到这里,我们就明白了为什么自定义Preference需要重写的方法为什么是onBindViewHolder()了,在适配器的onBindViewHHolder()中是直接委托给了Preference实现。
接着走,去Preference#onBindViewHolder()瞅一瞅:
Preference#onBindViewHolder(): 翻译的好啊,好地方!
public void onBindViewHolder(PreferenceViewHolder holder) {
View itemView = holder.itemView;
Integer summaryTextColor = null;
// 设置点击事件,划重点 mClickListener
itemView.setOnClickListener(mClickListener);
itemView.setId(mViewId);
// 配置 概述
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
if (summaryView != null) {
final CharSequence summary = getSummary();
if (!TextUtils.isEmpty(summary)) {
summaryView.setText(summary);
summaryView.setVisibility(View.VISIBLE);
summaryTextColor = summaryView.getCurrentTextColor();
} else {
summaryView.setVisibility(View.GONE);
}
}
// 配置 标题
final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
if (titleView != null) {
final CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
titleView.setText(title);
titleView.setVisibility(View.VISIBLE);
if (mHasSingleLineTitleAttr) {
titleView.setSingleLine(mSingleLineTitle);
}
// If this Preference is not selectable, but still enabled, we should set the
// title text colour to the same colour used for the summary text
if (!isSelectable() && isEnabled() && summaryTextColor != null) {
titleView.setTextColor(summaryTextColor);
}
} else {
titleView.setVisibility(View.GONE);
}
}
// 配置 小图标
final ImageView imageView = (ImageView) holder.findViewById(android.R.id.icon);
if (imageView != null) {
if (mIconResId != 0 || mIcon != null) {
if (mIcon == null) {
mIcon = AppCompatResources.getDrawable(mContext, mIconResId);
}
if (mIcon != null) {
imageView.setImageDrawable(mIcon);
}
}
if (mIcon != null) {
imageView.setVisibility(View.VISIBLE);
} else {
imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
}
}
View imageframe = holder.findViewById(R.id.icon_frame);
if (imageframe == null) {
imageframe = holder.findViewById(AndroidResources.ANDROID_R_ICON_frame);
}
if (imageframe != null) {
if (mIcon != null) {
imageframe.setVisibility(View.VISIBLE);
} else {
imageframe.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
}
}
if (mShouldDisableView) {
setEnabledStateOnViews(itemView, isEnabled());
} else {
setEnabledStateOnViews(itemView, true);
}
final boolean selectable = isSelectable();
itemView.setFocusable(selectable);
itemView.setClickable(selectable);
// 设置上下分割线
holder.setDividerAllowedAbove(mAllowDividerAbove);
holder.setDividerAllowedBelow(mAllowDividerBelow);
final boolean copyingEnabled = isCopyingEnabled();
if (copyingEnabled && monCopyListener == null) {
monCopyListener = new OnPreferenceCopyListener(this);
}
itemView.setOnCreateContextMenuListener(copyingEnabled ? monCopyListener : null);
itemView.setLongClickable(copyingEnabled);
// Remove touch ripple if the view isn't selectable
if (copyingEnabled && !selectable) {
ViewCompat.setBackground(itemView, null);
}
}
OKK,我们知道了点击事件是直接绑定在itemView上的,我们来看看这个mClickListener:
private final View.onClickListener mClickListener = new View.onClickListener() {
@Override
public void onClick(View v) {
// 处理点击事件,执行performClick()方法
performClick(v);
}
};
继续走------------> Preference#performClick(View)
@RestrictTo(LIBRARY_GROUP_PREFIX)
protected void performClick(View view) {
// 继续分发套娃
performClick();
}
好家伙,经典套娃,这次一定是具体实现 -----> Preference#performClick()
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void performClick() {
// 不可用或者不可点击的话直接return
if (!isEnabled() || !isSelectable()) {
return;
}
// 这个onClick()是一个空方法,可以在Preference设置了点击事件之前可以处理一些自己的逻辑
onClick();
// 朴实无华的点击分发,如果给Preference设置了点击事件(Preference$setOnPreferenceClickListerner)则调用接口方法处理自己的逻辑
if (monClickListener != null && mOnClickListener.onPreferenceClick(this)) {
return;
}
// 假如没有设置点击事件的话,这个点击事件没有完全消费掉(就是onPreferenceClick(this)为false)
// 这里会执行另一个接口方法 PreferenceManager.OnPreferenceTreeClickListener
// 这个方法的实现在 PreferenceFragmentCompat中,也就是我们可以通过这个方法得到当前点击的是哪一个
// Preference
PreferenceManager preferenceManager = getPreferenceManager();
if (preferenceManager != null) {
PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
.getOnPreferenceTreeClickListener();
if (listener != null && listener.onPreferenceTreeClick(this)) {
return;
}
}
// 这里就是上面的点击事件,全局点击事件都没有完全消费的话,这里就是直接执行 节点
// 就是启动一个 Activity
if (mIntent != null) {
Context context = getContext();
context.start到此Activity(mIntent);
}
}
到此,onBindView()就搞定了,UI也就在Fragment中显示出来了
怎么自定义Preference嘞?通过上面的分析啊,我们知道俩个属性分别在哪里被用到了,layout、widgetLyaout,那么我们自定义布局的时候如果还是采用原生那个排列的话,就是上面那个效果的话,就是直接把默认布局抄一遍,然后就可以改样式了,如果是完全非主流的样式就是自己搞个布局然后赋值给layout或者在代码里赋值给layoutResource布局搞好之后,就得需要对布局中的控件做一些配置,这个时候就得用到Preference#onBindViewHolder()这个方法了,用法和ReyclerView.Adapter.onBindViewHolder()一样同时,Preference也封装了notifyDataSetChanged()方法:Preference#notifyChanged()
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)