Generally follow the recommendations of the documentation and the probability of problems is very low. But many people are different, and accidents happen every time, such as the pit caused by not using the AppCompat theme this time!
The AppCompat framework is very important as the cornerstone of the Jetpack collection. Any default project created on Android Studio automatically integrates the AppCompat framework and uses its provided AppCompatActivity as an Activity Base.
The theme configured for the Activity on the App side generally extends from the system theme provided by the SDK or the theme provided by AppCompat, which may cause some exceptions to the AppCompat framework.
A non-AppCompat topic threw an exception
If you configure a theme that extends from the SDK, the Activity cannot start and the following exception occurs:
RuntimeException: Unable to start activity xxx: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
The reason is simply that much of the subsequent processing of the AppCompat framework is closely related to the properties configured for this topic. Therefore, the AppCompat theme is strictly checked before loading the screen, otherwise an exception will be thrown.
class AppCompatDelegateImpl.{
private ViewGroup createSubDecor(a) {
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."); }...returnsubDecor; }}Copy the code
How to solve this problem?
-
The theme was changed to extend from the AppCompat theme. However, if your theme is overwritten in many places, this can take a long time, and many of the custom attributes may conflict with the AppCompat theme and need to be analyzed and fine-tuned individually
-
Activity switch to the SDK version, android.app.activity. But as the Jetpack framework matures and becomes more popular, there are many important frameworks that rely heavily on AppCompatActivity support, such as Lifecycle framework, ViewModel framework, Preference, etc. This can cause problems with other framework features, which are not good either
-
Where there are compatibility problems in AppCompat, there are solutions to avoid. For example, the above exception is to check whether the windowActionBar property provided by the AppCompat framework is configured, so we can add a reference to the property in our theme. The downside is that many UI presentation problems that are not exceptions can easily be ignored if they are not found. In other words, this plan is prone to incomplete changes and omissions
There is nothing to say about the first two schemes. Let’s analyze how to operate the third scheme in detail.
How do I use a non-AppCompat theme
The windowActionBar property can be configured in additional themes that extend from the SDK, true or false depending on the need.
<style name="Theme.MaterialExtension" parent="android:Theme.Material.Light">.</style>
<style name="Theme.MaterialExtension.Customize">
<item name="windowActionBar">true</item>
</style>
Copy the code
After successfully starting the Activity screen:
Wait, where is the CheckBox for the CheckBox Settings entry?
I looked at the source code for CheckBoxPreference and found no special processing.
Check the Layout Inspector for a clue: An instance of the CheckBox exists in the view, but Width is 0. And the CheckBox implementation class name becomes AppCompatCheckBox.
It occurs to me that the AppCompat framework extends most of the SDK’s controls with the AppCompat prefix of the same name in order to enable newer systems to use new features such as Auto Size and Background Tint. So presumably, AppCompatCheckBox depends on compatibility properties that are not configured in our topic.
How to be compatible with AppCompat controls
Looking at the source code for the AppCompatCheckBox control, we can see that the constructor has a specific implementation for check buttons.
public AppCompatCheckBox(...). {... mCompoundButtonHelper =new AppCompatCompoundButtonHelper(this);
mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
}
Copy the code
Concrete is to load by AppCompatCompoundButtonHelper buttonCompat attributes configuration check button image.
void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
TintTypedArray a =
TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
R.styleable.CompoundButton, defStyleAttr, 0);
ViewCompat.saveAttributeDataForStyleable(mView, mView.getContext(),
R.styleable.CompoundButton, attrs, a.getWrappedTypeArray(), defStyleAttr, 0);
try {
boolean buttonDrawableLoaded = false;
if (a.hasValue(R.styleable.CompoundButton_buttonCompat)) {
final int resourceId = a.getResourceId(R.styleable.CompoundButton_buttonCompat, 0);
if(resourceId ! =0) {
try {
mView.setButtonDrawable(
AppCompatResources.getDrawable(mView.getContext(), resourceId));
buttonDrawableLoaded = true; }... }}... }finally{ a.recycle(); }}Copy the code
Obviously, this property is not configured in our theme, so the CheckBox does not show up.
We could have configured this property directly in our theme, but it would have been nice to do the same as the AppCompat framework, without providing the checkbox resource.
<declare-styleable name="CompoundButton">
<attr name="android:button"/>
<! -- Compat attr to load backported drawable types -->
<attr format="reference" name="buttonCompat"/>.</>
Copy the code
The AppCompat theme uses buttonCompat’s Attr in the CheckBox control’s Style.
<style name="Base.Widget.AppCompat.CompoundButton.CheckBox" parent="android:Widget.CompoundButton.CheckBox">
<item name="android:button">? android:attr/listChoiceIndicatorMultiple</item>
<item name="buttonCompat">? attr/listChoiceIndicatorMultipleAnimated</item>
<item name="android:background">? attr/controlBackground</item>
</style>
<style name="Widget.AppCompat.CompoundButton.CheckBox" parent="Base.Widget.AppCompat.CompoundButton.CheckBox"/>
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
<item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
</style>
Copy the code
In this case, apply the same Style to our theme.
<style name="Theme.MaterialExtension.Customize">.<item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
</style>
Copy the code
Faster compatible methods
If discover which control to have a problem, be like above method same check words really spend time. In fact, go directly to the AppCompat theme implementation to search for the control related compatibility Style, copy it.
For example, step through the implementation of the AppCompat theme and search for the Style of the CheckBox keyword.
<style name="Theme.AppCompat.DayNight.DarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar"/>
<style name="Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light.DarkActionBar"/>
<style name="Base.Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light">
<style name="Base.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">.<item name="editTextStyle">@style/Widget.AppCompat.EditText</item> <item name="editTextBackground">@drawable/abc_edit_text_material</item> <item name="editTextColor">? android:attr/textColorPrimary</item>.<! -- CheckBox compatibility Style hidden in here --> <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item> </style>
Copy the code
Pay attention to
In addition to appcompatactivities, the AppCompatDialog provided by the AppCompat framework also has limitations on topics that need to be noted. The compatability of an AppCompatDialog inner view can be processed in the same way as the Activity’s AppCompat control.
conclusion
There are three solutions to the problem of using non-AppCompat topics:
-
The theme was changed to extend from the AppCompat theme
-
Activity switch to the SDK version of Android.app.activity
-
Manually resolve AppCompat compatibility issues
There is no doubt that the limitations of the AppCompat framework on themes are due to the subsequent UI logic being tied to them, so the best solution must be option 1, which provides AppCompat themes.
- If you really don’t need it
Jetpack
Synergy with other frameworks (although this is highly unlikely), then option 2 can be chosen - If your existing theme is too large, you won’t be able to switch to the AppCompat theme for a while, and the impact will be small and uncomplicated. Then try Plan 3 to solve them one by one
Recommended reading
AppCompat has been out for two years and you still don’t know?
SplashScreen: a new SplashScreen for Jetpack
Compose perfect Copy of Flappy Bird!