According to the documentation
A Button which supports compatible features on older versions of the platform, including:
Allows dynamic tint of its background via the background tint methods in ViewCompat. Allows setting of the background tint using R.attr.backgroundTint and R.attr.backgroundTintMode. This will automatically be used when you use Button in your layouts and the top-level activity / dialog is provided by appcompat. You should only need to manually use this class when writing custom views.
Now, this makes me assume that the following two buttons would look exactly the same on high level devices.
<androidx.appcompat.widget.AppCompatButton
android:text="AppCompatButton"
android:id="@+id/appcompatbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:layout_below="@id/appcompatbutton"
android:id="@+id/button"
android:layout_width="wrap_content"
android:text="Button"
android:layout_height="wrap_content" />
However, here is how it actually looks:
I ran this on the following emulator: Galaxy Nexus, API:28 (720 x 1280 xhdpi)
And when I apply buttonStyle in my appTheme like this:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="buttonStyle">@style/Widget.MaterialComponents.Button</item>
</style>
It changes the AppCompatButton but not the normal button like this: (Note the slight difference in the rounded edges)
I also tried to create a custom button that both inherited from android.widget.Button and also androidx.appcompat.widget.AppCompatButton, both of these buttons show the same behaviour as using AppCompatButton in xml does. So it feels like the only outlier is Button in XML.
Question 1:
This all seems incredibly confusing according to me. Can someone clarify this as either a bug or feature?
EDIT 1
Doing debugging I found that the Button actually gets transformed into a MaterialButton, see the following:
Question 2:
Why is this transformation happening?
EDIT 2
Question 2 answer:
The transformation of Button to MaterialButton is due to the parent theme I was using.
Question 3:
How do you implement a custom button which works just like Button in xml would?
As a side note and personal opinion, also a slight repetition, this system is not only confusing but its hard to get it right and foolproof for future changes. In addition to this, the documentation is very poor. I would appreciate if an answer to this would be included as well, or at least a discussion regarding it, how to deal with it for example.
Short answers.
This all seems incredibly confusing according to me. Can someone clarify this as either a bug or feature?
They use different styles.
Why is this transformation happening?
There is an auto-inflation enabled which will replace <Button
with <com.google.android.material.button.MaterialButton
at runtime.
How do you implement a custom button which works just like Button in xml would?
You can customize the attributes in xml or the theme attributes.
Long answers.
The default style of MaterialButton
is Widget.MaterialComponents.Button
.
This style inherits from Widget.AppCompat.Button
but changes some attributes.
Here you can find the differences.
The main difference is here:
<item name="shapeAppearance">?attr/shapeAppearanceSmallComponent</item>
You can read more about shaping in the official doc. If you navigate through the style you will find:
<style name="ShapeAppearance.MaterialComponents.SmallComponent">
<item name="cornerSize">@dimen/mtrl_shape_corner_size_small_component</item>
</style>
where mtrl_shape_corner_size_small_component
= 4dp
.
It explains the slight difference in the rounded edges.
Also you are using
<item name="buttonStyle">@style/Widget.MaterialComponents.Button</item>
.
It doesn't work for the MaterialButton. You have to use:
<item name="materialButtonStyle">@style/Widget.MaterialComponents.Button</item>
The MaterialComponentsViewInflater
replaces some framework widgets with Material Components ones at inflation time, provided a Material Components theme is in use.
Something similar happens also with AppCompat (you can check that MaterialComponentsViewInflater extends AppCompatViewInflater
).
It means that, the <Button
is replaced <com.google.android.material.button.MaterialButton
at runtime, if you are using a Material Theme.
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
...
<item name="materialButtonStyle">@style/MyButtonStyle</item>
</style>
<style name="MyButtonStyle" parent="Widget.MaterialComponents.Button">
<item name="cornerRadius">xxx</item>
</style>
or
<style name="MyButtonStyle" parent="Widget.MaterialComponents.Button">
<item name="shapeAppearanceOverlay">@style/SShapeAppearanceOverlay.MyApp.Button.Rounded</item>
</style>
<style name="ShapeAppearanceOverlay.MyApp.Button.Rounded" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">xxdp</item>
</style>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With