Anupam Somani
Anupam Somani

Reputation: 224

How to pass parameters in Android drawable

How to pass arguments in the android drawable.

How to acheive the below mentioned code

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="{colorcode}" />
            <stroke android:width="1sp" android:color="{colorcode}" />
            <corners android:radius="15dp" />
        </shape>
    </item>
</selector>

and then from XML file take this drawable passing values for parameters. Something like

<TextView
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_marginRight="5dp"
    android:layout_weight="1"
    android:background="@drawable/rounded,{colorCode}"
    android:gravity="center"/>

Upvotes: 4

Views: 2868

Answers (2)

iknow
iknow

Reputation: 9862

You can do it using data binding and BindingAdapter. I will show an example using Kotlin.

  1. To enable data binding add to Your Gradle:
apply plugin: 'kotlin-kapt' // only need when You use Kotlin

...
android {  
    ...
    buildFeatures {
        dataBinding = true
    }
    ...
}
  1. Crete BindingAdapter (I am adding it to MainActivity.kt to simplify code):
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.DataBindingUtil
import com.myniprojects.teststackjava.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity()
{
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)
    }
}

// Here is binding adapter. It takes 2 colors, background and stroke. If You want more customization add more parameters 
@BindingAdapter(value = ["colorStoke", "colorBack"], requireAll = true)
fun setBackground(textView: TextView, @ColorRes colorStoke: Int, @ColorRes colorBack: Int)
{
    val stroke = ContextCompat.getColor(textView.context, colorStoke)
    val back = ContextCompat.getColor(textView.context, colorBack)

    val gd = GradientDrawable()

    // setting background
    gd.colors = intArrayOf(
            back,
            back
    )

    // here change parameters as You want
    gd.gradientType = GradientDrawable.LINEAR_GRADIENT

    gd.shape = GradientDrawable.RECTANGLE
    gd.cornerRadius = 15f;
    
    // setting stroke width and color 
    gd.setStroke(5, stroke)

    textView.background = gd
}

To customize GradeintDrawable check this docs

  1. In Your MainActivity.xml do this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="com.myniprojects.teststackjava.R" />
    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/txtVTest"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:gravity="center"
            android:text="TextView Text"
            android:textSize="40sp"
            app:colorBack="@{R.color.color_back}"
            app:colorStoke="@{R.color.color_stroke}"/>

    </FrameLayout>
</layout>

I am importing R to be able to pass resources. (Maybe there is another way without import but I couldn't pass color like @{@color/name} so I just import it). In Your TextView You have to call app:colorBack and app:colorStoke with colors that You want to set as stroke and back.

  1. Of course in res/values/colors:
<color name="color_stroke">#B71C1C</color>
<color name="color_back">#33691E</color>
  1. And final effect to show that everything works:

enter image description here


To generate ActivityMainBinding You first have to create a data binding layout. If You added Your dependency correctly to convert fast Your layout to data binding layout use: Alt + EnterConvert to data binding layout*

enter image description here

So Your layout should look like this:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    </FrameLayout>
</layout>

And now after rebuilding Your project You should get ActivityMainBinding class.

Upvotes: 4

SaadAAkash
SaadAAkash

Reputation: 3193

ShapeDrawable, GradientDrawable & ColorDrawable are direct subclasses of Drawable. You can explicitly cast your drawable to any of the aforementioned subclasses to invoke methods/property access like setColor(int), .paint like the following:

val drawable: Drawable = (yourDrawable as StateListDrawable).getStateDrawable(0)
when (drawable) {
    is ShapeDrawable -> {
        (drawable as ShapeDrawable).paint.color = ContextCompat.getColor(mContext, R.color.colorToSet)
    }
    is GradientDrawable -> {
        (drawable as GradientDrawable).setColor(ContextCompat.getColor(mContext, R.color.colorToSet))
        (drawable as GradientDrawable).cornerRadius = 2.0F
    }
    is ColorDrawable -> {
        (drawable as ColorDrawable).color = ContextCompat.getColor(mContext, R.color.colorToSet)
    }
}

Points to be noted:

You can use mutate() on this drawable because it returns the same instance of the drawable with the copied constant drawable state. The mutate() method doesn't share its state with any other drawable so it's pretty helpful if you want to keep the drawable as it is & then use it with different states in many places. Here's a great answer to how to use mutate on drawable & save states.

Additionally, as you mentioned in the comments, to change drawable radius too, use cornerRadii which as per the doc in the given embedded link, returns the radii for each of the 4 corners. For each corner, the array contains 2 values, [X_radius, Y_radius]. The corners are ordered top-left, top-right, bottom-right, bottom-left.

Upvotes: 1

Related Questions