Alterar a fonte do texto da guia no suporte ao design do Android TabLayout

109

Estou tentando trabalhar no novo TabLayoutda biblioteca de design do Android.

Quero alterar o texto da guia para uma fonte personalizada . E, tentei pesquisar algum estilo relacionado a TabLayout, mas acabei por isso .

Por favor, oriente como posso alterar as fontes do texto da guia.

Dory
fonte
3
Use Spannable String
NullByte

Respostas:

37

Crie um TextView a partir de código Java ou 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"
/>

Certifique-se de manter o id como está aqui porque o TabLayout verifica este ID se você usar textview personalizado

Então, a partir do código, aumente este layout e defina o custom Typefacenaquela textview e adicione esta view customizada à guia

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
fonte
2
Como posso definir tabTextColore tabSelectedTextColorpropriedade nesta situação?
Alireza Noorali
Obtive
162

Se você estiver usando TabLayoute quiser alterar a fonte, será necessário adicionar um novo loop for à solução anterior, como este:

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);
                }
        }
    }
} 

Por favor, consulte a alteração do estilo da fonte nas guias da barra de ação usando sherlock

Praveen Sharma
fonte
Não estou usando a visualização M. Existe alguma outra maneira de fazer isso.
Dory
1
Você não precisa de visualização do M, é válido para todos os usuáriosTabLayout
Mario Velasco
No entanto, estou encontrando um NullPointerException em android.view.View android.support.design.widget.TabLayout.getChildAt (int). Você pode me ajudar a consertá-lo? Não consigo encontrar o que estou perdendo no meu código.
brettbrdls
1
O primeiro parâmetro de setTypeFaceé a TypeFace, no caso de você não conseguir encontrar a Fontclasse (que parece não existir para mim)
Vahid Amiri
104

Crie seu próprio estilo personalizado e use o estilo pai como parent="@android:style/TextAppearance.Widget.TabWidget"

E em seu layout de guia, use este estilo como app:tabTextAppearance="@style/tab_text"

Exemplo: Estilo:

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

Exemplo: componente de layout de guia:

<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
fonte
9
Correto, estou usando parent="TextAppearance.Design.Tab"no meu caso.
Javatar
1
Isso funciona muito melhor do que as primeiras respostas e sem magia negra (API escondida), que poderia quebrar algo no futuro
Sulfkain
2
funciona apenas quando usar fontFamily, não funciona quando usa fontPath
Tram Nguyen
2
Eu estava recebendo exceções estranhas erraticamente ao usar TextAppearance.Widget.TabWidget. A resposta de @Javatar consertou para mim.
funct7 de
47

Ótima resposta de Praveen Sharma. Apenas um pequeno acréscimo: em vez de usar changeTabsFont()onde você precisar TabLayout, você pode simplesmente usar o seu próprio 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);
            }
        }
    }

}

E mais uma coisa. TabViewé um LinearLayoutcom TextViewdentro (também pode conter opcionalmente ImageView). Portanto, você pode tornar o código ainda mais simples:

@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);
}

Mas eu não recomendaria assim. Se a TabLayoutimplementação mudar, este código pode funcionar incorretamente ou até mesmo travar.

Outra maneira de personalizar TabLayouté adicionar uma exibição personalizada a ele. Aqui está o grande exemplo .

Andrei Aulaska
fonte
addTab não invocará caso você defina guias como esta: mViewPager.setAdapter (new YourPagerAdapter (getChildFragmentManager ())); mTabLayout.setupWithViewPager (mViewPager);
Penzzz
2
@Penzzz você está absolutamente certo. Nesse caso, você deve mover o código do método addTab para outro. Por exemplo, onMeasure.
Andrei Aulaska
@AndreiAulaska thnx Você salva o meu dia. Seu último Link me salve.
Amol Dale
isso não funciona mais, acho que na versão mais recente. A resposta de @ejw agora está funcionando. precisa adicioná-lo em addTab (guia, boolean setSelected)
Sayem
3
Isso funciona bem. NOTA: a partir da biblioteca de suporte 25.x, você precisa substituir em addTab(Tab tab, int position, boolean setSelected)vez de addTab(Tab tab).
Vicky Chijwani
20

Para usar o suporte a fontes no XMLrecurso em dispositivos em execução Android 4.1(API nível 16) e superior, use a Biblioteca de Suporte 26+

  1. Clique com o botão direito na pasta res
  2. Novo -> diretório de recursos do Android-> selecionar fonte -> Ok
  3. Coloque seu myfont.ttfarquivo na pasta de fontes recém-criada

Ao res/values/styles.xmladicionar:

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

No arquivo de layout, adicione app: 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 [fontes em xml]. ( Https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml )

Thomas VJ
fonte
funciona em formulários xamarin e xamarin android.thanks
Esmail Jamshidiasl
Ótima resposta! Deve estender "TextAppearance.Design.Tab"
XY
16

O método a seguir mudará a fonte inteira ViewGrouprecursivamente. Escolhi esse método porque você não precisa se preocupar com a estrutura interna do TabLayout. Estou usando a biblioteca de caligrafia para definir uma fonte.

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);
        }
    }
}
Egis
fonte
1
O problema com isso é que, se você anexar o layout a um Viewpager, perderá as fontes personalizadas.
rupps de
10

Para suporte de design 23.2.0, usando setupWithViewPager, você terá que mover o código de addTab (guia Tab) para addTab (guia Tab, boolean setSelected).

ejw
fonte
8

Você pode usar isso, funciona para mim.

 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
fonte
7

Bem, eu achei simples em 23.4.0 sem usar um loop. Basta substituir addTab (guia @NonNull Tab, boolean setSelected) conforme sugerido por @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);
}

E aqui está o 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 possa ajudar :)

Elsennov
fonte
1
Mas como definir tabSelectedTextColor e tabTextColo?
Mostafa Imran
@MostafaImran sua versão de android:textColor="@color/graylove"deve ter o seletor de lista de estados para aquela com a cor selecionada em estado especificada
AA_PV
7

Como Andrei respondeu, você pode alterar a face da fonte estendendo a classe TabLayout . E como disse Penzzz , você não pode fazer isso no método addTab . Substitua o método onLayout conforme abaixo:

@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);
            }
        }
    }
}

Deve substituir o método onLayout, porque, quando você usa o método setupWithViewPager para vincular o TabLayout ao ViewPager, você deve definir o texto das guias com o método setText ou no PagerAdapter depois disso e quando isso aconteceu, o método onLayout é chamado no ViewGroup pai ( TabLayout) e esse é o lugar para colocar a configuração de fontface. (Alterar um texto TextView causa a chamada do método onLayout de seu pai - um tabView tem dois filhos, um é ImageView e outro é TextView)

Outra solução:

Primeiro, essas linhas de código:

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

Na solução acima, deve ser escrito fora de dois loops.

Mas a melhor solução para API> = 16 é usar android: fontFamily :

Crie um diretório de recursos do Android denominado font e copie a fonte desejada para o diretório.

Em seguida, use estes 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
fonte
Esta é uma correção de desempenho ruim, onLayout()chamada com cada mudança de layout, como troca de guias ou até rolagem de lista abaixo das guias, com fors aninhados em um TabLayoutaplicativo de muitas guias ficarão com lag.
Amr Barakat
2
@Amr Barakat. De acordo com este link: developer.android.com/reference/android/view/… , int, int, int, int), isso não é verdade. Eu testei também. Eu coloquei um ponto de interrupção no método onLayout e ele é chamado quando as guias estão sendo criadas, não na alternância de guias ou na rolagem de lista.
Arash
1
Para mim, onLayout()é chamado várias vezes ao alternar entre guias (não tenho certeza do porquê exatamente), mas para compensar, eu só defino as fontes quando o boolean changedé verdadeiro. Isso evita definir as fontes várias vezes.
Robert
ótima solução, nenhum código necessário, apenas atributos xml
cuidado7j
3

Meu método Resolve assim, altere o texto da guia especificada,

 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);
 }
Macaco smish
fonte
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
fonte
2

Extensão Kotlin que funcionou para mim:

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
fonte
1

Meu 2p, Kotlin com verificação de referência, aplicável em qualquer lugar, pois irá parar se algo estiver errado.

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
fonte
1

E aqui está minha implementação em Kotlin que também permite alterar a fonte das guias selecionadas e não selecionadas.

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()
        }
    }
}

em 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
fonte
tabs.getTabAt (1) ?. text não altera o texto dinamicamente, uma vez definido.
Shanraisshan
0

Com as funções de extensão kotlin, use o seguinte:

 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
fonte
-2

mudança

if (tabViewChild instanceof TextView) {

para

if (tabViewChild instanceof AppCompatTextView) { 

para fazê-lo funcionar com android.support.design.widget.TabLayout (pelo menos de com.android.support:design:23.2.0)

Penzzz
fonte
4
mas AppCompatTextView estende TextView, então por que isso faria a diferença?
Shirane85