上一篇文章的DEMO中我们只是使用PreferenceActivity#addPreferencesFromResource(),系统就将设置页面展示出来了,至始至终我们没有跟View直接接触。父类到底帮我们做了什么,如果我们要自己控制View的实例可以吗?其大致的原理是什么呢。

我们将从源码入手,逐步探究背后的原理。以下代码版本为Android OREO。

先看下PreferenceActivity#addPreferencesFromResource()的实现逻辑。

PreferenceActivity#addPreferencesFromResource()

public abstract class PreferenceActivity extends ListActivity…{…public void addPreferencesFromResource(int preferencesResId) {requirePreferenceManager();setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,getPreferenceScreen()));}private void requirePreferenceManager() {if (mPreferenceManager == null) {if (mAdapter == null) {throw new RuntimeException("This should be called after super.onCreate.");}throw new RuntimeException("Modern two-pane PreferenceActivity requires use of a PreferenceFragment");}}…
}

在执行setPreferenceScreen()前先调用requirePreferenceManager()确保mPreferenceManager和mAdapter不为空。否则将抛出异常。

我们在使用的时候并没有发生上述异常,那么这个mPreferenceManager和和mAdapter是在哪创建的?又是干什么用的呢?

public abstract class PreferenceActivity extends ListActivity…{private PreferenceManager mPreferenceManager;★…@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);…if (mHeaders.size() > 0) {setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,mPreferenceHeaderRemoveEmptyIcon)); if (!mSinglePane) {getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);★②}}…if (mHeaders.size() == 0 && initialFragment == null) {…
setContentView(com.android.internal.R.layout.preference_list_content_single);mListFooter = (FrameLayout) findViewById(com.android.internal.R.id.list_footer);mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE); ★①mPreferenceManager.setOnPreferenceTreeClickListener(this);mHeadersContainer = null;}…}
}

我们发现mPreferenceManager是定义在PreferenceActivity的全局变量并在★①的onCreate()里进行初始化。
PreferenceManager在源码中的如下备注表示该类用于从activities或者xml中创建Preference组件的帮助类。

// PreferenceManager.java
/**
* Used to help create {@link Preference} hierarchies
* from activities or XML.
* <p>
* In most cases, clients should use
* {@link PreferenceActivity#addPreferencesFromIntent} or
* {@link PreferenceActivity#addPreferencesFromResource(int)}.
*
* @see PreferenceActivity
*/

而mAdapter则是定义在父类ListActivity中的ListAdapter实例,是在★②onCreate()中通过调用setListAdapter()进行初始化的。

public class ListActivity extends Activity {/*** This field should be made private, so it is hidden from the SDK.* {@hide}*/protected ListAdapter mAdapter;…
}

mPreferenceManager和mAdapter实例是后续操作所依赖的处理,所以在执行关键处理前需要确保其不为空。

两个实例都是在onCreate()里进行初始化的,也启发我们必须在父类的onCreate()执行后再调用addPreferencesFromResource()处理。

接着看后续处理。

调用setPreferenceScreen()前先调用PreferenceManager#inflateFromResource()初始化PreferenceScreen实例。
※getPreferenceScreen()实际从PerferenceManager中取得当前页面的PreferenceScreen实例,此时尚且为null。

PreferenceManager#inflateFromResource()

public class PreferenceManager {…public PreferenceScreen inflateFromResource(Context context, @XmlRes int resId,PreferenceScreen rootPreferences) {// Block commitssetNoCommit(true);final PreferenceInflater inflater = new PreferenceInflater(context, this);rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences, true);rootPreferences.onAttachedToHierarchy(this);// Unblock commitssetNoCommit(false);return rootPreferences;}…
}

通过创建Preference组件填充类PreferenceInflater的实例去解析指定的Preference布局。实际调用的是父类GenericInflater的相关逻辑。

abstract class GenericInflater<T, P extends GenericInflater.Parent> {public T inflate(XmlPullParser parser, P root, boolean attachToRoot) {synchronized (mConstructorArgs) {final AttributeSet attrs = Xml.asAttributeSet(parser);mConstructorArgs[0] = mContext;T result = (T) root;try {// Look for the root node.int type;while ((type = parser.next()) != parser.START_TAG&& type != parser.END_DOCUMENT) {;}if (type != parser.START_TAG) {throw new InflateException(parser.getPositionDescription()+ ": No start tag found!");}…// Temp is the root that was found in the xmlT xmlRoot = createItemFromTag(parser, parser.getName(),attrs);result = (T) onMergeRoots(root, attachToRoot, (P) xmlRoot);…// Inflate all children under temprInflate(parser, result, attrs);…}…return result;}}private void rInflate(XmlPullParser parser, T parent, final AttributeSet attrs)throws XmlPullParserException, IOException {final int depth = parser.getDepth();int type;while (((type = parser.next()) != parser.END_TAG || parser.getDepth() > depth) && type != parser.END_DOCUMENT) {if (type != parser.START_TAG) {continue;}if (onCreateCustomFromTag(parser, parent, attrs)) {continue;}if (DEBUG) {System.out.println("Now inflating tag: " + parser.getName());}String name = parser.getName();T item = createItemFromTag(parser, name, attrs);if (DEBUG) {System.out.println("Creating params from parent: " + parent);}((P) parent).addItemFromInflater(item);if (DEBUG) {System.out.println("-----> start inflating children");}rInflate(parser, item, attrs);if (DEBUG) {System.out.println("-----> done inflating children");}}}…
}

inflate()获取XML的根节点即PreferenceScreen,获取标签名称调用createItemFromTag()和createItem()创建对应的PreferenceScreen实例。

接着调用rInflate(),递归遍历xml节点并逐个调用createItemFromTag()和createItem()创建对应的子节点实例。

然后通过调用PerferenceGroup#addItemFromInflater()将子节点追加到所属的PerferenceGroup(PreferenceCategory或PreferenceScreen)中。

PerferenceGroup#addPreference()

public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {…public void addItemFromInflater(Preference preference) {addPreference(preference);}public boolean addPreference(Preference preference) {//每个PreferenceGroup对象都内持存放Preference对象的ArrayList。if (mPreferenceList.contains(preference)) { // 检查是否已经存在// Existsreturn true; // Group内已经包含了该Preference对象的话将驳回}// 判断该Preference是否已经指定了order数值if (preference.getOrder() == Preference.DEFAULT_ORDER) {// 判断Group是否被更改了mOrderingAsAdded的标志。// 默认为true,表示按照Preference的添加顺序进行排序// 可以通过orderingFromXml标签指定或setOrderingAsAdded()修改if (mOrderingAsAdded) {// Preference没指定order,Group也是默认的order计数的场合// 给改Preference手动设置计算后的order值(从0开始累加)preference.setOrder(mCurrentPreferenceOrder++);}// 待嵌套的Preference也是Group的话,将当前的顺序flag覆盖过去if (preference instanceof PreferenceGroup) {((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);}}// 告知Preference上级Preference即将变化,默认返回true// 意图是给予Preference拦截这种变化,表示自己不想被添加到Groupif (!onPrepareAddPreference(preference)) {return false;}synchronized(this) {// Preference列表中二分法查找待追加Preference// 过程中将调用到Preference#compareTo()的逻辑去和已有的Prefrence进行比较// 此刻列表中肯定不存在目标Preference,binarySearch将返回待插入index + 1的负值int insertionIndex = Collections.binarySearch(mPreferenceList, preference);if (insertionIndex < 0) {// 取得待插入位置insertionIndex = insertionIndex * -1 - 1;}// 将目标Preference插入目标位置// 为何不直接add而是先查找再添加至执行位置?// 答:Preference指定了order的场合,该数值不一定比最后一个元素的order值(从0开始)大,有可能介于list中间某处,所以最好用二分法查找找到合适的位置再插入mPreferenceList.add(insertionIndex, preference);}// 告知Preference对象已经被添加到Preference组件中。// ① 共享PreferenceManager实例给Preference:调用Preference#getPreferenceManager() 获取Group自己被追加到上级后持有的PreferenceManager实例传递给Preference// ② 将该Preference在PreferenceManager中的ID值保存// ③ 初始化Preference的默认值:调用dispatchSetInitialValue()->onSetInitialValue()将defaultValue标签或者setDefaultValue()配置的初始值反映preference.onAttachedToHierarchy(getPreferenceManager());// 初始化Preference持有的表示parent的Group变量preference.assignParent(this);// 判断此刻Group是否已经添加到Activity上去了// true则调用Preference的同名函数// 初次添加的场合,默认false;当addPrefereneFromResource已经调用完毕后手动调用PreferenceGroup#addPreference()时该flag即为true。if (mAttachedToActivity) {preference.onAttachedToActivity();}// 调用Preference#notifyHierarchyChanged()以回调层级变化的接口。notifyHierarchyChanged();return true; // 至此返回true表示添加成功}…protected boolean onPrepareAddPreference(Preference preference) {preference.onParentChanged(this, shouldDisableDependents());return true;}
}

至此,已经成功地解析了Preference的xml文件,将对应的Preference组件全部实例化并按照嵌套关系相互进行了绑定最后得到一个根组件PreferenceScreen的实例。

在返回PreferenceScreen的实例前调用Preferences.onAttachedToHierarchy()将PreferenceManager的实例及作为Preference在Manager中的ID反映PreferenceScreen中。

接下来继续剖析PreferenceActivity#setPreferenceScreen()的逻辑。

PreferenceActivity#setPreferenceScreen()

public abstract class PreferenceActivity extends ListActivity…{public void setPreferenceScreen(PreferenceScreen preferenceScreen) {// 再次确保mPreferenceManager和mAdapter实例不为空requirePreferenceManager();// 将得到的PreferenceScreen实例更新到PreferenceManager中。// 已有PreferenceScreen实例且两者相同的话将返回false。if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {// 得到的PreferenceScreen实例不为空且不同于已有实例的场合// 向PreferenceActivity内置主线程Handler发送MSG_BIND_PREFERENCES消息,接着调用bindPreferences()postBindPreferences();CharSequence title = getPreferenceScreen().getTitle();// Set the title of the activityif (title != null) {setTitle(title);}}}…private void bindPreferences() {// 从持有的PreferenceManager实例中取得持有的PreferenceScreen实例final PreferenceScreen preferenceScreen = getPreferenceScreen();if (preferenceScreen != null) {// 先调用父类ListActivity#getListView()取得ListView实例。// 再调用PreferenceScreen#bind()将自己和ListView绑定。preferenceScreen.bind(getListView());if (mSavedInstanceState != null) {super.onRestoreInstanceState(mSavedInstanceState);mSavedInstanceState = null;}}}
}

我们简单提及下ListView的实例是怎么获取到的。

ListActivity#getListView()

public class ListActivity extends Activity {
…public ListView getListView() {ensureList(); // 确保ListView实例已经初始化并返回ListView实例return mList;}private void ensureList() {// 如果已经初始化则什么也不做,实际上PreferenceActivity#onCreate()已经加载过了ListView的布局。if (mList != null) {return;}// 否则加载默认的包含ListView控件的布局。
setContentView(com.android.internal.R.layout.list_content_simple);}
}public abstract class PreferenceActivity extends ListActivity…{…@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Theming for the PreferenceActivity layout and for the Preference Header(s) layoutTypedArray sa = obtainStyledAttributes(null,com.android.internal.R.styleable.PreferenceActivity,com.android.internal.R.attr.preferenceActivityStyle,0);// 获取到style下PreferenceActivity_layout指定的布局文件id,默认将返回指定的默认布局即FW里preference_list_content.xml。final int layoutResId = sa.getResourceId(com.android.internal.R.styleable.PreferenceActivity_layout,com.android.internal.R.layout.preference_list_content); mPreferenceHeaderItemResId = sa.getResourceId(com.android.internal.R.styleable.PreferenceActivity_headerLayout,com.android.internal.R.layout.preference_header_item);mPreferenceHeaderRemoveEmptyIcon = sa.getBoolean(com.android.internal.R.styleable.PreferenceActivity_headerRemoveIconIfEmpty,false);sa.recycle();setContentView(layoutResId);★…}…
}

★可以看出PreferenceActivity在onCreate()里给自己指定了包含ListView的布局以保证ListActivity能够持有关键的ListView实例。

ListView控件的实例得到了,接下来看系统如何将ListView和PreferenceScreen这个抽象的概念联系到了一起。

PreferenceScreen#bind()

public final class PreferenceScreen extends PreferenceGroup…{public void bind(ListView listView) {// 将ListView的item点击回调设置为自己。listView.setOnItemClickListener(this);// 调用getRootAdapter()作成Adapter实例并设置给ListView去展示。listView.setAdapter(getRootAdapter());onAttachedToActivity();}public ListAdapter getRootAdapter() {if (mRootAdapter == null) {// 实际上就是获取PreferenceGroupAdapter实例。mRootAdapter = onCreateRootAdapter();}return mRootAdapter;}protected ListAdapter onCreateRootAdapter() {return new PreferenceGroupAdapter(this);}…
}

PreferenceGroupAdapter#PreferenceGroupAdapter()

public class PreferenceGroupAdapter extends BaseAdapter… {public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {mPreferenceGroup = preferenceGroup;// 设置PreferenceScreen的层级变化回调。mPreferenceGroup.setOnPreferenceChangeInternalListener(this);// 创建Preference列表。mPreferenceList = new ArrayList<Preference>();// 创建PreferenceLayout列表。mPreferenceLayouts = new ArrayList<PreferenceLayout>();// 初始化Preference列表并反映到View视图中。syncMyPreferences();}private void syncMyPreferences() {synchronized(this) {if (mIsSyncing) {return; // 如果已经在sync中驳回。}mIsSyncing = true;}List<Preference> newPreferenceList = new ArrayList<Preference>(mPreferenceList.size());// 从PreferenceScreen对象中取得Preference列表。flattenPreferenceGroup(newPreferenceList, mPreferenceGroup);mPreferenceList = newPreferenceList;// 通知视图刷新。notifyDataSetChanged();synchronized(this) {mIsSyncing = false; // 结束sync并将flag重置。notifyAll();}}private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {// PreferenceGroup#sortPreferences()调用Collections去将PreferenceScreen内部持有的Preference列表排序。group.sortPreferences();final int groupSize = group.getPreferenceCount();for (int i = 0; i < groupSize; i++) {final Preference preference = group.getPreference(i);// 遍历PreferenceScreen持有的Preference列表添加到Adapter的列表中去。preferences.add(preference);// mHasReturnedViewTypeCount:// 初始值为false,getItemViewType()调用过后置为true。// 表示:是否已经返回了item的viewtype和count。// Preference#isRecycleEnabled:// 默认为true,可由setIsRecycleEnabled()或Preference_recycleEnabled的style指定。// 表示:是否允许ListView缓存item的view。if (!mHasReturnedViewTypeCount && preference.isRecycleEnabled()) {// 既没有返回viewtype且支持itemview的缓存的场合,取得Preference的layout信息并创建Layout实例后缓存到列表中。addPreferenceClassName(preference);}// 元素为Group的场合,递归调用本方法继续收集Preference信息。if (preference instanceof PreferenceGroup) {final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;// isOnSameScreenAsChildren:用于表示Group是否需要和其子Preference显示在同一页面,默认为true。if (preferenceAsGroup.isOnSameScreenAsChildren()) {flattenPreferenceGroup(preferences, preferenceAsGroup);}}// 所有Preference对象都将变化回调设置为Adapter本身preference.setOnPreferenceChangeInternalListener(this);}}…
}

PreferenceGroupAdapter#addPreferenceClassName()

public class PreferenceGroupAdapter extends BaseAdapter… {private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {// 创建PreferenceLayout实例PreferenceLayout pl = in != null? in : new PreferenceLayout();// 获取Preference类信息,布局ID及插件布局ID并填充到Layout实例。pl.name = preference.getClass().getName();pl.resId = preference.getLayoutResource();pl.widgetResId = preference.getWidgetLayoutResource();return pl;}private void addPreferenceClassName(Preference preference) {// 创建每个Preference专属的PreferenceLayout实例。final PreferenceLayout pl = createPreferenceLayout(preference, null);// 从Layout列表中查找该Layout实例,获取待插入的下标。int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);// Layout列表中上不存在该Layout实例的话将该Layout添加到List中。if (insertPos < 0) {// Convert to insert indexinsertPos = insertPos * -1 - 1;mPreferenceLayouts.add(insertPos, pl);}}private static class PreferenceLayout implements Comparable<PreferenceLayout> {private int resId; // 持有classname等Preference的基本信息。…// 实现了比较器接口,按照名称,布局ID,插件布局ID的顺序去对比。// 以达到在List中排序查找的目的。public int compareTo(PreferenceLayout other) {int compareNames = name.compareTo(other.name);if (compareNames == 0) {if (resId == other.resId) {if (widgetResId == other.widgetResId) {return 0;} else {return widgetResId - other.widgetResId;}} else {return resId - other.resId;}} else {return compareNames;}}}…
}

syncMyPreferences()里创建完Preference列表和PreferenceLayout列表后将调用Adapter#notifyDataSetChanged()触发ListView开始布局,并将回调getView()。

PreferenceGroupAdapter#getView()

public class PreferenceGroupAdapter extends BaseAdapter… {public View getView(int position, View convertView, ViewGroup parent) {// 针对当前Preference实例做成临时Layout实例。final Preference preference = this.getItem(position);mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);// Layout列表中查找上述临时实例// 不存在的话将已缓存View置null,告诉Preference需要创建if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||(getItemViewType(position) == getHighlightItemViewType())) {convertView = null;}// Preference单独准备各自的View视图。View result = preference.getView(convertView, parent);if (position == mHighlightedPosition && mHighlightedDrawable != null) {// 当前下标需要高亮表示的话给当前View外层包裹一层高亮背景ViewGroup wrapper = new FrameLayout(parent.getContext());wrapper.setLayoutParams(sWrapperLayoutParams);wrapper.setBackgroundDrawable(mHighlightedDrawable);wrapper.addView(result);result = wrapper;}return result;}// 覆写了getItem()从Preference列表中取得指定下标的Preference实例。public Preference getItem(int position) {if (position < 0 || position >= getCount()) return null;return mPreferenceList.get(position);}…
}

Preference#getView()

public class Preference implements Comparable<Preference> {…public View getView(View convertView, ViewGroup parent) {// 判断View是否已缓存if (convertView == null) {convertView = onCreateView(parent); // 未缓存则新建}// 将View和数据绑定onBindView(convertView);return convertView;}protected View onCreateView(ViewGroup parent) {final LayoutInflater layoutInflater =(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);// 将Preference单独的Layout解析并转化为Viewfinal View layout = layoutInflater.inflate(mLayoutResId, parent, false);// 需要显示Widget的话将其布局添加到上述View,否则将Widget隐藏final ViewGroup widgetFrame = (ViewGroup) layout.findViewById(com.android.internal.R.id.widget_frame);if (widgetFrame != null) {if (mWidgetLayoutResId != 0) {layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);} else {widgetFrame.setVisibility(View.GONE);}}return layout;}// 将title,summary,icon等信息反映到对应View控件protected void onBindView(View view) {final TextView titleView = (TextView) view.findViewById(com.android.internal.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);}} else {titleView.setVisibility(View.GONE);}}final TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);if (summaryView != null) {final CharSequence summary = getSummary();if (!TextUtils.isEmpty(summary)) {summaryView.setText(summary);summaryView.setVisibility(View.VISIBLE);} else {summaryView.setVisibility(View.GONE);}}final ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon);if (imageView != null) {if (mIconResId != 0 || mIcon != null) {if (mIcon == null) {mIcon = getContext().getDrawable(mIconResId);}if (mIcon != null) {imageView.setImageDrawable(mIcon);}}if (mIcon != null) {imageView.setVisibility(View.VISIBLE);} else {imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);}}...}
}

总结一下PreferenceGroupAdapter的上述逻辑。
1. 从PreferenceScreen中读取并作成Preference列表和PreferenceLayout列表。
2. 手动触发ListView的绘制处理,让每个Preference单独准备自己的itemView。
3. Preference的layout存在变化的可能,每次准备itemView之前首先从PreferenceLayout列表中检查是否存在,如果不存在的话,告知Preference先执行View视图的创建处理然后再绑定。

至此,APP调用PreferenceActivity#addPreferencesFromResource()后系统如何展示设置画面的逻辑阐述完了。

简化流程

STEP ① 解析Preference布局文件得到PreferenceScreen
PreferenceActivity#addPreferencesFromResource()
PreferenceManager#inflateFromResource(xml id)
PreferenceInflater#inflate()
   PreferenceGroup#addPreference()

STEP ② 根据PreferenceScreen做成Adapter并和ListView绑定
PreferenceActivity#setPreferencesScreen()→bindPreferences()
PreferenceScreen#bind()
  PreferenceGroupAdapter#syncMyPreferences()

STEP ③ ListView绘制调用Preference准备itemView
PreferenceGroupAdapter#getView()
Preference#getView()→onCreateView()→onBindView()

除了PreferenceActivity我们知道还可以使用PreferenceFragment展示设置页面。PreferenceFragment也提供了addPreferencesFromResource()供APP调用,使用方法几乎一致。那么原理也一样吗?

PreferenceFragment#addPreferencesFromResource()

public abstract class PreferenceFragment extends Fragment … {…public PreferenceManager getPreferenceManager() {return mPreferenceManager;}public void setPreferenceScreen(PreferenceScreen preferenceScreen) {if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {onUnbindPreferences();mHavePrefs = true;if (mInitDone) {postBindPreferences();}}}public void addPreferencesFromResource(@XmlRes int preferencesResId) {requirePreferenceManager();setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(),preferencesResId, getPreferenceScreen()));}private void bindPreferences() {final PreferenceScreen preferenceScreen = getPreferenceScreen();if (preferenceScreen != null) {View root = getView();if (root != null) {View titleView = root.findViewById(android.R.id.title);if (titleView instanceof TextView) {CharSequence title = preferenceScreen.getTitle();if (TextUtils.isEmpty(title)) {titleView.setVisibility(View.GONE);} else {((TextView) titleView).setText(title);titleView.setVisibility(View.VISIBLE);}}}preferenceScreen.bind(getListView());}onBindPreferences();}…
}

上述逻辑和PreferenceActivity的调用逻辑几乎一样,也是持有PreferenceManager实例去加载xml后得到PreferenceScreen对象后和ListView进行绑定。

但是有一点是有区别的,PreferenceActivity是继承自ListActivity,通过加载包含ListView的布局让父类的关于ListView的API起效。

而PreferenceFragment是普通的Fragment组件,其ListView对象是自己单独加载进来的,关于ListView实例的一些API也从ListActivity中拷贝了进来。

PreferenceFragment#onCreateView()

public abstract class PreferenceFragment extends Fragment … {private int mLayoutResId = com.android.internal.R.layout.preference_list_fragment;public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {…// 从style中取得自定义布局,默认为包含ListView的preference_list_fragment.xmlmLayoutResId = a.getResourceId(com.android.internal.R.styleable.PreferenceFragment_layout,mLayoutResId);a.recycle();return inflater.inflate(mLayoutResId, container, false);}public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);…ListView lv = (ListView) view.findViewById(android.R.id.list);if (lv != null&& a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceFragment_divider)) {lv.setDivider(a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider));}a.recycle();}
} 

上述原理可以看出,Android通过一系列抽象出来的Preference的组件和内部指定的ListView进行交互,让APP简单配置下Preference布局,并不用和View打交道就可以快速搭建设置界面。

下一篇我们将研究下Android的常用Preference组件如何实现的。以及APP如何自定义Preference组件。

Preference组件探究之源码解读相关推荐

  1. php yii框架源码,yii 源码解读

    date: 2017-11-21 18:15:18 title: yii 源码解读 本篇博客阅读指南: php & 代码提示: 工欲善其事必先利其器 yii 源码阅读指南: 整体上全貌上进行了 ...

  2. php service locator,Yii源码解读-服务定位器(ServiceLocator)

    SL的目的也是解耦,并且非常适合基于服务和组件的应用. Service Locator充当了一个运行时的链接器的角色,可以在运行时动态地修改一个类所要选用的服务, 而不必对类作任何的修改. 一个类可以 ...

  3. Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_3细说invokeBeanDefinitionRegistryPostProcessors

    文章目录 Pre 细说invokeBeanDefinitionRegistryPostProcessors 流程图 源码分析 解析配置类 parser.parse(candidates) 配置类注册到 ...

  4. aqs java 简书,Java AQS源码解读

    1.先聊点别的 说实话,关于AQS的设计理念.实现.使用,我有打算写过一篇技术文章,但是在写完初稿后,发现掌握的还是模模糊糊的,模棱两可. 痛定思痛,脚踏实地重新再来一遍.这次以 Java 8源码为基 ...

  5. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

  6. Slim 框架源码解读

    0x00 前言 Slim 是由<PHP The Right Way>作者开发的一款 PHP 微框架,代码量不算多(比起其它重型框架来说),号称可以一下午就阅读完(我觉得前提是熟悉 Slim ...

  7. spring-cloud-context源码解读

    spring-cloud-context源码解读 1. The Bootstrap Application Context Understanding source code 2. Applicati ...

  8. PackageManagerService Android 8.1 源码解读 02

    接上文:PackageManagerService Android 8.1 源码解读 01 d.第三步细节:PKMS.main(),main函数主要工作: [检查]Package编译相关系统属性 [调 ...

  9. Amigo 源码解读

    Amigo 源码解读 现在 hotfix 框架有很多,原理大同小异,基本上是基于qq空间这篇文章 或者微信的方案.可惜的是微信的 Tinker 以及 QZone 都没有将其具体实现开源出来,只是在文章 ...

最新文章

  1. 科技互联网公司真的越来越重视数学了吗?
  2. 浦东新区2019年下半年部分街镇社区工作者和部分单位编外人员公开招聘考试大纲...
  3. Guzzle – 构建 RESTful Web 服务的 PHP HTTP 框架
  4. 18、Java并发性和多线程-饥饿与公平
  5. 解决input设置背景后,在ie7下浏览内容过长背景跟着滚动
  6. 在JS函数中执行C#中的函数、字段
  7. 发那科机器人圆弧指令怎么用_发那科机器人PR指令
  8. 计算机科学与技术导论课论文题目,优秀计算机专业导论论文题目 计算机专业导论论文题目哪个好...
  9. grads插值_GrADS中格点插值到站点(gr2stn)的详细方法
  10. 【WINDOWS / DOS 批处理】添加注释
  11. netapp linux ntfs,netapp存储常用命令
  12. 清华紫光输入法linux,清华紫光拼音输入法
  13. 华为交换机或路由器释放DHCP已分配的地址
  14. 什么是邮箱地址,电子邮箱地址大全 163vip邮箱都有哪些地址?
  15. Reflector 3 for Mac(ios屏幕镜像工具)
  16. java两个frame之间_java – JFrame中的两个JPanel,另一个是JP
  17. 第四回:matplotlib文字图例尽眉目
  18. CarSim 2022软件
  19. 并联下垂控制(DROOP控制)_SIMULINK模型搭建详解
  20. 在鼠标右键添加“使用WPS打开”

热门文章

  1. 蓝色对比关系图表合集PPT模板-优页文档
  2. (T2I) VILT
  3. 四川农业大学计算机考研调剂,四川农业大学大学2017年考研调剂信息
  4. form表单以及CSS
  5. python openpyxl遍历工作表(sheet)
  6. mock和fake的区别
  7. 跟yy2000流氓网页奋战的一天
  8. Kaggle泰坦尼克之灾:逻辑回归模型实现笔记(一)
  9. TOGAF ADM: 它是什么,它为什么如此重要?
  10. 模仿CSDN浏览器右下角弹出广告,兼容所有浏览器,内容可自定义,扩张性强