1

I'm migrating from old ListView to RecyclerView in my project. I have decided to use Data Binding to bind list values, but I'm experiencing some problems to set custom text span for my text box.

Please look at this piece of code (this is how it's done with old ListBox adapater):

@Override
public View getView(int position, @Nullable View convertView, ViewGroup parent) {
    ...

    Typeface tf = Typeface.createFromAsset(convertView.getContext().getAssets(), "fonts/fontawesome.ttf");
    SpannableStringBuilder textCategory = new SpannableStringBuilder((item != null ? item.getCategoryIconName() : null) + "     " + item.getCategoryName());
    textCategory.setSpan (new CustomTypefaceSpan("", tf), 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);

    viewHolder.eventCategory.setText(textCategory);

    ...
}

Basically, my CustomTypeFaceSpan is used to set multiple fonts for a single TextView. How to implement the same with Data Binding? Is it possible to use BindingAdapter for that?

My try:

<variable name="categoryText" type="String"/>
<variable name="data" type="Event"/>

<TextView
                android:id="@+id/event_category_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="32dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="16dp"
                android:paddingBottom="3dp"
                android:paddingTop="3dp"
                android:text="@{data.categoryIconName + data.categoryName}"
                android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                android:textColor="@color/colorEventCategoryText"
                app:customFunction="@{categoryText}"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

Binding adapter:

@BindingAdapter({"customFunction"})
    public static void setSpan(TextView textView, String categoryText)
    {
        Typeface tf = Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/fontawesome.ttf");
        SpannableStringBuilder textCategory = new SpannableStringBuilder(categoryText);
        textCategory.setSpan (new CustomTypefaceSpan("", tf), 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        textView.setText(textCategory);
    }

Problem: categoryText is always null. Any ideas? TextView is bound properly, because without custom function it displays text as it should.

My list adapter:

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder>
{
    public List<Event> events = null;
    private final ListFragment.OnFragmentInteractionListener mListener;

    public ListAdapter(List<Event> events, ListFragment.OnFragmentInteractionListener mListener)
    {
        this.events = events;
        this.mListener = mListener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
    {

        LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
        ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.cell, viewGroup, false);
        return new ViewHolder(binding);
    }


    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position)
    {

        final Event event = events.get(position);
        viewHolder.bind(event);

        viewHolder.binding.getRoot().setOnClickListener(v ->
        {
            if (null != mListener)
            {
                mListener.onFragmentInteraction(event);
            }
        });
    }

    @Override
    public int getItemCount()
    {
        return events.size();
    }


    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        private final ViewDataBinding binding;

        public ViewHolder(ViewDataBinding binding)
        {
            super(binding.getRoot());
            this.binding = binding;
        }

        public void bind(Object obj)
        {
            binding.setVariable(BR.data,obj);
            binding.executePendingBindings();
        }
    }
}
user1209216
  • 7,404
  • 12
  • 60
  • 123

1 Answers1

2

Use custom attribute like:

In xml:

<data>
  <variable
      name="name"
      type="String"></variable>
</data>
........
  <TextView
     android:id="@+id/textView2"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_margin="20dp"
     app:customFunction="@{name}"
    />

In Activity Class

.............
ActivityMainBinding binding = DataBindingUtil........
binding.setName("Something");
...................
@BindingAdapter("customFunction")
public static void myCustomFunction(TextView textView, String name){
    Log.d("MainActivity", "custom function called");
    String nameCaps = name.toUpperCase();
    textView.setText(nameCaps);
}

myCustomFunction(..) will be called when Textview is created from xml. You can use your custom function to set Span to your view.

Abu Yousuf
  • 5,729
  • 3
  • 31
  • 50
  • Custom function is called, but `name` parameter is alwasys null for me. I updated my question. Not it's not activity binding, it's RecyclerView (I don't know if it matters) – user1209216 Jul 10 '17 at 12:01
  • set value to your categoryText variable by binding.setCategoryText("value") method. setCategoryText() method will automatically created when you declare a variable in variable tag inside xml – Abu Yousuf Jul 10 '17 at 12:13
  • Check this link for more details https://medium.com/google-developers/android-data-binding-custom-setters-55a25a7aea47 – Abu Yousuf Jul 10 '17 at 12:22
  • 1
    Where to put `binding.setCategoryText("value")` do you mean ViewHolder? I have read docs you posted but I am still unable to figure out what's wrong. Question updated, added list adapter code – user1209216 Jul 11 '17 at 07:12
  • put that code in onCreateViewHolder() function after " ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.cell, viewGroup, false);" this line – Abu Yousuf Jul 11 '17 at 07:39
  • This is not possible, there is no such method available in `binding` object in my case. Why my current approach not working? It actually works, but I can't use custom function on it – user1209216 Jul 11 '17 at 07:44