2

Currently, I would like to have a bold effect on my floating text of InputTextLayout. Here's what I'm doing

this.usernameTextInputLayout.setTypeface(Utils.ROBOTO_BOLD_TYPE_FACE);

It works as expected. The floating text (Username) has became bold.

enter image description here

However, this will create another undesired effect for me. The hint text will become bold as well.

enter image description here

You can compare the above 2 images. Please take note, for comparison purpose, I just leave passwordTextInputLayout as it is.

Is it possible to have different fonts, for floating text and hint text of InputTextLayout?

Brijesh Kumar
  • 1,685
  • 2
  • 17
  • 28
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Yes, it's possible, but I'm pretty sure it would take reflection to do it. I've recently written a couple of custom `TextInputLayout` subclasses, and, off the top of my head, I can't think of a way to do it without reflection. If that's acceptable, I can probably put something together. – Mike M. Oct 23 '16 at 09:13
  • I had tried reflection technique in http://stackoverflow.com/a/30767869/72437 It will affect hint text as well. – Cheok Yan Cheng Oct 23 '16 at 09:14
  • Hmm, I'm relatively confident that it can be done. Gimme a little bit to do some tests. I'll let ya know. – Mike M. Oct 23 '16 at 09:17
  • Yeah, I think I got it, barring any unforeseen side effects. I can post an answer, if you'd like, so you can test it with your setup. – Mike M. Oct 23 '16 at 09:39
  • 1
    Thank you Mike. Can you kindly post your finding as answer? I will test it out later time... – Cheok Yan Cheng Oct 23 '16 at 09:44

1 Answers1

7

As you know, TextInputLayout uses a private helper class to handle the hint text stylings and animations. This class - CollapsingTextHelper - maintains separate typefaces for its collapsed and expanded states. We just need to set the right one, which we'll do using reflection.

I usually package these kinds of functionalities into custom subclasses, so I'll do the same here. If you don't want to use a subclass, the reflection stuff could be easily pulled into some simple methods you can put in your Activity or utility class.

public class CustomTextInputLayout extends TextInputLayout {

    private Object collapsingTextHelper;
    private Method setCollapsedTypefaceMethod;
    private Method setExpandedTypefaceMethod;

    public CustomTextInputLayout(Context context) {
        this(context, null);
    }

    public CustomTextInputLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
    }

    private void init() {
        try {
            Field cthField = TextInputLayout.class
                .getDeclaredField("mCollapsingTextHelper");
            cthField.setAccessible(true);
            collapsingTextHelper = cthField.get(this);

            setCollapsedTypefaceMethod = collapsingTextHelper
                .getClass().getDeclaredMethod("setCollapsedTypeface", Typeface.class);
            setCollapsedTypefaceMethod.setAccessible(true);

            setExpandedTypefaceMethod = collapsingTextHelper
                .getClass().getDeclaredMethod("setExpandedTypeface", Typeface.class);
            setExpandedTypefaceMethod.setAccessible(true);
        }
        catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
            collapsingTextHelper = null;
            setCollapsedTypefaceMethod = null;
            setExpandedTypefaceMethod = null;
            e.printStackTrace();
        }
    }

    public void setCollapsedTypeface(Typeface typeface) {
        if (collapsingTextHelper == null) {
            return;
        }

        try {
            setCollapsedTypefaceMethod.invoke(collapsingTextHelper, typeface);
        }
        catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public void setExpandedTypeface(Typeface typeface) {
        if (collapsingTextHelper == null) {
            return;
        }

        try {
            setExpandedTypefaceMethod.invoke(collapsingTextHelper, typeface);
        }
        catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Somewhat counterintuitively, TextInputLayout's collapsed state is when the hint is the floating label above the EditText. Its expanded state is when the hint is in the "normal" position, inside the EditText. Methods to set the typeface for both states are given above.

This is a drop-in replacement for TextInputLayout, and you can use it in your layouts just as you would that. For example:

<com.mycompany.myapp.CustomTextInputLayout
    android:id="@+id/username_til"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:hintTextAppearance="@style/TextLabel">

    <android.support.design.widget.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:hint="Username" />

</com.mycompany.myapp.CustomTextInputLayout>

In your code, to set the typeface of the floating text hint:

CustomTextInputLayout usernameTextInputLayout =
    (CustomTextInputLayout) findViewById(R.id.username_til);

usernameTextInputLayout.setCollapsedTypeface(Utils.ROBOTO_BOLD_TYPE_FACE);

The CollapsingTextHelper methods used above were added in version 23.1.0 of the support library. If you're using a previous version, or you're getting a NoSuchMethodException for some other reason, the original version of my answer that directly sets the typeface fields should work, no matter the version.

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • Thanks for the code. I realize hint text doesn't follow the font in `EditText`. However, by using reflection on `mExpandedTypeface`, I'm able to change hint text's font too. – Cheok Yan Cheng Oct 23 '16 at 15:23
  • 1
    Oh, doesn't it? I'll have to have a look at the source again. I probably had both set to the same in my theme, and I'd just assumed the one was set from the other. Thanks for the info! I'll update the answer with a correction after review the code again to be sure what's going on there. Glad you got it working. Cheers! – Mike M. Oct 23 '16 at 15:28
  • I just had a dig through the latest source, and the hint typeface still seems to be set from the `EditText`'s, but the style is restricted to `NORMAL`. I realized, though, that you might be setting the `EditText`'s typeface at runtime, after inflation, in which case, yeah, you're right; the `TextInputLayout` won't change its typeface. It only initializes that itself when the `EditText` is first added to it. After that, it won't react to changes you make on the `EditText`. It's the same for the hint string, color, etc. – Mike M. Oct 23 '16 at 17:13
  • Anyhoo, I did happen upon a couple of newer methods in `CollapsingTextHelper` that might simplify your code a bit, since you're setting both typefaces: `setCollapsedTypeface()` and `setExpandedTypeface()`. We still need to use reflection to get them, but it's a simple `invoke()` call to use them, and they call `recalculate()` themselves, so we can drop that bit. I've updated the answer to use them. If you wanna give 'em a try, lemme know if you've any problems, and I'll rollback the answer. I don't think you will, though, as long as you're using at least version 23.4.0 of the support library. – Mike M. Oct 23 '16 at 17:15
  • Im having NoSuchMethodException starting here: ```setCollapsedTypefaceMethod = collapsingTextHelper .getClass().getDeclaredMethod("setCollapsedTypeface", Typeface.class);``` Would be nice to be have a different floating label font and hint font. Please help. – ekouChiq Aug 22 '17 at 06:04
  • @ekouChiq You're probably using an older version of the support library. I'm not sure exactly which version added those methods, but I'll figure it out here in a little while. For now, update to the newest version you can, if possible. – Mike M. Aug 22 '17 at 06:15
  • @ekouChiq If you can't update, [the original version of my answer](https://stackoverflow.com/revisions/40201827/1) should work for you. – Mike M. Aug 22 '17 at 06:18
  • @MikeM. I'm actually using v.25.2.0... The original version works for me... Thank you. – ekouChiq Aug 22 '17 at 06:49
  • @ekouChiq Hmm, I just found that those methods were added in version 23.1.0, so 25.2.0 should work. Weird. Anyhoo, I'll do some more checks, and add a note to my answer about that. Thanks for reminding me. I'd meant to do that after I refactored for those methods, and completely forgot. Glad I had something worked for ya, at any rate. Cheers! – Mike M. Aug 22 '17 at 06:54
  • How can i call these methods within the same class problematically instead of calling them from activity. – Usman Rana Apr 17 '18 at 08:13
  • @UsmanRana I'm not sure what you're asking. If you mean you want to immediately set those typefaces directly in `CustomTextInputLayout`, then you can call `setCollapsedTypeface()` and `setExpandedTypeface()` right after the `init()` call in the third constructor. – Mike M. Apr 17 '18 at 08:24
  • that's exactly what i want, but calling in init didn't work work me. I had to call them in my activity or BindingUtils. How to set immediately? – Usman Rana Apr 17 '18 at 11:16
  • 2
    @UsmanRana Do it in `onFinishInflate()` instead. It should be done setting itself up by then. – Mike M. Apr 17 '18 at 19:48
  • Thanks @MikeM. , this was exactly that i asked for. – Usman Rana Apr 18 '18 at 06:33
  • @UsmanRana No problem. Yeah, it's been a while since I played with `TextInputLayout`. I forgot that it initially sets those typefaces internally when the `EditText` gets added, and that happens well after the constructor runs. Glad it worked. Cheers! – Mike M. Apr 18 '18 at 06:38
  • Field cthField = TextInputLayout.class .getDeclaredField("collapsingTextHelper"); worked for me – Daniyal Javaid Feb 03 '21 at 10:58