rupesh
rupesh

Reputation: 2891

Define style and custom attribute in View instead of xml

I want to implement custom style for view. Let's take an example of button. Some common values will be shared across the app for that I am using style like below:

<style name="Widget.Demo.Button.Primary" parent="@style/Widget.MaterialComponents.Button">
        <item name="fontFamily">@font/roboto</item>
        <item name="android:fontFamily">@font/roboto</item>
        <item name="android:minHeight">64dp</item>
        <item name="android:theme">@style/ThemeOverlay.Demo.GrayPrimary</item>
</style>

<style name="ThemeOverlay.Demo.GrayPrimary" parent="">
    <item name="colorPrimary">@color/gray</item>
</style>

Now I want to add some of the custom attribute in the button like below (This is just an example not the real attribute):

     <com.android.CustomButton
        android:id="@+id/btn_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/first"
        app:abc="primary"
        style="@style/Widget.Demo.Button.Primary"/>

attrs.xml:

<declare-styleable name="CustomButtonAttr" >
        <attr name="abc" format="enum" >
            <enum name="primary" value="1"/>
            <enum name="secondary" value="2"/>
    </declare-styleable>

I don't want to define style and custom attribute in xml everytime. Is there any way to get custom attribute directly in my customview and Widget.Demo.Button.Primary set by default in my below class?

class CustomButton : MaterialButton {
    constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(
        ThemeEnforcement.createThemedContext(context, attributeSet, 0, 0),
        attributeSet,
       0
    )
}

I don't want to define style in xml for every button or view which I create. So is there a way to define in CustomButton class along with custom attributes ? If yes could you please give me some references.

Thanks in advance.

Upvotes: 0

Views: 382

Answers (1)

ataulm
ataulm

Reputation: 15334

Yep, you can use the default style attribute in the constructor.

Define a new attribute:

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

Then use the appropriate view constructor:

private val defStyleAttr = R.attr.customButtonStyle

class CustomButton(context: Context, attrs: AttributeSet) : MaterialButton(
    ThemeEnforcement.createThemedContext(context, attributeSet, defStyleAttr, 0),
    attributeSet,
    defStyleAttr
) {

    init {
        val typedArray = context.obtainStyledAttributes(
            attrs,
            R.styleable.CustomButton, 
            defStyleAttr,
            0
        )
        ...
    
}

In your theme, point customButtonStyle to the style resource that you want used by default:

<style name="Theme.Demo" parent="Base.Theme.Demo">
    <item name="customButtonStyle">@style/Widget.Demo.Button.Primary</item>
</style>

Note that android:theme should be changed to materialThemeOverlay in that style resource, as it won't be applied when read from a default style. As you're already wrapping the context with the ThemeEnforcement function (newer versions of Material Design Components change this to MaterialThemeOverlay), this custom view supports materialThemeOverlay 👍


You can add your custom attributes to the style too:

<style name="Widget.Demo.Button.Primary" parent="@style/Widget.MaterialComponents.Button">
    <item name="abc">primary</item>
    <item name="fontFamily">@font/roboto</item>
    <item name="android:fontFamily">@font/roboto</item>
    <item name="android:minHeight">64dp</item>
    <item name="materialThemeOverlay">@style/ThemeOverlay.Demo.GrayPrimary</item>
</style>

Reference (blog + video)

Upvotes: 1

Related Questions