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.

Gallo
fuente
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);
}
Farmaan Elahi
fuente
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

Praveen Sharma
fuente
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" />
Milind Mevada
fuente
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 .

Andrei Aulaska
fuente
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 )

Thomas VJ
fuente
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);
        }
    }
}
Égida
fuente
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).

ejw
fuente
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);
            }
        }
    }
}
pavel
fuente
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 :)

Elsennov
fuente
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>
Arash
fuente
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);
 }
Smish jack
fuente
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>
sudhakara ks
fuente
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)
                }
            }
        }
    }
}
Michał Dobi Dobrzański
fuente
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) }
            }
}
Rafael Ruiz Muñoz
fuente
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>
Yvgen
fuente
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))
        }
    }
}

}

Kyriakos Georgiopoulos
fuente
-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)

Penzzz
fuente
4
pero AppCompatTextView extiende TextView, entonces, ¿por qué esto haría una diferencia?
Shirane85