Изменить шрифт текста вкладки в поддержке дизайна Android TabLayout

109

Пытаюсь работать над новинкой TabLayoutиз библиотеки дизайна андроида.

Я хочу изменить текст табуляции на собственный шрифт . И я попытался найти некоторые стили, связанные с этим TabLayout, но в итоге остановился на этом .

Пожалуйста, расскажите, как я могу изменить шрифты текста вкладки.

Дори
источник
3
Use Spannable String
NullByte

Ответы:

37

Создайте TextView из кода Java или XML, подобного этому

<?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"
/>

Обязательно сохраните идентификатор, как он здесь, потому что TabLayout проверяет этот идентификатор, если вы используете настраиваемый текстовый просмотр

Затем из кода надуйте этот макет и установите пользовательский вид Typefaceв этом текстовом представлении и добавьте это настраиваемое представление на вкладку

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
Как я могу установить tabTextColorи tabSelectedTextColorсвойство в этой ситуации?
Алиреза Нурали 01
Я получаю решение от ответа Praveen Sharma
Алиреза Нурали
162

Если вы используете TabLayoutи хотите изменить шрифт, вам нужно добавить новый цикл for к предыдущему решению, например:

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

См. Изменение стиля шрифта на вкладках панели действий с помощью sherlock

Правин Шарма
источник
Я не использую предварительный просмотр M. Есть ли другой способ сделать это.
Dory
1
Вам не нужен предварительный просмотр M, он доступен для всех, кто используетTabLayout
Марио Веласко
Однако я обнаружил исключение NullPointerException на android.view.View android.support.design.widget.TabLayout.getChildAt (int), вы можете мне помочь, как это исправить? Не могу найти то, что мне не хватает в моем коде.
brettbrdls
1
Первый параметр setTypeFace- это a TypeFace, на случай, если вы не можете найти Fontкласс (который, похоже, не существует для меня)
Вахид Амири
105

Создайте свой собственный стиль и используйте родительский стиль как parent="@android:style/TextAppearance.Widget.TabWidget"

И в макете вкладки используйте этот стиль как app:tabTextAppearance="@style/tab_text"

Пример: Стиль:

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

Пример: компонент макета вкладки:

<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
Это правильно, я использую parent="TextAppearance.Design.Tab"в своем случае.
Джаватар
1
Это работает намного лучше, чем первые ответы, и без черной магии (скрытый API), которая может что-то сломать в будущем
Sulfkain
2
просто работать при использовании fontFamily, не работать при использовании fontPath
Tram Nguyen
2
При использовании у меня беспорядочно возникали странные исключения TextAppearance.Widget.TabWidget. Ответ @Javatar исправил это для меня.
funct7
48

Отличный ответ от правеен Шарма. Небольшое дополнение: вместо того, чтобы использовать changeTabsFont()везде, где вам нужно TabLayout, вы можете просто использовать свои собственные 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);
            }
        }
    }

}

И вот еще. TabViewэто LinearLayoutс TextViewвнутренней стороны (это может также необязательно содержать ImageView). Так можно сделать код еще проще:

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

Но я бы не рекомендовал такой способ. Если TabLayoutреализация изменится, этот код может работать некорректно или даже дать сбой.

Другой способ настройки TabLayout- добавление к нему пользовательского представления. Вот отличный пример .

Андрей Ауласка
источник
addTab не будет вызываться, если вы установите следующие вкладки: mViewPager.setAdapter (new YourPagerAdapter (getChildFragmentManager ())); mTabLayout.setupWithViewPager (mViewPager);
Penzzz
2
@Penzzz, ты абсолютно прав. В этом случае вам следует переместить код из метода addTab в другой. Например onMeasure.
Андрей Ауласка 04
@AndreiAulaska thnx Ты спас мне день. Твоя последняя ссылка спасет меня.
Amol Dale
Я думаю, что в последней версии это больше не работает. Ответ @ejw теперь работает. нужно добавить его в addTab (вкладка Tab, логическое setSelected)
Sayem
3
Это хорошо работает. ПРИМЕЧАНИЕ: начиная с библиотеки поддержки 25.x вам нужно переопределить addTab(Tab tab, int position, boolean setSelected)вместо addTab(Tab tab).
Vicky Chijwani
20

Чтобы использовать поддержку шрифтов в XMLфункции на работающих устройствах Android 4.1(уровень API 16) и выше, используйте библиотеку поддержки 26+.

  1. Щелкните правой кнопкой мыши папку res
  2. Создать -> Каталог ресурсов Android -> выбрать шрифт -> ОК
  3. Поместите myfont.ttfфайл во вновь созданную папку шрифтов

При res/values/styles.xmlдобавлении:

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

В файл макета добавьте приложение: 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" />

См. [Шрифты в xml]. ( Https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml )

Томас VJ
источник
он работает с формами xamarin и xamarin android. спасибо,
Эсмаил Джамшидиасл
Отличный ответ! Следует расширить "TextAppearance.Design.Tab", хотя
XY
16

Следующий метод полностью ViewGroupрекурсивно изменит шрифт . Я выбрал этот метод, потому что вам не нужно заботиться о внутренней структуре TabLayout. Я использую библиотеку каллиграфии, чтобы установить шрифт.

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
Проблема в том, что если вы прикрепите макет к Viewpager, вы потеряете настроенные шрифты.
rupps
10

Для поддержки дизайна 23.2.0, используя setupWithViewPager, вам придется переместить код из addTab (вкладка Tab) в addTab (вкладка Tab, логическое setSelected).

ejw
источник
8

Вы можете использовать это, у меня это работает.

 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

Что ж, я нашел это просто в 23.4.0 без использования цикла. Просто переопределите addTab (вкладка @NonNull Tab, логическое setSelected), как предлагает @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);
}

А вот и 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" />

Надеюсь, это поможет :)

Ельсеннов
источник
1
Но как установить tabSelectedTextColor и tabTextColo?
Мостафа Имран,
@MostafaImran, ваша версия android:textColor="@color/graylove"должна иметь селектор списка состояний для этого с указанным цветом
state_selected
7

Как ответил Андрей , вы можете изменить шрифт, расширив класс TabLayout . И как сказал Пенззз , вы не можете сделать это в методе addTab . Переопределите метод onLayout, как показано ниже:

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

Необходимо перезаписать метод onLayout, потому что, когда вы используете метод setupWithViewPager для привязки TabLayout к ViewPager, вам необходимо установить текст вкладок либо с помощью метода setText, либо в PagerAdapter после этого, и когда это произойдет, метод onLayout будет вызван в родительской ViewGroup ( TabLayout), и это место для установки шрифта настройки. (Изменение текста TextView вызывает вызов метода onLayout его родителя - у tabView есть два дочерних элемента, один - ImageView, другой - TextView)

Другое решение:

Во-первых, эти строки кода:

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

В приведенном выше решении следует писать вне двух циклов.

Но лучшее решение для API> = 16 использует android: fontFamily :

Создайте каталог ресурсов Android с именем font и скопируйте в него нужный шрифт.

Затем используйте эти стили:

<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>
Сыпь
источник
Это плохое исправление производительности, которое вызывается onLayout()при каждом изменении макета, например при переключении вкладок или даже при прокрутке списка под вкладками, при этом вложенные fors во многих вкладках TabLayoutприложение будут задерживать.
Амр Баракат
2
@ Амр Баракат. Согласно этой ссылке: developer.android.com/reference/android/view/… , int, int, int, int) это неверно. Я тоже это тестировал. Я поставил точку останова в методе onLayout, и он вызывается , когда вкладки создаются, а не при переключении вкладок или прокрутке списка.
Араш,
1
У меня onLayout()действительно вызывается несколько раз при переключении вкладок (не знаю, почему именно), но чтобы учесть это, я устанавливаю шрифты только тогда, когда boolean changedистинно. Это предотвращает установку шрифтов несколько раз.
Роберт
отличное решение, код не требуется, только атрибуты xml
осторожно7j
3

Мой метод Resolve точно так же, измените текст вкладки Указанный,

 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>
Sudhakara Ks
источник
2

Расширение Kotlin, которое сработало для меня:

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

Мой 2p, Kotlin с проверкой ссылок, применим везде, так как остановится, если что-то пойдет не так.

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

А вот моя реализация на Kotlin, которая также позволяет изменять шрифт для выбранных и невыделенных вкладок.

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

в 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 не изменяет текст динамически после установки.
shanraisshan
0

С функциями расширения kotlin используйте это:

 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

+ Изменить

if (tabViewChild instanceof TextView) {

для

if (tabViewChild instanceof AppCompatTextView) { 

чтобы он работал с android.support.design.widget.TabLayout (по крайней мере, из com.android.support:design:23.2.0)

Penzzz
источник
4
но AppCompatTextView расширяет TextView, так почему это имеет значение?
Shirane85