当前位置: 首页 > news >正文

人才招聘网网站策划方案深圳市中心是哪个区

人才招聘网网站策划方案,深圳市中心是哪个区,网站建设盈利模式,威海市城乡建设局网站Android Settings 系列文章#xff1a; Android Settings解析SettingsIntelligenceSettingsProvider 首语 Android设置应用是Android系统中一个非常重要的系统应用#xff0c;它允许用户调整和设置系统的各种参数和功能#xff08;系统设置/自定义设置/控制应用权限/开发…Android Settings 系列文章 Android Settings解析SettingsIntelligenceSettingsProvider 首语 Android设置应用是Android系统中一个非常重要的系统应用它允许用户调整和设置系统的各种参数和功能系统设置/自定义设置/控制应用权限/开发者选项/系统信息等使用户获得更好的使用体验。同时它一般也是Android系统开发者了解深入的第一个系统级应用也是用户使用最频繁的系统应用。 源码目录 AOSP源码路径为packages/apps/Settings。src/com/android/settings目录下包含Settings的主要源码。libs目录下的contextualcards.aar包含实现上下文卡片功能的代码和资源它可以将相关的内容组织在一起以卡片的形式展现给用户。res目录下包含各种静态资源。Android.bp文件中可以看到模块名为Settings。 设计指南 上图是Settings里一个普通的页面从这个页面可以看出它将许多设置放在一起设置列表是多个控件的组合。 它有如下优点 提供一个很好的概述。用户应该能够浏览设置屏幕并了解所有单独的设置及其值。直观的设置项目。常用设置放在屏幕顶部。限制一个屏幕上的设置数量。将一些设置移动到单独的屏幕来创建直观的菜单。使用明确的标题和状态。标题简短而有意义。在标题下方显示状态以突出设置的值显示具体细节。 关于Settings设计的详细规则及细节可以参考官网设计指南 Preference 在Android 常用组件里存在一个Preference组件它提供了一个方便的用户界面用于管理和显示应用程序的各种设置选项让用户可以轻松浏览和更改应用程序的设置。Preference还通过SharedPreference实现保存读取数据以其key作为SharedPreference的键实现持久化数据。Settings中大多数菜单都是通过Preference去实现且使用的是androidx包的Preference因此首先了解下Preference的使用。 Preference组件和其它页面组件使用类似区别在于XML 资源必须放置于 res/xml/ 目录Preference的根标签必须为PreferenceScreen。举例如下 ?xml version1.0 encodingutf-8? PreferenceScreenxmlns:andoirdhttp://schemas.android.com/apk/res-autoPreferenceCategoryapp:keyprefer_categoryPreferenceandoird:keyfeedbackandoird:titleSend feedbackandoird:summaryReport technical issues or suggest new features//PreferenceCategory /PreferenceScreenPreferenceCategory是对Preferences进行分组的标签显示类别标题并在视觉上进行分隔Preference。如设计指南中的Settings Display页面的截图中Brightness/Lock display类别标签。 以下是Preference相关属性的介绍 attrdescriptionandoird:allowDividerAbove在菜单上显示一条分割线andoird:allowDividerBelow在菜单下显示一条分割线android:defaultValue默认值。android:dependency设置此元素附属于另一个元素依赖的可用则当前元素也可用enable反之。andoird:enableCopying启用长按复制android:enabled设置是否可用。android:fragment指定跳转fragment。android:icon指定左侧的图标。andoird:iconSpaceReserved为图标预留位置菜单向右偏移默认falseandoird:isPreferenceVisible菜单是否显示android:key选项的名称也是用来存储时唯一的key。android:layout给当前元素指定一个自定义布局。android:order偏好的顺序。如果不指定默认的顺序将字母。android:persistent是否将其值持久化。android:selectable设置是否可以选择操作。android:shouldDisableView当enabled设置为false变暗同时此属性设置为false时disable但不变暗。andoird:singleLineTitle菜单title限制为一行默认为trueandroid:summary摘要配置的简要说明显示在标题下面。android:title选项的标题当没有设置summary时自动垂直居中显示。android:widgetLayout控件可调小部件的布局。是为一个优先选择的布局比如一个复选框选择要指定一个自定义布局注意包括的只是复选框在这里。 Setting中扩展的attr如下 declare-styleable namePreference!-- 搜索关键词 --attr namekeywords formatstring /!-- 是否可搜索默认为true --attr namesearchable formatboolean /!-- Preference controller类 --attr namecontroller formatstring /!-- 自定义字幕 --attr nameunavailableSliceSubtitle formatstring /!-- Preference针对work profile默认为false --attr nameforWork formatboolean /!-- 用于在双窗格上突出显示菜单首选项的标识符 --attr namehighlightableMenuKey formatstring //declare-styleable查看Preference的源码可知还有一些自定义Preference实现的组件如CheckBoxPreference/DropDownPreference/EditTextPreference/ListPreference/SwitchPreference等它是针对不同Android控件(checkbox/dropdown/edittext等)实现的自定义Preference如需使用只需要在xml引用即可 然后创建一个fragment继承于PreferenceFragmentCompat。onCreatePreferences方法在PreferenceFragmentCompat的onCreate方法调用用于创建Prerefence。通过setPreferencesFromResource引用定义的Preference xml资源。这样通过Preference实现的一个简单的菜单就显示在屏幕上了。 public class SettingsFragment extends PreferenceFragmentCompat {Overridepublic void onCreatePreferences(Bundle savedInstanceState, String rootKey) {setPreferencesFromResource(R.xml.root_preferences, rootKey);}//preference点击事件通过key区分Overridepublic boolean onPreferenceTreeClick(NonNull Preference preference) {return super.onPreferenceTreeClick(preference);} }看下Preference的点击事件实现如果对应的fragment实现了OnPreferenceStartFragmentCallback重写了onPreferenceStartFragment方法那么Preference的跳转实现就在onPreferenceStartFragment方法里并返回处理结果如果没有实现OnPreferenceStartFragmentCallback则去获取xml中设置的android:fragment或者setFragment设置的fragment跳转。 public abstract class PreferenceFragmentCompat extends Fragment implementsPreferenceManager.OnPreferenceTreeClickListener,PreferenceManager.OnDisplayPreferenceDialogListener,PreferenceManager.OnNavigateToScreenListener,DialogPreference.TargetFragment {Overridepublic boolean onPreferenceTreeClick(NonNull Preference preference) {if (preference.getFragment() ! null) {boolean handled false;if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) {handled ((OnPreferenceStartFragmentCallback) getCallbackFragment()).onPreferenceStartFragment(this, preference);}Fragment callbackFragment this;while (!handled callbackFragment ! null) {if (callbackFragment instanceof OnPreferenceStartFragmentCallback) {handled ((OnPreferenceStartFragmentCallback) callbackFragment).onPreferenceStartFragment(this, preference);}callbackFragment callbackFragment.getParentFragment();}if (!handled getContext() instanceof OnPreferenceStartFragmentCallback) {handled ((OnPreferenceStartFragmentCallback) getContext()).onPreferenceStartFragment(this, preference);}if (!handled getActivity() instanceof OnPreferenceStartFragmentCallback) {handled ((OnPreferenceStartFragmentCallback) getActivity()).onPreferenceStartFragment(this, preference);}if (!handled) { final FragmentManager fragmentManager getParentFragmentManager();final Bundle args preference.getExtras();final Fragment fragment fragmentManager.getFragmentFactory().instantiate(requireActivity().getClassLoader(), preference.getFragment());fragment.setArguments(args);fragment.setTargetFragment(this, 0);fragmentManager.beginTransaction().replace(((View) requireView().getParent()).getId(), fragment).addToBackStack(null).commit();}return true;}return false;} }androidx包中Preference只有针对Fragment的实现没有针对Activity的实现。还有针对Dialog实现的PreferenceDialogFragmentCompat。在dialog里引用preference。 页面加载分析 本文以Android 13 Settings源码为例进行分析。 首页加载流程 在AndroidManifest.xml中可以看到启动activity为SettingsSettings中包含大量的静态类继承于SettingsActivity。 activity-alias android:nameSettingsandroid:labelstring/settings_label_launcherandroid:taskAffinitycom.android.settings.rootandroid:launchModesingleTaskandroid:exportedtrueandroid:targetActivity.homepage.SettingsHomepageActivityintent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.DEFAULT /category android:nameandroid.intent.category.LAUNCHER //intent-filtermeta-data android:nameandroid.app.shortcuts android:resourcexml/shortcuts//activity-alias查看SettingsActivity的onCreate方法首先读取fragment class和HighlightMenuKey设置布局为settings_main_prefs.xml通过intent传递的数据显示不同的布局。切换fragment或根据之前保存状态显示页面。settings_main_prefs.xml中包含顶部的switch bar底部的button(Back/Skip/Next)和中间的framelayout显示Fragmentswitch bar和button默认隐藏。 public class SettingsActivity extends SettingsBaseActivityimplements PreferenceManager.OnPreferenceTreeClickListener,PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,ButtonBarHandler, FragmentManager.OnBackStackChangedListener {Overrideprotected void onCreate(Bundle savedState) {// Should happen before any call to getIntent()getMetaData();final Intent intent getIntent();if (shouldShowTwoPaneDeepLink(intent) tryStartTwoPaneDeepLink(intent)) {finish();super.onCreate(savedState);return;}super.onCreate(savedState);Log.d(LOG_TAG, Starting onCreate);createUiFromIntent(savedState, intent);}private void getMetaData() {try {ActivityInfo ai getPackageManager().getActivityInfo(getComponentName(),PackageManager.GET_META_DATA);if (ai null || ai.metaData null) return;//读取设置的fragmentmFragmentClass ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);mHighlightMenuKey ai.metaData.getString(META_DATA_KEY_HIGHLIGHT_MENU_KEY);} catch (NameNotFoundException nnfe) {// No recoveryLog.d(LOG_TAG, Cannot get Metadata for: getComponentName().toString());}}protected void createUiFromIntent(Bundle savedState, Intent intent) {...setContentView(R.layout.settings_main_prefs);...if (savedState ! null) {// We are restarting from a previous saved state; used that to initialize, instead// of starting fresh.setTitleFromIntent(intent);ArrayListDashboardCategory categories savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);if (categories ! null) {mCategories.clear();mCategories.addAll(categories);setTitleFromBackStack();}} else {//加载fragmentlaunchSettingFragment(initialFragmentName, intent);}} }AndroidManifest中并没有传递Settings对应的fragment数据而是指定targetActivity为SettingsHomepageActivity查看SettingsHomepageActivity的onCreate方法布局为settings_homepage_container.xml然后初始化搜索栏。设备不是低内存的情况下加载Suggestion菜单设置fragment为TopLevelSettings。 Override protected void onCreate(Bundle savedInstanceState) {...setContentView(R.layout.settings_homepage_container);...initSearchBarView();...if (!getSystemService(ActivityManager.class).isLowRamDevice()) {initAvatarView();final boolean scrollNeeded mIsEmbeddingActivityEnabled !TextUtils.equals(getString(DEFAULT_HIGHLIGHT_MENU_KEY), highlightMenuKey);showSuggestionFragment(scrollNeeded);if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {showFragment(() - new ContextualCardsFragment(), R.id.contextual_cards_content);((FrameLayout) findViewById(R.id.main_content)).getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);}}mMainFragment showFragment(() - {final TopLevelSettings fragment new TopLevelSettings();fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,highlightMenuKey);return fragment;}, R.id.main_content);... } private void initSearchBarView() {final Toolbar toolbar findViewById(R.id.search_action_bar);FeatureFactory.getFactory(this).getSearchFeatureProvider().initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);if (mIsEmbeddingActivityEnabled) {final Toolbar toolbarTwoPaneVersion findViewById(R.id.search_action_bar_two_pane);//初始化搜索实现FeatureFactory.getFactory(this).getSearchFeatureProvider().initSearchToolbar(this /* activity */, toolbarTwoPaneVersion,SettingsEnums.SETTINGS_HOMEPAGE);}}在搜索的实现类SearchFeatureProviderImpl中可以看到构造的搜索跳转intent如下可以发现Settings的搜索核心实现在另外一个app内包名为com.android.settings.intelligence。在SettingsIntelligence 这篇文章会对Settings搜索和SettingsIntelligence模块进行深入分析继续分析Settings页面加载。 Overridepublic Intent buildSearchIntent(Context context, int pageId) {return new Intent(Settings.ACTION_APP_SEARCH_SETTINGS).setPackage(getSettingsIntelligencePkgName(context)).putExtra(Intent.EXTRA_REFERRER, buildReferrer(context, pageId));}default String getSettingsIntelligencePkgName(Context context) {return context.getString(R.string.config_settingsintelligence_package_name);}!-- Settings intelligence package name --string nameconfig_settingsintelligence_package_name translatablefalsecom.android.settings.intelligence/string首先看下TopLevelSettings的继承关系TopLevelSettings继承于DashboardFragmentDashboardFragment是静态和动态Settings 菜单的基类Settings中大多数菜单对应的fragment继承于DashboardFragment它继承于SettingsPreferenceFragmentSettingsPreferenceFragment是Settings fragment的基类它继承于InstrumentedPreferenceFragment它记录fragment显示状态继承于ObservablePreferenceFragmentObservablePreferenceFragment是在SettingsLib里定义的模块路径frameworks/base/packages/SettingsLib在后面会对这个模块深入分析。ObservablePreferenceFragment继承于PreferenceFragmentCompat。 public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener,PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {public TopLevelSettings() {final Bundle args new Bundle();// Disable the search icon because this page uses a full search view in actionbar.args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);setArguments(args);}//设置preference对应xml资源Overrideprotected int getPreferenceScreenResId() {return R.xml.top_level_settings;}Overridepublic void onAttach(Context context) {super.onAttach(context);HighlightableMenu.fromXml(context, getPreferenceScreenResId());use(SupportPreferenceController.class).setActivity(getActivity());} }首先查看onAttach方法调用DashboardFragment的use方法获取SupportPreferenceController实例。SupportPreferenceController存储在mPreferenceControllers中通过addPreferenceController方法将PreferenceController添加到mPreferenceControllers中在DashboardFragment的onAttach方法中会调用addPreferenceController通过createPreferenceControllers方法将代码设置的controller添加到mControllers集合然后读取xml中设置的controller添加到mControllers集合。 然后看下加载Preference的onCreatePreferences方法首先通过getPreferenceScreenResId获取对应的xml资源TopLevelSettings对应的是top_level_settings.xmlxml中定义了Settings首页菜单最终通过addPreferencesFromResource方法显示Preference。在TopLevelSettings的onCreatePreferences方法还对图标颜色进行了处理。Preference点击事件调用Preferencecontroller的handlePreferenceTreeClick方法。 public abstract class DashboardFragment extends SettingsPreferenceFragmentimplements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,BasePreferenceController.UiBlockListener {Overridepublic void onAttach(Context context) {super.onAttach(context);...// Load preference controllers from codefinal ListAbstractPreferenceController controllersFromCode createPreferenceControllers(context);// Load preference controllers from xml definitionfinal ListBasePreferenceController controllersFromXml PreferenceControllerListHelper.getPreferenceControllersFromXml(context, getPreferenceScreenResId());// Filter xml-based controllers in case a similar controller is created from code already.final ListBasePreferenceController uniqueControllerFromXml PreferenceControllerListHelper.filterControllers(controllersFromXml, controllersFromCode);// Add unique controllers to list.if (controllersFromCode ! null) {mControllers.addAll(controllersFromCode);}mControllers.addAll(uniqueControllerFromXml);for (AbstractPreferenceController controller : mControllers) {addPreferenceController(controller);} }//获取对应Preference controller实例protected T extends AbstractPreferenceController T use(ClassT clazz) {ListAbstractPreferenceController controllerList mPreferenceControllers.get(clazz);if (controllerList ! null) {if (controllerList.size() 1) {Log.w(TAG, Multiple controllers of Class clazz.getSimpleName() found, returning first one.);}return (T) controllerList.get(0);}return null;}protected void addPreferenceController(AbstractPreferenceController controller) {if (mPreferenceControllers.get(controller.getClass()) null) {mPreferenceControllers.put(controller.getClass(), new ArrayList());}mPreferenceControllers.get(controller.getClass()).add(controller);}Overridepublic void onCreatePreferences(Bundle savedInstanceState, String rootKey) {checkUiBlocker(mControllers);refreshAllPreferences(getLogTag());...}private void refreshAllPreferences(final String tag) {...// Add resource based tiles.displayResourceTiles();...}private void displayResourceTiles() {final int resId getPreferenceScreenResId();if (resId 0) {return;}addPreferencesFromResource(resId);final PreferenceScreen screen getPreferenceScreen();screen.setOnExpandButtonClickListener(this);displayResourceTilesToScreen(screen);}Overridepublic boolean onPreferenceTreeClick(Preference preference) {final CollectionListAbstractPreferenceController controllers mPreferenceControllers.values();for (ListAbstractPreferenceController controllerList : controllers) {for (AbstractPreferenceController controller : controllerList) {if (controller.handlePreferenceTreeClick(preference)) {// log here since calling super.onPreferenceTreeClick will be skippedwritePreferenceClickMetric(preference);return true;}}}return super.onPreferenceTreeClick(preference);} }接下来我们以SupportPreferenceController为例分析下PreferenceController。它继承于BasePreferenceControllerBasePreferenceController继承于AbstractPreferenceController。 public class SupportPreferenceController extends BasePreferenceController {//指定显示状态Overridepublic int getAvailabilityStatus() {return mSupportFeatureProvider null ? UNSUPPORTED_ON_DEVICE : AVAILABLE;}//点击事件Overridepublic boolean handlePreferenceTreeClick(Preference preference) {if (preference null || mActivity null ||!TextUtils.equals(preference.getKey(), getPreferenceKey())) {return false;}mSupportFeatureProvider.startSupport(mActivity);return true;} }AbstractPreferenceController是一个抽象类主要方法如下 public abstract class AbstractPreferenceController {//preference是否有效public abstract boolean isAvailable();//preference点击事件public boolean handlePreferenceTreeClick(Preference preference) {return false;}//显示preferencepublic void displayPreference(PreferenceScreen screen) {final String prefKey getPreferenceKey();if (TextUtils.isEmpty(prefKey)) {Log.w(TAG, Skipping displayPreference because key is empty: getClass().getName());return;}if (isAvailable()) {setVisible(screen, prefKey, true /* visible */);if (this instanceof Preference.OnPreferenceChangeListener) {final Preference preference screen.findPreference(prefKey);preference.setOnPreferenceChangeListener((Preference.OnPreferenceChangeListener) this);}} else {setVisible(screen, prefKey, false /* visible */);}}//更新preference状态(summary)public void updateState(Preference preference) {refreshSummary(preference);}//preference keypublic abstract String getPreferenceKey(); }BasePreferenceController对AbstractPreferenceController进行了简单封装对Preference状态进行处理共有6中状态其次对Preference搜索支持也进行了处理。 public abstract class BasePreferenceController extends AbstractPreferenceController implements Sliceable {Retention(RetentionPolicy.SOURCE)IntDef({AVAILABLE, AVAILABLE_UNSEARCHABLE, UNSUPPORTED_ON_DEVICE, DISABLED_FOR_USER,DISABLED_DEPENDENT_SETTING, CONDITIONALLY_UNAVAILABLE})public interface AvailabilityStatus {}//preference 有效public static final int AVAILABLE 0;//preference 有效不能搜索public static final int AVAILABLE_UNSEARCHABLE 1;//当前不可用将来可能可用public static final int CONDITIONALLY_UNAVAILABLE 2;//设备不支持public static final int UNSUPPORTED_ON_DEVICE 3;//当前用户不支持public static final int DISABLED_FOR_USER 4;//preference置灰无法更改依赖其它设置public static final int DISABLED_DEPENDENT_SETTING 5;//指定Preference状态AvailabilityStatuspublic abstract int getAvailabilityStatus();//preference 有效的实现Overridepublic final boolean isAvailable() {if (mIsForWork mWorkProfileUser null) {return false;}final int availabilityStatus getAvailabilityStatus();return (availabilityStatus AVAILABLE|| availabilityStatus AVAILABLE_UNSEARCHABLE|| availabilityStatus DISABLED_DEPENDENT_SETTING);}//针对DISABLED_DEPENDENT_SETTING状态进行置灰Overridepublic void displayPreference(PreferenceScreen screen) {super.displayPreference(screen);if (getAvailabilityStatus() DISABLED_DEPENDENT_SETTING) {// Disable preference if it depends on another setting.final Preference preference screen.findPreference(getPreferenceKey());if (preference ! null) {preference.setEnabled(false);}}} }从上面可以看出来Preferencecontroller它是Preference的控制器控制Preference的显示点击事件搜索。Settings中大多数Preference的控制器都继承于BasePreferenceController。以上就是首页菜单加载的流程。 那SupportPreferenceContoller是那个菜单的控制器呢它是首页Tips support菜单的控制器。top_level_settings.xml有定义这个preference。Settings中大多数是以这样的实现构成的xml定义Preference和引用PreferencecontrollerPreferencecontroller去实现对应菜单的逻辑。 二级页面加载流程 首先查看首页菜单的点击事件它是获取了Preference controller的handlePreferenceTreeClick方法处理点击事件。 public abstract class DashboardFragment extends SettingsPreferenceFragmentimplements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,BasePreferenceController.UiBlockListener {Overridepublic boolean onPreferenceTreeClick(Preference preference) {final CollectionListAbstractPreferenceController controllers mPreferenceControllers.values();for (ListAbstractPreferenceController controllerList : controllers) {for (AbstractPreferenceController controller : controllerList) {if (controller.handlePreferenceTreeClick(preference)) {// log here since calling super.onPreferenceTreeClick will be skippedwritePreferenceClickMetric(preference);return true;}}}return super.onPreferenceTreeClick(preference);} }Preference controller的点击基础实现如下 public abstract class BasePreferenceController extends AbstractPreferenceController implements Sliceable {Overridepublic boolean handlePreferenceTreeClick(Preference preference) {if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {return super.handlePreferenceTreeClick(preference);}if (!mIsForWork || mWorkProfileUser null) {return super.handlePreferenceTreeClick(preference);}final Bundle extra preference.getExtras();extra.putInt(EXTRA_USER_ID, mWorkProfileUser.getIdentifier());new SubSettingLauncher(preference.getContext()).setDestination(preference.getFragment()).setSourceMetricsCategory(preference.getExtras().getInt(CATEGORY,SettingsEnums.PAGE_UNKNOWN)).setArguments(preference.getExtras()).setUserHandle(mWorkProfileUser).launch();return true;} }如果Preference controller不处理则通过onPreferenceStartFragment方法TopLevelSettings实现了OnPreferenceStartFragmentCallback public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener,PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {Overridepublic boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {new SubSettingLauncher(getActivity()).setDestination(pref.getFragment()).setArguments(pref.getExtras()).setSourceMetricsCategory(caller instanceof Instrumentable? ((Instrumentable) caller).getMetricsCategory(): Instrumentable.METRICS_CATEGORY_UNKNOWN).setTitleRes(-1).setIsSecondLayerPage(true).launch();return true;} }可以发现跳转二级页面的实现都是通过SubSettingLauncher来传递参数并且跳转目标fragment。toIntent方法构造调整intent可以看到跳转的类是SubSettingslauncher方法进行跳转。 public class SubSettingLauncher {public void launch() {...final Intent intent toIntent();boolean launchAsUser mLaunchRequest.mUserHandle ! null mLaunchRequest.mUserHandle.getIdentifier() ! UserHandle.myUserId();boolean launchForResult mLaunchRequest.mResultListener ! null;if (launchAsUser launchForResult) {launchForResultAsUser(intent, mLaunchRequest.mUserHandle,mLaunchRequest.mResultListener, mLaunchRequest.mRequestCode);} else if (launchAsUser !launchForResult) {launchAsUser(intent, mLaunchRequest.mUserHandle);} else if (!launchAsUser launchForResult) {launchForResult(mLaunchRequest.mResultListener, intent, mLaunchRequest.mRequestCode);} else {launch(intent);}}public Intent toIntent() {final Intent intent new Intent(Intent.ACTION_MAIN);copyExtras(intent);intent.setClass(mContext, SubSettings.class);if (TextUtils.isEmpty(mLaunchRequest.mDestinationName)) {throw new IllegalArgumentException(Destination fragment must be set);}intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, mLaunchRequest.mDestinationName);if (mLaunchRequest.mSourceMetricsCategory 0) {throw new IllegalArgumentException(Source metrics category must be set);}intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,mLaunchRequest.mSourceMetricsCategory);intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, mLaunchRequest.mArguments);intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,mLaunchRequest.mTitleResPackageName);intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,mLaunchRequest.mTitleResId);intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.mTitle);intent.addFlags(mLaunchRequest.mFlags);intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,mLaunchRequest.mTransitionType);intent.putExtra(SettingsActivity.EXTRA_IS_SECOND_LAYER_PAGE,mLaunchRequest.mIsSecondLayerPage);return intent;}VisibleForTestingvoid launch(Intent intent) {mContext.startActivity(intent);} }SubSettings继承于SettingsActivity说明首页菜单除了自定义实现页面跳转逻辑的之外其它都是跳转到SubSettings这个Activity这里有一个小技巧正常情况下我们抓取当前页面的Activity可以通过以下命令 adb shell dumpsys window | grep mCurrentFocus但是我们不清楚这个页面对应的fragment通过上面命令都是SubSettings。在跳转时可以通过以下命令获取fragment adb logcat -s SubSettings这样就打印出了具体的启动fragment。 public class SubSettings extends SettingsActivity {Overridepublic boolean onNavigateUp() {finish();return true;}Overrideprotected boolean isValidFragment(String fragmentName) {//打印页面Log.d(SubSettings, Launching fragment fragmentName);return true;} }剩下的相关逻辑都和TopLevelSettings类似这里不继续展开分析了。 动态插入菜单 在Settings里的一些菜单我们会发现一些菜单在xml和代码中并未添加但实际上显示在页面上这是为什么呢原来是Settings支持动态插入菜单。实现逻辑如下 在创建Preference的时候refreshAllPreferences方法刷新Preference包括来自xml的静态Preference和动态Preference。动态Preference添加实现在refreshDashboardTiles方法中。 public abstract class DashboardFragment extends SettingsPreferenceFragmentimplements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,BasePreferenceController.UiBlockListener {Overridepublic void onCreatePreferences(Bundle savedInstanceState, String rootKey) {checkUiBlocker(mControllers);refreshAllPreferences(getLogTag());...}private void refreshAllPreferences(final String tag) {...// Add resource based tiles.displayResourceTiles();//动态PreferencerefreshDashboardTiles(tag);}private void refreshDashboardTiles(final String tag) {final PreferenceScreen screen getPreferenceScreen();final DashboardCategory category mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());...final ListTile tiles category.getTiles();// Create a list to track which tiles are to be removed.final MapString, ListDynamicDataObserver remove new ArrayMap(mDashboardTilePrefKeys);// Install dashboard tiles and collect pending observers.final boolean forceRoundedIcons shouldForceRoundedIcon();final ListDynamicDataObserver pendingObservers new ArrayList();for (Tile tile : tiles) {final String key mDashboardFeatureProvider.getDashboardKeyForTile(tile);...final ListDynamicDataObserver observers;if (mDashboardTilePrefKeys.containsKey(key)) {// Have the key already, will rebind.final Preference preference screen.findPreference(key);observers mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this, forceRoundedIcons, preference, tile, key,mPlaceholderPreferenceController.getOrder());} else {// Dont have this key, add it.final Preference pref createPreference(tile);observers mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this, forceRoundedIcons, pref, tile, key,mPlaceholderPreferenceController.getOrder());//添加Preferencescreen.addPreference(pref);registerDynamicDataObservers(observers);mDashboardTilePrefKeys.put(key, observers);}if (observers ! null) {pendingObservers.addAll(observers);}remove.remove(key);...//类别key public String getCategoryKey() {return DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP.get(getClass().getName());} }首先获取了类别 keyPARENT_TO_CATEGORY_KEY_MAP中实现了页面和key的对应。通过页面class name来确定页面对应的key。 public class DashboardFragmentRegistry {static {PARENT_TO_CATEGORY_KEY_MAP new ArrayMap();PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),CategoryKey.CATEGORY_HOMEPAGE);PARENT_TO_CATEGORY_KEY_MAP.put(NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),CategoryKey.CATEGORY_CONNECT);PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(),...} }页面key的定义在CategoryKey类中。这样通过key就清楚当前页面是否动态加载那些菜单。 public final class CategoryKey {// Activities in this category shows up in Settings homepage.public static final String CATEGORY_HOMEPAGE com.android.settings.category.ia.homepage;// Top level category.public static final String CATEGORY_NETWORK com.android.settings.category.ia.wireless;public static final String CATEGORY_CONNECT com.android.settings.category.ia.connect;public static final String CATEGORY_DEVICE com.android.settings.category.ia.device;public static final String CATEGORY_APPS com.android.settings.category.ia.apps;... }getTilesForCategory方法的实现在DashboardFeatureProviderImpl类中它是通过CategoryManager类的getTilesByCategory方法实现。mCategories是获取所有动态菜单的集合。 public class CategoryManager {public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {tryInitCategories(context);return mCategoryByKeyMap.get(categoryKey);}private synchronized void tryInitCategories(Context context) {// Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange// happens.tryInitCategories(context, false /* forceClearCache */);}private synchronized void tryInitCategories(Context context, boolean forceClearCache) {if (mCategories null) {final boolean firstLoading mCategoryByKeyMap.isEmpty();if (forceClearCache) {mTileByComponentCache.clear();}mCategoryByKeyMap.clear();//获取categories listmCategories TileUtils.getCategories(context, mTileByComponentCache);for (DashboardCategory category : mCategories) {mCategoryByKeyMap.put(category.key, category);}backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);sortCategories(context, mCategoryByKeyMap);filterDuplicateTiles(mCategoryByKeyMap);if (firstLoading) {logTiles(context);final DashboardCategory homepageCategory mCategoryByKeyMap.get(CategoryKey.CATEGORY_HOMEPAGE);if (homepageCategory null) {return;}for (Tile tile : homepageCategory.getTiles()) {final String key tile.getKey(context);if (TextUtils.isEmpty(key)) {Log.w(TAG, Key hint missing for homepage tile: tile.getTitle(context));continue;}HighlightableMenu.addMenuKey(key);}}}} }从loadActivityTiles方法里可以看出在Settings里动态插入菜单只能是系统应用。 源码路径frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java public class TileUtils {public static final String EXTRA_SETTINGS_ACTION com.android.settings.action.EXTRA_SETTINGS;public static final String IA_SETTINGS_ACTION com.android.settings.action.IA_SETTINGS;private static final String SETTINGS_ACTION com.android.settings.action.SETTINGS;public static ListDashboardCategory getCategories(Context context,MapPairString, String, Tile cache) {final long startTime System.currentTimeMillis();final boolean setup Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) ! 0;final ArrayListTile tiles new ArrayList();final UserManager userManager (UserManager) context.getSystemService(Context.USER_SERVICE);for (UserHandle user : userManager.getUserProfiles()) {// TODO: Needs much optimization, too many PM queries going on here.if (user.getIdentifier() ActivityManager.getCurrentUser()) {loadTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);loadTilesForAction(context, user, OPERATOR_SETTINGS, cache,OPERATOR_DEFAULT_CATEGORY, tiles, false);loadTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,MANUFACTURER_DEFAULT_CATEGORY, tiles, false);}if (setup) {loadTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);loadTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);}}...return categories;}static void loadTilesForAction(Context context,UserHandle user, String action, MapPairString, String, Tile addedCache,String defaultCategory, ListTile outTiles, boolean requireSettings) {final Intent intent new Intent(action);if (requireSettings) {// 只允许settings通过SETTINGS_ACTION添加intent.setPackage(SETTING_PKG);}loadActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent);loadProviderTiles(context, user, addedCache, defaultCategory, outTiles, intent);}private static void loadActivityTiles(Context context,UserHandle user, MapPairString, String, Tile addedCache,String defaultCategory, ListTile outTiles, Intent intent) {final PackageManager pm context.getPackageManager();final ListResolveInfo results pm.queryIntentActivitiesAsUser(intent,PackageManager.GET_META_DATA, user.getIdentifier());for (ResolveInfo resolved : results) {//系统应用if (!resolved.system) {// Do not allow any app to add to settings, only system ones.continue;}final ActivityInfo activityInfo resolved.activityInfo;final Bundle metaData activityInfo.metaData;loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);}}private static void loadTile(UserHandle user, MapPairString, String, Tile addedCache,String defaultCategory, ListTile outTiles, Intent intent, Bundle metaData,ComponentInfo componentInfo) {// Skip loading tile if the component is tagged primary_profile_only but not running on// the current user.if (user.getIdentifier() ! ActivityManager.getCurrentUser() Tile.isPrimaryProfileOnly(componentInfo.metaData)) {Log.w(LOG_TAG, Found componentInfo.name for intent intent is primary profile only, skip loading tile for uid user.getIdentifier());return;}String categoryKey defaultCategory;// Load categorycategoryKey metaData.getString(EXTRA_CATEGORY_KEY);final boolean isProvider componentInfo instanceof ProviderInfo;final PairString, String key isProvider? new Pair(((ProviderInfo) componentInfo).authority,metaData.getString(META_DATA_PREFERENCE_KEYHINT)): new Pair(componentInfo.packageName, componentInfo.name);Tile tile addedCache.get(key);if (tile null) {tile isProvider? new ProviderTile((ProviderInfo) componentInfo, categoryKey, metaData): new ActivityTile((ActivityInfo) componentInfo, categoryKey);addedCache.put(key, tile);} else {tile.setMetaData(metaData);}if (!tile.userHandle.contains(user)) {tile.userHandle.add(user);}if (!outTiles.contains(tile)) {outTiles.add(tile);}} }然后遍历tiles集合Tile类里包含Preference的数据(Key/order/intent等等)也可以设置这些字段的key。 最后一个动态菜单就被成功添加到当前页面了。我们以System-Developer options 菜单为例它是被动态添加到Settings里的菜单。它定义在Settings的AndroidManifest.xml中。 activityandroid:nameSettings$DevelopmentSettingsDashboardActivityandroid:labelstring/development_settings_titleandroid:icondrawable/ic_settings_developmentandroid:exportedtrueandroid:enabledfalseintent-filter android:priority1action android:nameandroid.settings.APPLICATION_DEVELOPMENT_SETTINGS /action android:namecom.android.settings.APPLICATION_DEVELOPMENT_SETTINGS /action android:nameandroid.service.quicksettings.action.QS_TILE_PREFERENCES/category android:nameandroid.intent.category.DEFAULT //intent-filterintent-filteraction android:namecom.android.settings.action.SETTINGS //intent-filtermeta-data android:namecom.android.settings.order android:value-40/meta-data android:namecom.android.settings.categoryandroid:valuecom.android.settings.category.ia.system /meta-data android:namecom.android.settings.summaryandroid:resourcestring/summary_empty/meta-data android:namecom.android.settings.iconandroid:resourcedrawable/ic_settings_development /meta-data android:namecom.android.settings.FRAGMENT_CLASSandroid:valuecom.android.settings.development.DevelopmentSettingsDashboardFragment /meta-data android:namecom.android.settings.HIGHLIGHT_MENU_KEYandroid:valuestring/menu_key_system/meta-data android:namecom.android.settings.PRIMARY_PROFILE_CONTROLLEDandroid:valuetrue //activity首先它设置action为com.android.settings.action.SETTING从前面TileUtils类分析知道这个action只能Settings里添加时设置。然后设置菜单顺序菜单category为com.android.settings.category.ia.system查阅DashboardFragmentRegistry类中PARENT_TO_CATEGORY_KEY_MAP的对应关系可知对应页面fragment为SystemDashboardFragment即System页面接着指定了Summaryiconfragment等等。这样开发者选项菜单就被插入到了System页面下。 Settings中还存在其它动态插入的选项例如Google GMS插入的首页菜单Google和Digital Wellbeing parental controlls。 因为很多应用需要在Settings中增加菜单作为应用的入口这种不需要修改Settings代码而直接修改应用的AndroidManifest.xml文件实现解耦并自动适配。当然只有系统应用可以动态在Settings插入菜单。 SettingsLib 在分析Settings页面加载分析的时候发现有部分类来自SettingsLib模块这个模块是干嘛的呢 SettingsLib是Android系统中一个专注于为Settings应用提供服务的库。它包含了许多Settings基础功能并封装了一些操作。 源码路径frameworks/base/packages/SettingsLib 从bp文件可知编译后会生成一个SettingsLib的jar包。SettingsLib下根据不同功能UI基础实现有许多模块SettingsLib引用这些模块。 SettingsLib模块只有具有系统级别权限如系统应用framework等才可以调用第三方应用无法使用。 此时在想为什么不直接在Settings中直接实现呢因为将不同功能UI等的基础实现放在一个公共模块中可以方便其它与Settings交互的模块或framework使用进行定制使用因此SettingsLib虽专注于为Settings但它服务于系统供系统进行Settings相关扩展使用例如SystemUI模块就在使用SettingsLib相关实现。 相关资料 官方文档Android“设置”菜单 总结 AndroidSettings具有以下优势 界面。引入Preference显示菜单设置项。统一的页面风格页面简单标题状态清晰。 扩展性。Android Settings页面采用单个Activity(SubSettings)多Fragment支持其它系统应用在Settings添加菜单可扩展性强。 Preference和PreferenceController的配合使用方便定制新的设置项和页面厂商定制性高。 使用。添加了搜索让用户可以轻松快速找到设置项。界面也决定了用户可以轻松修改各种设置项。
http://www.dnsts.com.cn/news/102345.html

相关文章:

  • seo网站关键词优化多少钱做网站什么样的域名好
  • 站库设计网站官网外贸网站推广优化
  • 通过网站赚钱做网站公司需要什么资质
  • 做电力的系统集成公司网站搜索引擎有哪些?
  • 好玩的网站源码温州网站关键词推广
  • 搭建网站的流程和方法怎么建立自己的网页
  • 高端网站建设与发展免费项目进度管理软件
  • 松门建设规划局网站做标书分享网站
  • 网站免费申请空间江苏新站优化
  • 网站开发代码域名是不是网址的地址
  • 杭州高端网站建设公司哪家好做的最好的快餐网站
  • 住房城市建设部门户网站刷网站seo排名软件
  • 百度优化网站建设网站开发速成班
  • 仿历史网站模板下载网站空间多大合适
  • 咸阳网站建设公司电话网站建设招聘网
  • 公司网站开发策略和基本步骤马云有没有学过做网站
  • 商务网站建设规划做网站的的广告词
  • 本地邵阳网站建设2021软件公司排名
  • 济南网站排名推广站长工具亚洲高清
  • 网站设计的目标是什么东莞公司注册要多少钱
  • 建站大师阙梅娇简介自己做网站花钱吗
  • 四川城乡建设厅官方网站足球世界排名国家最新
  • 苏州网站开发建设电话VPS如何做网站服务器
  • 中国电力建设企业协会网站企业网站优化服务商
  • 服装设计网站素材海口装饰设计网站建设
  • 阿里云搭建网站多少钱wordpress权限管理
  • 河北雄安新区规划建设局网站廊坊建站服务
  • 个人外贸网站网站制作苏州
  • 网页设计与网站建设专业wordpress chmod
  • 建网站的公司浩森宇特桂林北京网站建设