2

I am using a RecyclerView to populate the data from cloud. But it looks like, setting custom font for a TextView is not easy as its inside the view. Traditional way, this is what I tried :

TextView tx = (TextView)findViewById(R.id.person_age);

    Typeface custom_font = Typeface.createFromAsset(getAssets(), "fonts/Dancing Script.ttf");

    tx.setTypeface(custom_font);

But here , it crashes the app, So looking for fix, here is the code to the Main Activiy:

    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Parse.initialize(this, "app-id", "clientkey");
    setContentView(R.layout.activity_big_board);
    initializeData();
    TextView tx = (TextView)findViewById(R.id.person_age);

    Typeface custom_font = Typeface.createFromAsset(getAssets(), "fonts/Dancing Script.ttf");

    tx.setTypeface(custom_font);
    adapter = new RVAdapter(persons);


    rv=(RecyclerView)findViewById(R.id.rv);

    LinearLayoutManager llm = new LinearLayoutManager(this);
    rv.setLayoutManager(llm);
    rv.setHasFixedSize(true);


    initializeAdapter();

}

private void initializeData(){
    persons = new ArrayList<>();


    ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("BigBoard");
    query.findInBackground(new FindCallback<ParseObject>() {
        public void done(List<ParseObject> credentialList, ParseException e) {
            if (e == null) {
                for(int i=0;i<credentialList.size();i++)
                {
                    a=credentialList.get(i).getString("Location");
                    b=credentialList.get(i).getString("Feed");
                    persons.add(new Person(a,b));



                    Log.d("OUT", "So the Val::------> " +a +b);


                }
            } else {
                Log.d("score", "Error: " + e.getMessage());
            }

            adapter.notifyDataSetChanged();
        }
    });


}

private void initializeAdapter(){

    rv.setAdapter(adapter);
}

And here is the code to the Adapter class:

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {

public static class PersonViewHolder extends RecyclerView.ViewHolder {

    CardView cv;
    TextView personName;
    TextView personAge;
    //ImageView personPhoto;

    PersonViewHolder(View itemView) {
        super(itemView);
        cv = (CardView)itemView.findViewById(R.id.cv);
        personName = (TextView)itemView.findViewById(R.id.person_name);
        personAge = (TextView)itemView.findViewById(R.id.person_age);
        //personPhoto = (ImageView)itemView.findViewById(R.id.person_photo);
    }
}

List<Person> persons;

RVAdapter(List<Person> persons){
    this.persons = persons;
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

@Override
public PersonViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
    PersonViewHolder pvh = new PersonViewHolder(v);
    return pvh;
}

@Override
public void onBindViewHolder(PersonViewHolder personViewHolder, int i) {
    personViewHolder.personName.setText(persons.get(i).name);
    personViewHolder.personAge.setText(persons.get(i).surname);
   // personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
}

@Override
public int getItemCount()
{
    if(persons!=null)
    {
        return persons.size();
    }
    else
    {
        return 0;
    }
}

}

So, what's the best way to do this without causing any problems to the UI Thread? Thanks,

OBX
  • 6,044
  • 7
  • 33
  • 77
  • "it crashes the app" -- use LogCat to examine the Java stack trace associated with your crash: https://stackoverflow.com/questions/23353173/unfortunately-myapp-has-stopped-how-can-i-solve-this – CommonsWare Oct 27 '15 at 15:17
  • I thought it wasn't required. Its a `NullPointerException` , as I guess its trying to set font for something that is not yet ready, – OBX Oct 27 '15 at 15:21
  • The question is : should I choose to set it in the Adapter class? whats the correct way? – OBX Oct 27 '15 at 15:22

4 Answers4

4

Move your Typeface logic into PersonViewHolder, probably in its constructor, for two reasons:

  1. The TextView exists by that point.

  2. Your RecyclerView probably will have more than one PersonViewHolder, in which case you need to set the Typeface in every PersonViewHolder, because there will be more than one item and more than one TextView that needs the Typeface.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Well, I did try that before posting in here, but then the problem was with `getAssets()` , cannot resolve method getAssets() , any idea? :) – OBX Oct 27 '15 at 15:31
  • @oblivion: `getAssets()` is a method on `Context`. Call `getContext()` on the `TextView` to get a `Context`. – CommonsWare Oct 27 '15 at 15:33
  • Did you mean to replace `getAssets()` with `getContext()` ?, then again its the same error :( – OBX Oct 27 '15 at 15:36
  • 3
    @oblivion: No. You have an instance of a `TextView` (`personAge`). The `TextView` class defines a method, `getContext()`. `Context` defines a method, `getAssets()`. Hence, `personAge.getContext().getAssets()` will return your `AssetManager`. – CommonsWare Oct 27 '15 at 16:00
1
 public class Adapter_Recycler_Film extends RecyclerView.Adapter<Adapter_Recycler_Film.DataViewHolder> {
    private static Typeface face;
    private Context mContext;
    Adapter_Recycler_Film.DataViewHolder viewHolder;

    public Adapter_Recycler_Film(Context context,List<String> dateData) {
        ........
        this.mContext=context;
        face = Typeface.createFromAsset(mContext.getAssets(),"fonts/IRANSansMobile(FaNum)_Medium.ttf");
    }
}

public class DataViewHolder extends RecyclerView.ViewHolder {
    public TextView tDate;

    public DataViewHolder(View itemView) {
        super(itemView);
        tDate = (TextView) itemView.findViewById(R.id.txt);
        if(tDate!=null){
            tDate.setTypeface(face);
        }

    }
}
0

As @CommonsWare's answer is probably the most relevant to your specific case. In the perspective of efficiency, readability and hindsight I would recommend creating a custom TextView and then assigning the TextView to the item row of your RecyclerView or anywhere in your application where you need a specified font for that matter.

CustomFontTextView.java

package icn.premierandroid.misc;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

import icn.premierandroid.R;

public class CustomFontTextView extends TextView {
    AttributeSet attr;
    public CustomFontTextView(Context context) {
        super(context);
        setCustomFont(context, attr);
    }

    public CustomFontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        String customFont = null;
        TypedArray a = null;
        if (attrs != null) {
            a = ctx.obtainStyledAttributes(attrs, R.styleable.CustomFontTextView);
            customFont = a.getString(R.styleable.CustomFontTextView_customFont);
        }
        if (customFont == null) customFont = "fonts/portrait-light.ttf";
        setCustomFont(ctx, customFont);
        if (a != null) {
            a.recycle();
        }
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
            tf = Typeface.createFromAsset(ctx.getAssets(), asset);
        } catch (Exception e) {
            Log.e("textView", "Could not get typeface", e);
            return false;
        }
        setTypeface(tf);
        return true;
    }
}

values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomFontTextView">
        <attr name="customFont" format="string"/>
    </declare-styleable>
</resources >

Example item row for RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    card_view:cardUseCompatPadding="true"
    card_view:cardCornerRadius="8dp">

   /** Containers and other Components **/

    <icn.premierandroid.misc.CustomFontTextView
        android:id="@+id/comment_user_name"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:gravity="center_vertical"
        android:layout_weight="0.36"/>

    <icn.premierandroid.misc.CustomFontTextView
        android:layout_width="0dp"
        android:layout_weight="0.40"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:layout_marginStart="1dp"
        android:layout_marginLeft="1dp" />

    <icn.premierandroid.misc.CustomFontTextView
        android:id="@+id/comment_time"
        android:layout_width="0dp"
        android:layout_weight="0.25"
        android:gravity="center_vertical"
        android:layout_height="match_parent"/>

/** Containers and other Components **/

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

So armed with this code, you won't need to change anything in your RecyclerView.Adapter, RecyclerView.ViewHolder or anywhere where you have a TextView already initialized.

Note: you can still initialize the components by using TextView textView = (TextView) findViewById(R.id.textView);. The XML does all the work for you. Unless you are creating TextView components programmatically then you would need to create it as CustomFontTextView textView = new CustomFontTextView();

Also you can apply the same logic to almost any components such as EditText, Switches, RadioButtons, TabLayouts etc etc

BradleyIW
  • 1,338
  • 2
  • 20
  • 37
-1

create other class which extend application

public class yourApplication extends Application {
public Typeface robotoRegular;
public Typeface robotoLight;
public Typeface robotoBold;
public Typeface robotoMedium;


@Override
public void onCreate() {
    super.onCreate();
    robotoRegular = Typeface.createFromAsset(this.getAssets(), getResources().getString(R.string.robotoRegular));
    robotoLight = Typeface.createFromAsset(this.getAssets(), getResources().getString(R.string.robotoLight));
    robotoBold = Typeface.createFromAsset(this.getAssets(), getResources().getString(R.string.robotoBold));
    robotoMedium = Typeface.createFromAsset(this.getAssets(), getResources().getString(R.string.robotoMedium));

    }
}

then set it to the binder

 @Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element
    final String name = mDataset.get(position);

    holder.txtHeader.setTypeface(((yourApplication) mContext.getApplicationContext()).robotoRegular);
    holder.txtFooter.setTypeface(((yourApplication) mContext.getApplicationContext()).robotoLight);
}