Cambiar la fuente del texto de la pestaña en el soporte de diseño de Android TabLayout


109

Estoy tratando de trabajar en lo nuevo TabLayoutde la biblioteca de diseño de Android.

Quiero cambiar el texto de la pestaña a una fuente personalizada . Y traté de buscar algunos estilos relacionados TabLayout, pero terminé con esto .

Indique cómo puedo cambiar las fuentes de texto de las pestañas.


3
Use Spannable String
NullByte

Respuestas:


37

Cree un TextView a partir de código Java o XML como este

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:textSize="15sp"
    android:textColor="@color/tabs_default_color"
    android:gravity="center"
    android:layout_height="match_parent"
/>

Asegúrese de mantener la identificación como está aquí porque TabLayout verifica esta identificación si usa la vista de texto personalizada

Luego, desde el código, infle este diseño y configure la vista personalizada Typefaceen esa vista de texto y agregue esta vista personalizada a la pestaña

for (int i = 0; i < tabLayout.getTabCount(); i++) {
     //noinspection ConstantConditions
     TextView tv = (TextView)LayoutInflater.from(this).inflate(R.layout.custom_tab,null)
     tv.setTypeface(Typeface);       
     tabLayout.getTabAt(i).setCustomView(tv);
}

2
¿Cómo puedo configurar tabTextColory tabSelectedTextColorpropiedad en esta situación?
Alireza Noorali

Obtengo la solución de la respuesta de
Praveen

162

Si está utilizando TabLayouty desea cambiar la fuente, debe agregar un nuevo bucle for a la solución anterior como esta:

private void changeTabsFont() {
    ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0);
        int tabsCount = vg.getChildCount();
        for (int j = 0; j < tabsCount; j++) {
            ViewGroup vgTab = (ViewGroup) vg.getChildAt(j);
            int tabChildsCount = vgTab.getChildCount();
            for (int i = 0; i < tabChildsCount; i++) {
                View tabViewChild = vgTab.getChildAt(i);
                if (tabViewChild instanceof TextView) {
                    ((TextView) tabViewChild).setTypeface(Font.getInstance().getTypeFace(), Typeface.NORMAL);
                }
        }
    }
} 

Consulte cambiar el estilo de fuente en las pestañas de la barra de acción usando sherlock


No estoy usando la vista previa de M. Hay alguna otra forma de hacerlo.
Dory

1
No necesita la vista previa de M, es válida para todos los que usanTabLayout
Mario Velasco

Sin embargo, me encuentro con una NullPointerException en android.view.View android.support.design.widget.TabLayout.getChildAt (int), ¿pueden ayudarme a solucionarlo? No puedo encontrar lo que me falta en mi código.
brettbrdls

1
El primer parámetro de setTypeFacees a TypeFace, en caso de que no pueda encontrar la Fontclase (que no parece existir para mí)
Vahid Amiri

104

Cree su propio estilo personalizado y utilice el estilo principal como parent="@android:style/TextAppearance.Widget.TabWidget"

Y en el diseño de su pestaña use este estilo como app:tabTextAppearance="@style/tab_text"

Ejemplo: Estilo:

<style name="tab_text" parent="@android:style/TextAppearance.Widget.TabWidget">
    <item name="android:fontFamily">@font/poppins_regular</item>
</style>

Ejemplo: componente de diseño de pestañas:

<android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:tabTextAppearance="@style/tab_text" />

9
Esto es correcto, lo estoy usando parent="TextAppearance.Design.Tab"en mi caso.
Javatar

1
Esto funciona mucho mejor que las primeras respuestas y sin magia negra (api oculta), que podría romper algo en el futuro
Sulfkain

2
solo funciona cuando se usa fontFamily, no funciona cuando se usa fontPath
Tram Nguyen

2
Recibía extrañas excepciones de forma errática al usar TextAppearance.Widget.TabWidget. La respuesta de @Javatar lo arregló para mí.
funct7

47

Gran respuesta de Praveen Sharma. Solo una pequeña adición: en lugar de usar en changeTabsFont()cualquier lugar que necesite TabLayout, simplemente puede usar el suyo CustomTabLayout.

import android.content.Context;
import android.graphics.Typeface;
import android.support.design.widget.TabLayout;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class CustomTabLayout extends TabLayout {
    private Typeface mTypeface;

    public CustomTabLayout(Context context) {
        super(context);
        init();
    }

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

    public CustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/Roboto-Regular.ttf");
    }

    @Override
    public void addTab(Tab tab) {
        super.addTab(tab);

        ViewGroup mainView = (ViewGroup) getChildAt(0);
        ViewGroup tabView = (ViewGroup) mainView.getChildAt(tab.getPosition());

        int tabChildCount = tabView.getChildCount();
        for (int i = 0; i < tabChildCount; i++) {
            View tabViewChild = tabView.getChildAt(i);
            if (tabViewChild instanceof TextView) {
                ((TextView) tabViewChild).setTypeface(mTypeface, Typeface.NORMAL);
            }
        }
    }

}

Y una cosa más. TabViewes un LinearLayoutcon TextViewinterior (también puede contener opcionalmente ImageView). Para que pueda simplificar aún más el código:

@Override
public void addTab(Tab tab) {
    super.addTab(tab);

    ViewGroup mainView = (ViewGroup) getChildAt(0);
    ViewGroup tabView = (ViewGroup) mainView.getChildAt(tab.getPosition());
    View tabViewChild = tabView.getChildAt(1);
    ((TextView) tabViewChild).setTypeface(mTypeface, Typeface.NORMAL);
}

Pero no lo recomendaría de esta manera. Si la TabLayoutimplementación cambia, este código puede funcionar incorrectamente o incluso fallar.

Otra forma de personalizarlo TabLayoutes agregarle una vista personalizada. Aquí está el gran ejemplo .


addTab no se invocará en caso de que establezca pestañas como esta: mViewPager.setAdapter (new YourPagerAdapter (getChildFragmentManager ())); mTabLayout.setupWithViewPager (mViewPager);
Penzzz

2
@Penzzz, tienes toda la razón. En este caso, debe mover el código del método addTab a otro. Por ejemplo onMeasure.
Andrei Aulaska

@AndreiAulaska thnx Me salvas el día. Tu último Link sálvame.
Amol Dale

esto ya no funciona, creo que en la última versión. La respuesta de @ ejw ahora está funcionando. Necesito agregarlo en addTab (pestaña Tab, boolean setSelected)
Sayem

3
Esto funciona bien. NOTA: a partir de la biblioteca de soporte 25.x, debe anular en addTab(Tab tab, int position, boolean setSelected)lugar de addTab(Tab tab).
Vicky Chijwani

20

Para utilizar la compatibilidad con fuentes en la XMLfunción en dispositivos que se ejecutan Android 4.1(API nivel 16) y superior, utilice la biblioteca de compatibilidad 26+.

  1. Haga clic derecho en la carpeta res
  2. Nuevo -> directorio de recursos de Android-> seleccionar fuente -> Ok
  3. Coloque su myfont.ttfarchivo en la carpeta de fuentes recién creada

En res/values/styles.xmlagregar:

<style name="customfontstyle" parent="@android:style/TextAppearance.Small">
    <item name="android:fontFamily">@font/myfont</item>
</style>

En el archivo de diseño, agregue la aplicación: tabTextAppearance = "@ style / customfontstyle",

<android.support.design.widget.TabLayout
    android:id="@+id/tabs"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tabGravity="fill"
    app:tabTextAppearance="@style/customfontstyle"
    app:tabMode="fixed" />

Consulte [fuentes en xml]. ( Https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml )


funciona en formularios xamarin y xamarin android.thanks
Esmail Jamshidiasl

¡Gran respuesta! Sin embargo
XY

16

El siguiente método cambiará la fuente por completo de forma ViewGrouprecursiva. Elegí este método porque no tienes que preocuparte por la estructura interna de TabLayout. Estoy usando la biblioteca de caligrafía para establecer una fuente.

void changeFontInViewGroup(ViewGroup viewGroup, String fontPath) {
    for (int i = 0; i < viewGroup.getChildCount(); i++) {
        View child = viewGroup.getChildAt(i);
        if (TextView.class.isAssignableFrom(child.getClass())) {
            CalligraphyUtils.applyFontToTextView(child.getContext(), (TextView) child, fontPath);
        } else if (ViewGroup.class.isAssignableFrom(child.getClass())) {
            changeFontInViewGroup((ViewGroup) viewGroup.getChildAt(i), fontPath);
        }
    }
}

1
El problema con esto es que si adjunta el diseño a un Viewpager, perderá las fuentes personalizadas.
ruptura

10

Para soporte de diseño 23.2.0, usando setupWithViewPager, tendrá que mover el código de addTab (pestaña Tab) a addTab (pestaña Tab, boolean setSelected).


8

Puedes usar esto, me funciona.

 private void changeTabsFont() {
    ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0);
    int tabsCount = vg.getChildCount();
    for (int j = 0; j < tabsCount; j++) {
        ViewGroup vgTab = (ViewGroup) vg.getChildAt(j);
        int tabChildsCount = vgTab.getChildCount();
        for (int i = 0; i < tabChildsCount; i++) {
            View tabViewChild = vgTab.getChildAt(i);
            if (tabViewChild instanceof TextView) {
                AssetManager mgr = getActivity().getAssets();
                Typeface tf = Typeface.createFromAsset(mgr, "fonts/Roboto-Regular.ttf");//Font file in /assets
                ((TextView) tabViewChild).setTypeface(tf);
            }
        }
    }
}

7

Bueno, lo encontré simple en 23.4.0 sin usar un bucle. Simplemente anule addTab (pestaña @NonNull Tab, boolean setSelected) como lo sugiere @ejw.

@Override
public void addTab(@NonNull Tab tab, boolean setSelected) {
    CoralBoldTextView coralTabView = (CoralBoldTextView) View.inflate(getContext(), R.layout.coral_tab_layout_view, null);
    coralTabView.setText(tab.getText());
    tab.setCustomView(coralTabView);

    super.addTab(tab, setSelected);
}

Y aquí está el XML

<?xml version="1.0" encoding="utf-8"?>
<id.co.coralshop.skyfish.ui.CoralBoldTextView
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/custom_text"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:ellipsize="end"
   android:gravity="center"
   android:singleLine="true"
   android:textColor="@color/graylove"
   android:textSize="@dimen/tab_text_size" />

Espero que pueda ayudar :)


1
Pero, ¿cómo configurar tabSelectedTextColor y tabTextColo?
Mostafa Imran

@MostafaImran su versión de android:textColor="@color/graylove"debe tener el selector de lista de estado para eso con el color state_selected especificado
AA_PV

7

Como respondió Andrei , puede cambiar la fuente ampliando la clase TabLayout . Y como dijo Penzzz , no puede hacerlo con el método addTab . Invalidar OnLayout método como bramido:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom){
    super.onLayout(changed, left, top, right, bottom);

    final ViewGroup tabStrip = (ViewGroup)getChildAt(0);
    final int tabCount = tabStrip.getChildCount();
    ViewGroup tabView;
    int tabChildCount;
    View tabViewChild;

    for(int i=0; i<tabCount; i++){
        tabView = (ViewGroup)tabStrip.getChildAt(i);
        tabChildCount = tabView.getChildCount();
        for(int j=0; j<tabChildCount; j++){
            tabViewChild = tabView.getChildAt(j);
            if(tabViewChild instanceof AppCompatTextView){
                if(fontFace == null){
                    fontFace = Typeface.createFromAsset(context.getAssets(), context.getString(R.string.IranSans));
                }
                ((TextView) tabViewChild).setTypeface(fontFace, Typeface.BOLD);
            }
        }
    }
}

Debe sobrescribir el método onLayout, porque, cuando usa el método setupWithViewPager para vincular el TabLayout con el ViewPager, debe establecer el texto de las pestañas con el método setText o en el PagerAdapter después de eso y cuando esto sucedió, el método onLayout se llamará en el ViewGroup principal ( TabLayout) y ese es el lugar para colocar la configuración de fontface. (Cambiar un texto de TextView causa que se llame al método onLayout de su padre: un tabView tiene dos hijos, uno es ImageView y otro es TextView)

Otra solución:

Primero, estas líneas de código:

    if(fontFace == null){
        fontFace = Typeface.createFromAsset(context.getAssets(), context.getString(R.string.IranSans));
    }

En la solución anterior, debe escribirse fuera de dos bucles.

Pero una mejor solución para API> = 16 es usar android: fontFamily :

Cree un directorio de recursos de Android llamado fuente y copie la fuente que desee en el directorio.

Luego usa estos estilos:

<style name="tabLayoutTitles">
    <item name="android:textColor">@color/white</item>
    <item name="android:textSize">@dimen/appFirstFontSize</item>
    <item name="android:fontFamily">@font/vazir_bold</item>
</style>

<style name="defaultTabLayout">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">@dimen/defaultTabLayoutHeight</item>
    <item name="android:gravity">right</item>
    <item name="tabTextAppearance">@style/tabLayoutTitles</item>
    <item name="tabSelectedTextColor">@color/white</item>
    <item name="tabIndicatorColor">@color/white</item>
    <item name="tabIndicatorHeight">@dimen/accomTabIndicatorHeight</item>
    <item name="tabMode">fixed</item>
    <item name="tabGravity">fill</item>
    <item name="tabBackground">@drawable/rectangle_white_ripple</item>
    <item name="android:background">@color/colorPrimary</item>
</style>

Esta es una solución de mal rendimiento, onLayout()se llamó con cada cambio de diseño, como el cambio de pestaña o incluso el desplazamiento de listas debajo de las pestañas, con fors anidados en muchas pestañas, la TabLayoutaplicación será lenta.
Amr Barakat

2
@Amr Barakat. Según este enlace: developer.android.com/reference/android/view/… , int, int, int, int), esto no es cierto. Yo también lo probé. Puse un punto de interrupción en el método onLayout y se llama cuando se crean las pestañas, no en el cambio de pestaña o en el desplazamiento de la lista.
Arash

1
Para mí onLayout(), me llaman varias veces cuando se cambia de pestaña (no estoy seguro de por qué exactamente), pero para tener en cuenta esto, solo configuro las fuentes cuando boolean changedes verdadero. Hacer eso evita configurar las fuentes varias veces.
Robert

gran solución, no se requiere código, solo atributos xml
cuidado 7j

3

Mi método de resolución así, cambia el texto de la pestaña Especificado,

 ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0);
 ViewGroup vgTab = (ViewGroup) vg.getChildAt(1);
 View tabViewChild = vgTab.getChildAt(1);
 if (tabViewChild instanceof TextView) {
      ((TextView) tabViewChild).setText(str);
 }

2
I think this is easier way.

<android.support.design.widget.TabLayout
   android:id="@+id/tabs"
   app:tabTextColor="@color/lightPrimary"
   app:tabSelectedTextColor="@color/white"
   style="@style/CustomTabLayout"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"/>
<style name="CustomTabLayout" parent="Widget.Design.TabLayout">
   <item name="tabMaxWidth">20dp</item>
   <item name="tabMode">scrollable</item>
   <item name="tabIndicatorColor">?attr/colorAccent</item>
   <item name="tabIndicatorHeight">2dp</item>
   <item name="tabPaddingStart">12dp</item>
   <item name="tabPaddingEnd">12dp</item>
   <item name="tabBackground">?attr/selectableItemBackground</item>
   <item name="tabTextAppearance">@style/CustomTabTextAppearance</item>
   <item name="tabSelectedTextColor">?android:textColorPrimary</item>
</style>
<style name="CustomTabTextAppearance" parent="TextAppearance.Design.Tab">
   <item name="android:textSize">16sp</item>
   <item name="android:textStyle">bold</item>
   <item name="android:textColor">?android:textColorSecondary</item>
   <item name="textAllCaps">false</item>
</style>

2

Extensión de Kotlin que funcionó para mí:

fun TabLayout.setFont(font: FontUtils.Fonts) {
    val vg = this.getChildAt(0) as ViewGroup
    for (i: Int in 0..vg.childCount) {
        val vgTab = vg.getChildAt(i) as ViewGroup?
        vgTab?.let {
            for (j: Int in 0..vgTab.childCount) {
                val tab = vgTab.getChildAt(j)
                if (tab is TextView) {
                    tab.typeface = FontUtils.getTypeFaceByFont(FontUtils.Fonts.BOLD, context)
                }
            }
        }
    }
}

1

Mi 2p, Kotlin con verificación de referencia, aplicable en todas partes, ya que se detendrá si algo anda mal.

private fun setTabLayouFont(tabLayout: TabLayout) {
    val viewGroupTabLayout = tabLayout.getChildAt(0) as? ViewGroup?
    (0 until (viewGroupTabLayout?.childCount ?: return))
            .map { viewGroupTabLayout.getChildAt(it) as? ViewGroup? }
            .forEach { viewGroupTabItem ->
                (0 until (viewGroupTabItem?.childCount ?: return))
                        .mapNotNull { viewGroupTabItem.getChildAt(it) as? TextView }
                        .forEach { applyDefaultFontToTextView(it) }
            }
}

1

Y aquí está mi implementación en Kotlin que también permite cambiar la fuente para las pestañas seleccionadas y no seleccionadas.

class FontTabLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    @AttrRes defStyleAttr: Int = 0
) : TabLayout(context, attrs, defStyleAttr) {

    private var textSize = 14f

    private var defaultSelectedPosition = 0

    private var selectedTypeFace: Typeface? = ResourcesCompat.getFont(context, R.font.muli_bold)
    private var normalTypeFace: Typeface? = ResourcesCompat.getFont(context, R.font.muli_regular)

    @ColorInt private var selectedColor = 0
    @ColorInt private var normalTextColor = 0

    init {
        attrs?.let { initAttrs(it) }
        addOnTabSelectedListener()
    }

    private fun initAttrs(attrs: AttributeSet) {
        val a = context.obtainStyledAttributes(attrs, R.styleable.FontTabLayout)

        textSize = a.getDimensionPixelSize(R.styleable.FontTabLayout_textSize, 14).toFloat()

        defaultSelectedPosition = a.getInteger(R.styleable.FontTabLayout_defaultSelectedPosition, 0)
        val selectedResourceId = a.getResourceId(R.styleable.FontTabLayout_selectedTypeFace, R.font.muli_bold)
        val normalResourceId = a.getResourceId(R.styleable.FontTabLayout_normalTypeFace, R.font.muli_regular)

        selectedColor = a.getColor(com.google.android.material.R.styleable.TabLayout_tabSelectedTextColor, 0)
        normalTextColor = a.getColor(R.styleable.FontTabLayout_normalTextColor, 0)

        selectedTypeFace = ResourcesCompat.getFont(context, selectedResourceId)
        normalTypeFace = ResourcesCompat.getFont(context, normalResourceId)

        a.recycle()
    }

    private fun addOnTabSelectedListener() {
        addOnTabSelectedListener(object : OnTabSelectedListenerAdapter() {

            override fun onTabUnselected(tab: Tab?) {
                getCustomViewFromTab(tab)?.apply {
                    setTextColor(normalTextColor)
                    typeface = normalTypeFace
                }
            }

            override fun onTabSelected(tab: Tab?) {

                getCustomViewFromTab(tab)?.apply {
                    setTextColor(selectedColor)
                    typeface = selectedTypeFace
                }
            }

            private fun getCustomViewFromTab(tab: Tab?) = tab?.customView as? AppCompatTextView

        })
    }

    override fun setupWithViewPager(viewPager: ViewPager?, autoRefresh: Boolean) {
        super.setupWithViewPager(viewPager, autoRefresh)
        addViews(viewPager)
    }

    private fun addViews(viewPager: ViewPager?) {
        for (i in 0 until tabCount) {
            val customTabView = getCustomTabView(i).apply {
                typeface = if (i == defaultSelectedPosition) selectedTypeFace else normalTypeFace
                val color = if (i == defaultSelectedPosition) selectedColor else normalTextColor
                setTextColor(color)
                text = viewPager?.adapter?.getPageTitle(i)
            }

            getTabAt(i)?.customView = customTabView
        }
    }

    private fun getCustomTabView(position: Int): AppCompatTextView {
        return AppCompatTextView(context).apply {
            gravity = Gravity.CENTER
            textSize = this@FontTabLayout.textSize
            text = position.toString()
        }
    }
}

en attrs.xml:

<declare-styleable name="FontTabLayout">
    <attr name="normalTextColor" format="reference|color" />
    <attr name="textSize" format="dimension" />
    <attr name="defaultSelectedPosition" format="integer" />
    <attr name="selectedTypeFace" format="reference" />
    <attr name="normalTypeFace" format="reference" />
</declare-styleable>

tabs.getTabAt (1) ?. text no cambia el texto dinámicamente, una vez establecido.
shanraisshan

0

Con las funciones de extensión de kotlin use esto:

 fun TabLayout.setFontSizeAndColor(typeface: Typeface, @DimenRes textSize: Int, @ColorRes textColor: Int) {
val viewGroup: ViewGroup = this.getChildAt(0) as ViewGroup
val tabsCount: Int = viewGroup.childCount
for (j in 0 until tabsCount) {
    val viewGroupTab: ViewGroup = viewGroup.getChildAt(j) as ViewGroup
    val tabChildCount: Int = viewGroupTab.childCount
    for (i in 0 until tabChildCount) {
        val tabViewChild: View = viewGroupTab.getChildAt(i) as View
        if ( tabViewChild is TextView) {
            tabViewChild.typeface = typeface
            tabViewChild.gravity = Gravity.FILL
            tabViewChild.maxLines = 1
            tabViewChild.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.resources.getDimension(textSize))
            tabViewChild.setTextColor(ContextCompat.getColor(this.context, textColor))
        }
    }
}

}


-2

Cambio

if (tabViewChild instanceof TextView) {

para

if (tabViewChild instanceof AppCompatTextView) { 

para que funcione con android.support.design.widget.TabLayout (al menos de com.android.support:design:23.2.0)


4
pero AppCompatTextView extiende TextView, entonces, ¿por qué esto haría una diferencia?
Shirane85
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.