29

I want to apply two different font styles to a text in a single TextView.

My case is same as Android - two sentences, two styles, one TextView. The only difference is that I want to set a Custom Font on the whole text. I have included Helvetica Font as an assets in my project and want to apply that font to the TextView with first part of the text will be Helvetica BOLD and remaining part Helvetica NORMAL. Any suggestions how it can be done ?

Text needed in following format. Custom text with different styles and single textview.

enter image description here

Community
  • 1
  • 1
rizzz86
  • 3,862
  • 8
  • 35
  • 52
  • ,why can't you use two different textview with different fontstyles. – Senthil Mg Mar 08 '12 at 14:17
  • @SenthilMg you can see the reason for this on the reference link in the question. It will not format text as it is required – rizzz86 Mar 08 '12 at 14:33
  • ,i have guess to split the complete string into two by using regex as : and set on different textview for eg: String text = AAAA:BBBBB , String[] splitText = text.split(":"); tv1.setText(splitText[0]);tv2.setText(splitText[1]); – Senthil Mg Mar 08 '12 at 14:39

6 Answers6

77

One way to do this is to extend TypefaceSpan:

import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.TypefaceSpan;

    public class CustomTypefaceSpan extends TypefaceSpan {
        private final Typeface newType;

        public CustomTypefaceSpan(String family, Typeface type) {
            super(family);
            newType = type;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            applyCustomTypeFace(ds, newType);
        }

        @Override
        public void updateMeasureState(TextPaint paint) {
            applyCustomTypeFace(paint, newType);
        }

        private static void applyCustomTypeFace(Paint paint, Typeface tf) {
            int oldStyle;
            Typeface old = paint.getTypeface();
            if (old == null) {
                oldStyle = 0;
            } else {
                oldStyle = old.getStyle();
            }

            int fake = oldStyle & ~tf.getStyle();
            if ((fake & Typeface.BOLD) != 0) {
                paint.setFakeBoldText(true);
            }

            if ((fake & Typeface.ITALIC) != 0) {
                paint.setTextSkewX(-0.25f);
            }

            paint.setTypeface(tf);
        }
    }

Then when you want to use two different typefaces call:

String firstWord = "first ";
String secondWord = "second";

// Create a new spannable with the two strings
Spannable spannable = new SpannableString(firstWord+secondWord);

// Set the custom typeface to span over a section of the spannable object
spannable.setSpan( new CustomTypefaceSpan("sans-serif",CUSTOM_TYPEFACE), 0, firstWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan( new CustomTypefaceSpan("sans-serif",SECOND_CUSTOM_TYPEFACE), firstWord.length(), firstWord.length() + secondWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

// Set the text of a textView with the spannable object
textView.setText( spannable );
Community
  • 1
  • 1
Ljdawson
  • 12,091
  • 11
  • 45
  • 60
  • Thanks Laurence for the answer. I am trying this one and lets see what will be the outcome. Also let me try to understand what is going on in the code snippet you have provided )) – rizzz86 Mar 08 '12 at 16:03
  • No problem, I've made small change to the second block of code concerning the name of the extended TypefaceSpan. In terms of implementation, the CustomTypefaceSpan is ready to go, you will just need to add this to your project. The second block of code is what you will need to edit. – Ljdawson Mar 08 '12 at 17:11
  • 2
    Laurence that works for me. The issue seems to be small but the solution is a bit complex. Thanks for the help that really saved my time. – rizzz86 Mar 09 '12 at 07:13
  • Hi, i have posted a related question here http://stackoverflow.com/questions/17744664/any-way-to-set-span-directly-to-spanable-text?noredirect=1#comment25870879_17744664 In which i want to know can i drireclty apply any spans on my text to use the above custom type faces, please have a look on my question and help me – Renjith K N Jul 19 '13 at 11:34
  • There's a minor bug in here: where you have secondWord.length(), it should be the length of the entire combined string, not just the length of the second word. Otherwise, you will most likely get an IndexOutOfBoundsException – dennisdrew Mar 06 '14 at 23:06
  • Although answer is good enough, but i would like to mention one point here that please do not change the span to string for using with textview or edit text as it will lose the span that you put on message. – Ankur Chaudhary Jul 10 '14 at 07:55
  • Of course it will Ankur, that's why I used the Spannable class. – Ljdawson Jul 13 '14 at 14:18
7

Here is a solution more straightforward, you can use HTML to set different styles on the same TextView.

For example:

// Styled label
String styledText = "<big><b><font color='#333333'>title</font></b></big> <small><b><font color='#CC5490'>subtitle</font></b></small>";

// Apply the styled label on the TextView
textView.setText(Html.fromHtml(styledText));

You need the following import:

import android.text.Html;
Yoann Hercouet
  • 17,894
  • 5
  • 58
  • 85
2

This may work - create your own custom TextView and then use a StyleSpan on one part of it:

public class CustomTextView extends TextView {

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

    @Override
    public void setTypeface(Typeface tf, int style) {
        if (style == 1){
            //replace "HelveticaBOLD.otf" with the name of your bold font
            tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "HelveticaBOLD.otf");
        }else{
            //replace "HelveticaNORMAL.otf" with the name of your normal font
            tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "HelveticaNORMAL.otf");
        }
        super.setTypeface(tf, 0);
    }
}

And then you can do something like:

int index1 = 0; //wherever bold should begin
int index2 = 5; //wherever bold should end

Spannable span = new SpannableString("some string");
span.setSpan(new StyleSpan(Typeface.BOLD),index1, index2,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

((CustomTextView)findViewById(R.id.yourTextView)).setText(span);
Sam Dozor
  • 40,335
  • 6
  • 42
  • 42
0

TypefaceSpan - For example, let's consider a TextView with android:textStyle="italic" and a typeface created based on a font from resources, with a bold style. When applying a TypefaceSpan based the typeface, the text will only keep the bold style, overriding the TextView's textStyle. When applying a TypefaceSpan based on a font family: "monospace", the resulted text will keep the italic style.

https://developer.android.com/reference/android/text/style/TypefaceSpan?

0

You can create a custom view and render your text with Two Paint objects using canvas.drawText method

Mina Wissa
  • 10,923
  • 13
  • 90
  • 158
-2

did you try setting a custom typeface on the TextView before applying spanable text ?

Typeface face = Typeface.createFromAsset(ctx.getAssets(), "fonts/boost.ttf")
TextView tv = (TextView) ctx.findViewById(id);
tv.setTypeface(face);

and then apply SpannableStringBuilder as from the linked question - I'm only assuming that if your ttf supports 'normal' and 'bold' it would render accordingly :)

gibffe
  • 835
  • 8
  • 8