9

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:

enter image description here

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)

enter image description here

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: enter image description here

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.

4
  • i usually just set a background drawable to my buttons to ensure they always look the same, i usually do the drawable via xml code though, there you can set the colors you want and the rounded edges Commented Jun 21, 2019 at 17:01
  • I have been complaining to google about the Theme/Style/Attributes engine since late 2012. The answer has been complete silence until around 2017 when they said: "yeah... we gotta focus on discoverability" in a reddit AMA. Then they released all the Material Design libraries, but the Android one was so incomplete (still is) that it was kind of a joke. Fast forwards to 2019... they kept the whole thing (Material 2.0), it's still incomplete, but now Android Studio (3.5 maybe?) should help you find "where your stuff comes from". So...yes, the system is awful. Commented Jun 21, 2019 at 17:06
  • @MartinMarconcini Thanks for that answer, it doesn't solve the problem at hand but it does provide me, and others, with the comfort that they are not alone with the issue. Commented Jun 23, 2019 at 13:11
  • @LudvigW Good question! Maybe it is late to answer. I've just tried to explain all points, with examples and links to the current code. And yes, it is not simple to get all the info about components and styles. No problem to discuss each point if is not clear. Commented Sep 3, 2019 at 23:49

2 Answers 2

4

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.

  1. They use different styles.

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 ?attr/shapeAppearanceSmallComponent" rel="nofollow noreferrer">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> 
  1. The auto-inflation is here.

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.

  1. There are different options. One of these is to define a custom style for buttons.
<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> 
Sign up to request clarification or add additional context in comments.

Comments

0

I changed the <Button> to <ImageButton>

Quick and short way.

Don't forget to check & change references in java/kotlin files. Compiler will alert you any way.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.