Reputation: 224
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
Reputation: 9862
You can do it using data binding and BindingAdapter. I will show an example using Kotlin.
apply plugin: 'kotlin-kapt' // only need when You use Kotlin
...
android {
...
buildFeatures {
dataBinding = true
}
...
}
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
<?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.
<color name="color_stroke">#B71C1C</color>
<color name="color_back">#33691E</color>
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 + Enter
➡ Convert to data binding layout
*
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
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