RomanKovalev
RomanKovalev

Reputation: 859

Style in custom view not affected on super constructor call

I'm trying to create custom view and set its style programatically. I created custom view based on AppCompatButton:

public class RangeSelectorButton extends androidx.appcompat.widget.AppCompatButton {
    public int onClickKey = -1;

    public RangeSelectorButton(Context context) {
        this(context, null);
    }

    public RangeSelectorButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public RangeSelectorButton(Context context, AttributeSet attrs) {
        this(context, attrs, R.style.RangeSelectorButton);
    }
}

and now I get stuck in strange behaviour:

<ru.SomeDomain.CustomViews.RangeSelector
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clickable="true" android:focusable="true">

                <!-- In this case all works fine -->
                <ru.SomeDomain.RangeSelectorButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:minHeight="10dp"
                    android:minWidth="40dp"
                    style="@style/RangeSelectorButton"
                    />

                <!-- Style not applies -->
                <ru.SomeDomain.CustomViews.RangeSelectorButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:minHeight="10dp"
                    android:minWidth="40dp"
                    />
</ru.SomeDomain.CustomViews.RangeSelector>

What I should to do if I don't want use style attribute in xml every time when I create my custom view?

If it is necessary my style.xml contains:

<style name="RangeSelectorButton" parent="@android:style/Widget.Button">
    <item name="android:background">@drawable/range_toggle_button_selector</item>
</style>

range_toggle_button_selector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/range_unselected" android:state_pressed="false" />
    <item android:drawable="@drawable/range_selected" android:state_pressed="true" />
</selector>

Upvotes: 1

Views: 312

Answers (1)

Nicolas
Nicolas

Reputation: 7121

The problem here is that you're passing a style in the place of the defStyleAttr parameter, which expects an attribute, not a style. There's two solutions here:

  1. Use an attribute. In attrs.xml declare:

    <attr name="rangeSelectorButtonStyle" format="reference"/>
    

    and in your app theme:

    <item name="rangeSelectorButtonStyle">@style/RangeSelectorButton</style>
    

    and change your constructor to:

    public RangeSelectorButton(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.rangeSelectorButtonStyle);
    }
    

    If you're making a library for example, this is the best way of doing it since it allows users to customize your widget style.

  2. If you don't want to use an attribute, you can call the fourth View constructor, which has a parameter that takes a default style and not an attribute:

    View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    

    Add this constructor to your view and in the second constructor, call the fourth like this:

    public RangeSelectorButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0, R.style.RangeSelectorButton);
    }
    

    Note that the fourth constructor requires API 21.

Upvotes: 4

Related Questions