Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to set TextView text color using data binding library

android:textColor="@{holder.getTitleColor(context, item)}"

where the method in Holder class is defined like below

public int getTitleColor(Context context, Item item) {

No matter if I return color int (@ColorInt) or color resource (@ColorRes) it paints the text solid white. What am I doing wrong?

I seems the int you are providing is interpreted as a hex color, even though it seem intuitive that this setter should be expecting a resource ID.

use the Context reference generated for each bindable view, and use it to convert the resource ID to the color you are pointing to, as described in the DataBinding Dev Guide:

A special variable named context is generated for use in binding expressions as needed. The value for context is the Context from the root View's getContext().

use it to set color like this:

 <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.text}"
            android:textColor="@{context.getColor(data.colorRes)}"

for backwards compatibility you can use ContextCompat. Import needed:

<layout>
        <import type="android.support.v4.content.ContextCompat"/>
        <variable name="data" type="com.whatever.myapp.MyModel"/>
    </data>
     <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.text}"
            android:textColor="@{ContextCompat.getColor(context, data.colorRes)}"
</layout>
                If you try to use this solution in a device with API level lower than 23, you get an error indicating "java.lang.NoSuchMethodError". For those who need a solution for previous versions, use this: android:textColor="@{context.getResources().getColor(data.colorRes)}"
– Fer
                May 29, 2017 at 9:43
                where does this "data" object come from? are you not supposed to set it to R.color.my_colour for example?
– Jono
                Jun 10, 2020 at 14:12
                The use of context as shown in the answer also implies theme-awareness. E.g. if you use ViewModels you might be tempted to get the color from getApplication() but that will always result in default theme colors.
– l33t
                Jun 22, 2021 at 23:52

In addition to the solution of @Mardann, here is an updated solution that also works on API lower than 23 by using ContextCompat.getColor():

<layout>
        <import type="androidx.core.content.ContextCompat" />
        <variable
            name="data"
            type="com.example.myapp.MyDataObject" />
    </data>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{data.text}"
        android:textColor="@{ContextCompat.getColor(context, data.colorRes)}"/>
    </layout>
  • Make sure to import ContextCompat as shown above.
  • You can automagically 'context' as method parameter for ContextCompat.getColor() because it will be automatically resolved to the view's context.
  • create method by using BindingAdapter

    @BindingAdapter({"bind:color"})
    public static void setColor(TextView textView, Item item) {
        textView.setTextColor(<set color of your choice>);
    

    and to call it from xml

    app:color="@{item}"
                    Any explanation why my method doesn't work? It returns the int that's needed by android:textColor, I've no idea what's wrong with the code in the question.
    – tomrozb
                    Oct 7, 2016 at 18:59
                    put some log in your method and check whether its going there or not, because with databinding i dont think so you can call method like this and that also with return value
    – Ravi
                    Oct 8, 2016 at 9:20
    

    In my case, the color value was in a String Format(eg. "#000000")

    1.String TxtColor = "#000000"

    2.Import "android.graphics.Color"

    <layout>
          <import type="android.graphics.Color"/>
          <variable name="txtColor" type="String"/>
        </data>
         .... other views
    </layout>
    

    3.Set to Desired View -- In my case it was TextView

        ........ other views
      <android.support.v7.widget.AppCompatTextView
            android:id="@+id/tvTitle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textcolor= "@{Color.parseColor(txtColor)}" //when we import android.graphics.Color we can access it's all methods present
            tools:text="Test"/>
           ...... other views
    

    4.Binding from Activity/Adapter --in my case it was Adapter

    inner class ViewHolder(private val binding: BindingClass) :
        RecyclerView.ViewHolder(binding.root) {
        fun setData(data: DataClass, TxtColor : String?) {
            binding.txtColor= TxtColor 
            binding.executePendingBindings()
    
  • Add your binding adapter (place this outside all your classes)

    @BindingAdapter("app:full_text", "app:span_text", "app:span_color")
    fun formatText(textView: TextView, full_text: String, span_text: String, span_color: Int) {
        val firstMatchingIndex = full_text.indexOf(span_text)
        val lastMatchingIndex = firstMatchingIndex + span_text.length
        val spannable = SpannableString(full_text)
        spannable.setSpan(ForegroundColorSpan(span_color), firstMatchingIndex, lastMatchingIndex, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
        textView.text = spannable
    
  • Setup string resources with variables

    <string name="percentage">%1$d\%%</string>
    <string name="booking_fee">Require card and collect %1$s at Booking</string>
    
  • Convert value to string in your ViewHolder (if you need to)

    fun bind(percentage: Int) {
        binding.percentage = context.resources.getString(R.string.percentage, percentage)
        binding.executePendingBindings()
    
  • Apply bindings via your xml layout

    <variable name="percentage" type="String" /> </data> <TextView app:full_text="@{@string/booking_fee(percentage)}" app:span_color="@{@color/color_primary}" app:span_text="@{percentage}" />

    Create an extension function for textview.

    @BindingAdapter("android:colorId")
    fun TextView.setTextColorValue(@ColorRes colorId: Int) {
        if (colorId == 0) return
        setTextColor(ContextCompat.getColor(context, colorId))
    

    use it in xml like this

    <TextView
                android:id="@+id/deliveryStatus"
                android:colorId="@{model.deliveryStatusColor}" />
    

    You can also use binding adapter and then use color resource.

    Define this method anywhere in project:

    @BindingAdapter(value = "text_color") //customise your name here 
    public static void setTextColor(TextView view, int color) {
        view.setTextColor(color);
    

    And then in XML use your new attribute:

    <TextView
        app:text_color="@{@color/colorPrimary}"/>
    

    Create Binding Adapter as follows, here I am passing all the strings to be colored inside {}. Replace the {blah} string with colored blah string in span.

    @BindingAdapter( "spanColor")
    fun formatText(view:TextView, hexColorValue:Int) {
        val text = view.text
        val span = SpannableStringBuilder(text)
        var i = 0
        var diff = 0
        while (i < text.length) {
            val firstIndex = text.indexOf('{', i) - diff
            val secondIndex = text.indexOf('}', i) - diff
            if (firstIndex < 0 || secondIndex < 0) break
            span.delete(firstIndex, firstIndex + 1)
            span.delete(secondIndex - 1, secondIndex)
            span.setSpan(ForegroundColorSpan(hexColorValue), firstIndex, secondIndex-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
            i = secondIndex + diff + 1
            diff += 2
        view.text = span
    

    In your XMl file use the attribute (app:spanColor="@{@color/colorAccent}") as

     <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:layout_marginTop="@dimen/space_xlarge"
                    style="@style/DefaultSmallText"
                    app:spanColor="@{@color/colorAccent}"
                    android:text="@string/create_credential_message"/>
    

    string.xml

    <string name="create_credential_message"><![CDATA[{Username} must at least contain 8 alphanumeric characters or an email address. {Password} must be 8-20 characters long, contain uppercase, lowercase, number, & special characters.]]></string>
                    note:-> android:textColor="@{ContextCompat.getColor(context, data.colorRes)}" gives exception following exception in some case :  Fatal Exception: android.content.res.Resources$NotFoundException Resource ID #0x0     So i have to do workarround .And above solution is working
    – sankyrahul
                    Apr 16, 2020 at 5:18
                    This is because binding gives default value 0 for integers when initialising. you can return in this case to avoid that exception. Look at my answer.
    – SajithK
                    May 22, 2021 at 7:21
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

  •