6

I'm trying to apply some custom font to my TextView with one line as described in a post by Lisa Wray. The TextView is part of an item that goes into a RecyclerView

I've added data binding dependency to my top level build file.

classpath 'com.android.tools.build:gradle:1.3.0'
classpath "com.android.databinding:dataBinder:1.0-rc1"

I have also applied the plugin to my main module:

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'

Here is the item.xml file that will be added to the RecyclerView.

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

    <data></data>

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="100dp"
        android:layout_height="130dp"
        android:layout_gravity="center"
        card_view:cardCornerRadius="2dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/image"
                android:layout_width="match_parent"
                android:layout_height="100dp"/>

            <TextView
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:font="@{@string/font_yekan}"/>

        </LinearLayout>
    </android.support.v7.widget.CardView>
</layout>

I've added a layout root element and app:font="@{@string/font_yekan}" combined with a static setter method:

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName) {
    textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

should do the trick. But when I run the program, the font isn't changed. However, when I remove the above static method, I get the following error:

Cannot find the setter for attribute 'app:font' with parameter type java.lang.String.

So data binding framework has recognized the binding stuff, but the setter method doesn't get called (Logs don't print output).

What is the problem here?

Ashkan Sarlak
  • 7,124
  • 6
  • 39
  • 51

2 Answers2

6

Provided the above layout and setup, assuming following:

Inside your RecyclerView adapter you have bound the view by one of these ways:

  1. In onCreateViewHolder method of your adapter class

    @Override
    public MyAdapter.MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.recycler_item,
                parent, false);
        return new MyHolder(binding.getRoot());
    }
    
  2. Or in its onBindViewHolder method

        @Override
        public void onBindViewHolder(MyAdapter.MyHolder holder, int position) {
            DataBindingUtil.bind(holder.itemView);
            //...
        }
    

Following resource setup

Your assets folder should look similar to this:

assets folder

Your string resource file should have full qualified name for font:

<string name="kenyan">kenyan_rg.ttf</string>

With this ensured, it should work (and it does for me)

PLNech
  • 3,087
  • 1
  • 23
  • 52
subhash
  • 2,689
  • 18
  • 13
  • Very awesome answer man! So in short, I have to inflate the view using the binding framework, or tell it explicitly that the view is using the binding framework (second method). – Ashkan Sarlak Sep 15 '15 at 06:53
  • Yes, thats it. And even better if you did something like: `public class MyViewHolder extends RecyclerView.ViewHolder { private MyViewBinding binding; public MyViewHolder(View itemView) { super(itemView); binding = DataBindingUtil.bind(itemView); } public void bind(YourObject o) { binding.setYourObject(o); } }` and in your onBind method `@Override public void onBindViewHolder(MyViewHolder holder, int position) { MyObject object = //get your object holder.bind(status); }` – subhash Sep 15 '15 at 08:07
  • 1
    I think the first method is the superior one. As `onCreateViewHolder` is called some limited times (e.g. 6, 7 times), but `onBindViewHolder` is getting called every time you scroll. – Ashkan Sarlak Sep 15 '15 at 08:28
  • 1
    First method is good for binding where it does not involves any data type model objects. I think my previous comment is good in case there is some data you need to bind in onBind method. It creates bind method on ViewHolder, which has reference to ViewDataBinding and just call holder's bind method. – subhash Sep 15 '15 at 10:47
0
    Even i have the same problem below is my code


    @BindingAdapter({"bind:customFont"})
        public static void customFont(TextView textView, String fontName) {
textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/"+fontName));
        }    

    <TextView
                android:id="@+id/tv_book_stay"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:layout_marginTop="35dp"
                app:customFont="@{@string/roboto_bold}"
                android:text="@{data.mob1BookYourStay}"
                android:textSize="22sp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/iv_logo" />

    <string name="roboto_bold">roboto_bold.ttf</string>

    DataBindingUtil.setContentView(this, R.layout.activity_home);
praveen2034
  • 109
  • 6