Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Androidx AppCompatButton looks different from Button even on high API Level device

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.

like image 941
Ludvig W Avatar asked Jun 21 '19 11:06

Ludvig W


1 Answers

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 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>
like image 97
Gabriele Mariotti Avatar answered Nov 04 '22 00:11

Gabriele Mariotti