AppCompat发布两年了,还没了解?
近日随笔近期疫情日渐严峻,大家多多保重,出门记得戴口罩。希望河北,黑龙江能尽早控制住局面迎来拐点,全国人民开开心心过个好年。
AppCompatActivity
AppCompatActivity extends FragmentActivity extends ComponentActivity extends ComponentActivity extends Activity
FragmentActivity
采用FragmentController类对AndroidX的Fragment新组件提供支撑,比如提供了咱们常用的getSupportFragmentManager() API。androidx.activity.ComponentActivity
实现了ViewModel接口,和Lifecycle框架进行配合以支撑ViewModel框架的运行。androidx.core.app.ComponentActivity
实现了Lifecycle接口并通过ReportFragment支撑Lifecycle框架的运行。
~AppCompatDelegate~
ensureSubDecor() 确保ActionBar的特有UI结构创建完毕 removeAllViews() 确保ContentView的所有Child全部被移除干净 inflate() 将画面的内容布局解析并添加到ContentView下
ensureWindow()
获取Activity所属的Window引用并添加window相关回调getDecorView()
告知Window去创建DecorView,这里要提一下PhoneWindow的generateLayout(),其将依据主题的创建不同的布局结构。比如AppCompatActivity的话将解析screen_simple.xml得到DecorView的基本结构,其包括根布局LinearLayout,用来映射actionmode布局的viewstub以及承载App内容的id为ContentView inflate()
获取ActionBar的布局,主要是abc_screen_toolbar.xml和abc_screen_content_include.xml两个文件removeViewAt() & addView()
将ContentView的子View迁移至ActionBar布局下。具体方法是将其所有child移除并add到ActionBar布局下id为action_bar_activity_content的ViewGroup下面, 并将原有ContentView的id置空,同时将该目标ViewGroup的id设置为Content。 意味着它将成为AppCompatActivity画面承载内容区域的父布局
~公开的API~
getSupportActionBar() 用以获取AppCompat特有的ActionBar组件供开发者定制ActionBar getDelegate() 获取AppCompatActivity内部实现的大管家AppCompatDelegate的实例(实际上将通过静态的create()获取实现类AppCompatDelegateImpl的实例) getDrawerToggleDelegate() 获取抽屉导航布局DrawerLayout的代理类ActionBarDrawableToggleImpl的实例,用来和ActionBar进行UI的交互 onNightModeChanged() 不同于配置了uiMode的外部配置变更后才能收到主题变化的通知。 本API可以在暗黑主题的适配模式(比如跟随系统设置模式和跟随电量设置模式等)发生变化后得到回调,可利用这个时机做些补充处理
~使用上的注意~
You can add an ActionBar to your activity when running on API level 7 or higher by extending this class for your activity and setting the activity theme to Theme.AppCompat or a similar theme.
You need to use a Theme.AppCompat theme (or descendant) with this activity.
// AppCompatThemeImpl.javaprivate ViewGroup createSubDecor() { TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme); if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) { a.recycle(); throw new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity."); } ...}AppCompatDialog
AppCompatTheme
Theme.AppCompat
继承自Base.V7.Theme.AppCompat主题,指定AppCompatViewInflater为widget等class的解析类,并设置AppCompatTheme所定义的基本属性,其顶级主题仍旧是老牌的主题Theme.HoloTheme.AppCompat.DayNight
能够自动适配暗黑主题。其继承自Base.V7.Theme.AppCompat.Light,与Theme.AppCompat的区别主要在于其默认情况下采用了light系的主题。比如colorPrimary采用primary_material_light,而Theme.AppCompat则采用primary_material_dark颜色
Dark Theme / 暗黑模式
private boolean applyDayNight(final boolean allowRecreation) { ... @NightMode final int nightMode = calculateNightMode(); @ApplyableNightMode final int modeToApply = mapNightMode(nightMode); final boolean applied = updateForNightMode(modeToApply, allowRecreation); ... if (nightMode == MODE_NIGHT_AUTO_BATTERY) { // 注册监听省电模式的广播接收器 getAutoBatteryNightModeManager().setup(); } ...}
abstract class AutoNightModeManager { ... void setup() { ... if (mReceiver == null) { mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 省电模式变化后的回调 onChange(); } }; } mContext.registerReceiver(mReceiver, filter); } ...}
private class AutoBatteryNightModeManager extends AutoNightModeManager { ... @Override public void onChange() { // 省电模式变化后回调主题切换方法更新主题 applyDayNight(); }
@Override IntentFilter createIntentFilterForBroadcastReceiver() { if (Build.VERSION.SDK_INT >= 21) { IntentFilter filter = new IntentFilter(); filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); return filter; } return null; }}private boolean updateForNightMode(final int mode, final boolean allowRecreation) { ... // 如果Activity的BaseContext尚未初始化则直接适配新的主题值 if ((sAlwaysOverrideConfiguration || newNightMode != applicationNightMode) && !mBaseContextAttached...) { ... try { ... ((android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf); handled = true; ... } }
final int currentNightMode = mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// 如果Activity的BaseContext已经创建, // 且App没有声明要处理暗黑主题变化的话,将重绘Activity if (!handled ...) { ActivityCompat.recreate((Activity) mHost); handled = true; }
// 假使App声明了处理暗黑主题变化的话, // 那么将新的主题值更新到Configuration的uiMode属性 // 并回调Activity#onConfigurationChanged(),等待App的自行处理 if (!handled && currentNightMode != newNightMode) { ... updateResourcesConfigurationForNightMode(newNightMode, activityHandlingUiMode); handled = true; }
// 最后检查是否要通知App暗黑主题模式发生变化 // (注意这里指的是App设置的暗黑主题切换的策略发生变更, // 比如由跟随系统设置变更为固定暗黑模式等) if (handled && mHost instanceof AppCompatActivity) { ((AppCompatActivity) mHost).onNightModeChanged(mode); } ...}AppCompatViewInflater
final View createView(View parent, final String name, @NonNull Context context...) { ... switch (name) { case "TextView": view = createTextView(context, attrs); verifyNotNull(view, name); break; case "ImageView": view = createImageView(context, attrs); verifyNotNull(view, name); break; ... } ... return view;}
protected AppCompatTextView createTextView(Context context, AttributeSet attrs) { return new AppCompatTextView(context, attrs);}AppCompatTextView
~Dynamic Tint~
// ColorStateListInflaterCompat.javaprivate static ColorStateList inflate(Resources r, XmlPullParser parser) { ... while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { ... final int color = modulateColorAlpha(baseColor, alphaMod); colorList = GrowingArrayUtils.append(colorList, listSize, color); stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec); listSize++; } ... return new ColorStateList(stateSpecs, colors);}// ResourceManagerInternal.javastatic void tintDrawable(Drawable drawable, TintInfo tint, int[] state) { ... if (tint.mHasTintList || tint.mHasTintMode) { drawable.setColorFilter(createTintFilter( tint.mHasTintList ? tint.mTintList : null, tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE, state)); } else { drawable.clearColorFilter(); } ...}// AppCompatTextViewAutoSizeHelper.javavoid loadFromAttributes(AttributeSet attrs, int defStyleAttr) { ... if (a.hasValue(R.styleable.AppCompatTextView_autoSizeTextType)) { mAutoSizeTextType = a.getInt(R.styleable.AppCompatTextView_autoSizeTextType, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE); } ... if (supportsAutoSizeText()) { if (mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) { ... setupAutoSizeText(); } ... }}
private boolean setupAutoSizeText() { if (supportsAutoSizeText() && mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) { ... if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) { ... for (int i = 0; i < autoSizeValuesLength; i++) { autoSizeTextSizesInPx[i] = Math.round( mAutoSizeMinTextSizeInPx + (i * mAutoSizeStepGranularityInPx)); } mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx); } mNeedsAutoSizeText = true; } ...}// AppCompatTextView.javaprotected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { ... if (mTextHelper != null && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) { mTextHelper.autoSizeText(); }}
// AppCompatTextHelper.javavoid autoSizeText() { mAutoSizeTextHelper.autoSizeText();}
// AppCompatTextViewAutoSizeHelper.javavoid autoSizeText() { ... if (mNeedsAutoSizeText) { ... synchronized (TEMP_RECTF) { ... // 计算最佳size final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF); // 如果和预设的size不一致的话更新size if (optimalTextSize != mTextView.getTextSize()) { setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize); } } } ...}AppCompatImageView
辅助类
AppCompatBackgroundHelper AppCompatDrawableManager AppCompatTextHelper AppCompatTextViewAutoSizeHelper AppCompatTextClassifierHelper AppCompatResources AppCompatImageHelper
…