Добавьте значки в SlidingTabLayout вместо текста

Я реализую SlidingTabLayout в своем приложении для Android. Мой запрос заключается в том, что я хотел бы реализовать значки на своих скользящих вкладках вместо текстов для навигации. Я много искал в Интернете любой такой учебник или образец, но ничего не нашел. Я также искал предыдущий вопрос в stackoverflow: Здесь - SlidingTabLayout со значками. Это было немного информативно, но на самом деле не помогло мне.

Чтобы было ясно. Я требую, чтобы мои вкладки состояли из только значков. Нет текста.

Поскольку я новичок во всей этой настройке, я был бы признателен, если бы вы опубликовали правильный код с объяснением. Спасибо за ваше время и усилия!

P.S. Я также слышал о полосе pagerslidingtabstrip от Андреаза Штуца и подумал, не подойдет ли она больше для того типа задач, которыми я занимаюсь...

Также: вот как я хотел бы, чтобы мои скользящие вкладки выглядели. Посмотрите на верхнюю часть этого изображения.

РЕДАКТИРОВАТЬ: ТЕПЕРЬ ВЫШЕЛ ЛОЛЛИПОП (С МАТЕРИАЛЬНЫМ ДИЗАЙНОМ). Я ВИДЕЛ МНОЖЕСТВО ПРИЛОЖЕНИЙ, ИСПОЛЬЗУЮЩИХ НОВЫЙ «ТОЛЬКО ЗНАЧОК» SLIDING-TAB-LAYOUT ПОД ПАНЕЛЬЮ ИНСТРУМЕНТОВ (ПАНЕЛЬ ДЕЙСТВИЙ ANDROID 5.0). ЕСТЬ ЛИ ИХ НОВЫЙ СПОСОБ ЭТОГО РЕАЛИЗОВАТЬ?? ЕЩЕ РАЗ СПАСИБО!

Видите скользящие вкладки вверху?


person Adifyr    schedule 05.05.2014    source источник
comment
Проверьте этот mkyong.com/android/android-tablayout-example.   -  person Zohra Khan    schedule 05.05.2014
comment
stackoverflow.com/questions/28125794/   -  person Daniel    schedule 25.01.2015
comment
Мне это было нужно, поэтому я немного изменил коды SlidingTabLayout, чтобы упростить использование значков для вкладок github.com/kimkevin /SlidingIconTabLayout   -  person kimkevin    schedule 13.07.2015


Ответы (11)


Ключом к этому является возврат SpannableString, содержащего ваш значок в ImageSpan, из метода getPageTitle(position) вашего PagerAdapter:

private int[] imageResId = {
        R.drawable.ic_tab_notifications,
        R.drawable.ic_tab_weather,
        R.drawable.ic_tab_calendar
};

@Override
public CharSequence getPageTitle(int position) {
    Drawable image = getResources().getDrawable(imageResId[position]);
    image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
    SpannableString sb = new SpannableString(" ");
    ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
    sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    return sb;
}

Обычно этого было бы достаточно, но вкладка по умолчанию, созданная SlidingTabLayout, делает вызов TextView#setAllCaps(true), который эффективно отключает все ImageSpans, поэтому вместо этого вам придется использовать пользовательское представление вкладки:

рез/макет/custom_tab.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="12sp"
    android:textStyle="bold"
    android:background="?android:selectableItemBackground"
    android:padding="16dp"
    />

и где бы вы ни настраивали/привязывали свой ViewPager:

SlidingTabLayout slidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
slidingTabLayout.setCustomTabView(R.layout.custom_tab, 0);
slidingTabLayout.setViewPager(viewPager);

(не забудьте позвонить setCustomTabView перед setViewPager)

person Jeremy Dowdall    schedule 27.05.2014
comment
Можем ли мы сделать SlidingTabLayout, который должен заполнять ширину его вкладок и делать их равными частями?. - person Vikalp Patel; 11.07.2014
comment
@jeremy Как установить другое изображение для выбранной вкладки? - person Divya; 25.07.2014
comment
@VikalpPatel, использующий веса для ширины вашей пользовательской вкладки, должен помочь: замените android:layout_width="wrap_content" на android:layout_width="0dp" и android:layout_weight="1" - person Jeremy Dowdall; 25.08.2014
comment
@Divya настраивает то, что вы возвращаете из getPageTitle, в зависимости от того, является ли позиция выбранной в данный момент или нет. ViewPager.getCurrentItem() — это один из способов узнать, какая позиция выбрана. - person Jeremy Dowdall; 25.08.2014
comment
Это работает отлично! Можете ли вы просто сказать мне, как я могу изменить рисунок выбранной вкладки? Я хочу, чтобы рисунки моего выбора были другого цвета, чем невыбранные... +1 - person Sandra; 16.10.2014
comment
Я попытался установить селектор для ImageSpan, но это не сработало. Кто-нибудь знает решение этой проблемы? - person Sandra; 16.10.2014
comment
Мне удалось сделать это, изменив класс SlidingTabLayout. Изменения, которые я сделал, заключались в том, чтобы этот класс принимал ImageViews для вкладок. В качестве вдохновения я использовал библиотеку ViewPagerAddons, упомянутую в ответе Манаса Баджаджа. Если это слишком сложно для кого-то из вас, вы можете просто использовать библиотеку в своем проекте. - person Sandra; 16.10.2014
comment
Как поставить иконку по центру. - person Codelord; 29.12.2014
comment
@CodeLord, если настраиваемая вкладка больше, чем ее содержимое, то должно работать использование атрибута гравитации: android:gravity="center" (обратите внимание, что это не атрибут android:layout_gravity) - person Jeremy Dowdall; 30.12.2014
comment
@jeremy, используя гравитацию для решения проблемы, перед гравитацией я должен установить ее ширину, чтобы она соответствовала родителю. Затем значок помещается в центр. - person Codelord; 31.12.2014
comment
@JeremyDowdall Есть ли способ использовать это при получении изображений из Glide, а не файлов для рисования? - person Shivam Bhalla; 30.10.2015

Я решил ту же проблему, но также с изменением рисунка на выбранной вкладке.

Создайте свои чертежи для вкладок с двумя состояниями. first_tab_drawable.xml, second_tab_drawable.xml, Third_tab_drawable.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_selected" android:state_selected="true"/>
    <item android:drawable="@drawable/ic_normal"/>
</selector>

Создайте свой собственный адаптер пейджера, наследуемый от PagerAdapter:

public class MyPagerAdapter extends PagerAdapter {

    private int[] drawablesIds = {
        R.drawable.first_tab_drawable,
        R.drawable.second_tab_drawable,
        R.drawable.third_tab_drawable
    };

    //Constructor and other standard funcs...

    public int getDrawableId(int position){
        //Here is only example for getting tab drawables
        return drawablesIds[position];
    }
    //...
}

Изменить код SlidingTabLayout:

private void populateTabStrip() {
    //Here is no more standard PagerAdapter!
    final MyPagerAdapter adapter = (MyPagerAdapter) mViewPager.getAdapter();

    final View.OnClickListener tabClickListener = new TabClickListener();

    for (int i = 0; i < adapter.getCount(); i++) {
        View tabView = null;
        TextView tabTitleView = null;

        if (mTabViewLayoutId != 0) {
            // If there is a custom tab view layout id set, try and inflate it
            tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                    false);
            tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
        }

        if (tabView == null) {
            tabView = createDefaultTabView(getContext());
        }

        if (tabTitleView == null && TextView.class.isInstance(tabView)) {
            tabTitleView = (TextView) tabView;
        }

        //Set icon, that can be changeable instead setting text.
        //I think the text also can setting here from getPageTitle func.
        //But we interesting only in icon
        tabTitleView.setCompoundDrawablesWithIntrinsicBounds(adapter.getDrawableId(i), 0, 0, 0);
        //Select tab if it is current
        if (mViewPager.getCurrentItem() == i){
            tabView.setSelected(true);
        }
        tabView.setOnClickListener(tabClickListener);

        mTabStrip.addView(tabView);
    }
}

И сделайте действительно выбранный заголовок TextView также в SlidingTabLayout в InternalViewPagerListener:

    @Override
    public void onPageSelected(int position) {

        //Clear old selection and make new
        for(int i = 0; i < mTabStrip.getChildCount(); i ++){
            mTabStrip.getChildAt(i).setSelected(false);
        }
        mTabStrip.getChildAt(position).setSelected(true);

        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
            mTabStrip.onViewPagerPageChanged(position, 0f);
            scrollToTab(position, 0);
        }

        if (mViewPagerPageChangeListener != null) {
            mViewPagerPageChangeListener.onPageSelected(position);
        }
    }

Надеюсь, это будет полезно для кого-то.

person v1k    schedule 14.11.2014
comment
Как бы вы центрировали значок, если ширина вкладки составляет 50% от ширины SlidingTabLayout. Нужно ли мне создавать собственное представление? - person Daniel; 24.01.2015
comment
Я не центрировал. Вкладки в моем случае начинаются слева, и если они занимают 50%, они не будут заполнять всю ширину. - person v1k; 24.01.2015
comment
@Daniel Попробуйте добавить android:layout_weight=1 в свой собственный макет вкладок. - person v1k; 16.04.2015
comment
Я уже решил это. stackoverflow.com/questions/28125794/ Спасибо, в любом случае! - person Daniel; 16.04.2015
comment
привет @v1k, твой код идеален, но в нем есть ошибка. Впервые, когда вы прокручиваете между вкладками, первая и вторая вкладки показывают выбранное состояние. Это поведение исчезает после переключения со второй на первую вкладку. Причина такого поведения в том, что вы использовали tabTitleView.setSelected(true); в своем populateTabStrip(), что вызывает такое поведение. Если вы измените его на tabView.setSelected(true);, тогда он будет работать нормально. Я редактирую ваш код, чтобы отразить это. - person Rohan Kandwal; 11.06.2015
comment
Большое спасибо, я не наблюдал эту проблему, в моем случае я выбрал вторую вкладку после инициализации. - person v1k; 11.06.2015

Чтобы настроить SlidingTabLayout так, как вы хотите, вам нужно всего лишь изменить метод populateTabStrip():

public void populateTabStrip() {
        final PagerAdapter adapter = mViewPager.getAdapter();
        final View.OnClickListener tabClickListener = new TabClickListener();

        for (int i = 0; i < adapter.getCount(); i++) {
            View tabView = null;

            tabView = LayoutInflater.from(getContext()).inflate(R.layout.tab_layout, mTabStrip,
                    false);

            ImageView iconImageView = (ImageView) tabView.findViewById(R.id.tab_layout_icon);
            iconImageView.setImageDrawable(getContext().getResources().getDrawable(getIconResourceArray()[i]));

            tabView.setOnClickListener(tabClickListener);

            mTabStrip.addView(tabView);
        }
    }

Ваш макет может быть примерно таким:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="75dp"
    android:paddingTop="15dp"
    android:layout_height="50dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/tab_layout_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_gravity="center" />
</LinearLayout>

Способ реализации метода getResourceArray() зависит от вас. Вот как я это сделал:

public class IconSlidingTabLayout extends HorizontalScrollView {
    private Integer[] mIconResourceArray;

    ...

    public Integer[] getIconResourceArray() {
        return mIconResourceArray;
    }

    public void setIconResourceArray(Integer[] mIconResourceArray) {
        this.mIconResourceArray = mIconResourceArray;
    }
}

В деятельности:

mSlidingTabLayout = (IconSlidingTabLayout) findViewById(R.id.icon_sliding_tab_layout);
Integer[] iconResourceArray = { R.drawable.news_tab_icon,
        R.drawable.challenges_tab_icon, R.drawable.trophies_tab_icon,
        R.drawable.leaderboard_tab_icon };
mSlidingTabLayout.setIconResourceArray(iconResourceArray);

Имейте в виду, что для доступа к R.layout.tab_layout* вам необходимо импортировать yourpackage.R вместо android.R, как это делается по умолчанию в SlidingTabStrip.

person Samuel    schedule 14.11.2014

Хорошо... есть много способов реализовать это изменение. Это самый быстрый и грязный, просто для идеи.

Замените метод SlidingTabLayout.populateTabStrip() на этот..

    private void populateTabStrip() {
    final OnClickListener tabClickListener = new TabClickListener();

    View tabView = LayoutInflater.from(getContext()).inflate(R.layout.tab_notif_icon_only, mTabStrip, false);
    tabView.setOnClickListener(tabClickListener);
    mTabStrip.addView(tabView);

    tabView = LayoutInflater.from(getContext()).inflate(R.layout.tab_weather_icon_only, mTabStrip, false);
    tabView.setOnClickListener(tabClickListener);
    mTabStrip.addView(tabView);

    tabView = LayoutInflater.from(getContext()).inflate(R.layout.tab_calendar_icon_only, mTabStrip, false);
    tabView.setOnClickListener(tabClickListener);
    mTabStrip.addView(tabView);

Создайте каждый макет таким образом LinearLayout > ImageView elements, src указывающий на icon..

person Tom    schedule 12.06.2014
comment
Привет. Можете ли вы сказать мне, что такое mtabstrip и для чего нужны адаптер и onclicklistener? - person Adifyr; 12.06.2014
comment
Часть оригинальной реализации SlidingTabLayout. Слушатель используется для добавления функций onclick на вкладки, и нет необходимости в адаптере, поскольку вы не хотите устанавливать заголовок страницы на вкладках. - person Tom; 12.06.2014

Ваш адаптер должен реализовывать PagerSlidingTabStrip.IconTabProvider.

Затем в getPageIconResIdвы можете вернуть идентификатор ресурса изображения:

public class ViewPagerAdapter extends FragmentPagerAdapter implements 
PagerSlidingTabStrip.IconTabProvider {

   private static final int[] icons = {R.drawable.image1, R.drawable.image2, R.drawable.image3};

    @Override
    public int getPageIconResId(int i) {
        return icons[i];
    }
}

Имейте в виду, что будет показано только изображение, а не текст.

person klimat    schedule 06.02.2015

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

Во-первых, я определил два массива внутри класса SlidingTabLayout (это можно легко передать другому классу):

private int[] imageResId = {
        R.drawable.multi_random,
        R.drawable.single_random,
        R.drawable.known_person,
};

private int[] imageResSelected = {
        R.drawable.multi_random_selected,
        R.drawable.single_random_selected,
        R.drawable.known_person_selected
};

private int mOldPosition = 0;

Теперь мы изменим метод populateTabStrip() внутри SlidingTabLayout:

 for (int i = 0; i < adapter.getCount(); i++) {
            View tabView = null;
            ImageView tabImageView = null;

            if (mTabViewLayoutId != 0) { // HAS TO BE SET FOR THE MOMENT!
                // If there is a custom tab view layout id set, try and inflate it
                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                        false);
                tabImageView = (ImageView) tabView.findViewById(mTabViewTextViewId);
            }

            if (tabView == null) {
                tabView = createDefaultTabView(getContext());
            }

            if (tabImageView == null && ImageView.class.isInstance(tabView)) {
                tabImageView = (ImageView) tabView;
            }

            int resourceId;
            if (i == mViewPager.getCurrentItem()) {
                resourceId = imageResSelected[i];
                mOldPosition = i;
            } else {
                resourceId = imageResId[i];
            }
            tabImageView.setImageResource(resourceId);
            tabView.setOnClickListener(tabClickListener);

            mTabStrip.addView(tabView);
        }

Чтобы обновить изображение в соответствии с выбранной вкладкой, мы добавляем несколько строк в метод onPageSelected.

// This Method is called once the transition is finished
// Change the drawable of the old one..
ImageView view = (ImageView) mTabStrip.getChildAt(mOldPosition);
view.setImageResource(imageResId[mOldPosition]);

// And now change it of the current one
view = (ImageView) mTabStrip.getChildAt(position);
view.setImageResource(imageResSelected[position]);

mOldPosition = position;

Наконец, нам нужно добавить собственное представление вкладок:

mSlidingTabLayout.setCustomTabView(R.layout.custom_tab, 0);

Как этот

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:padding="16dp"
          android:layout_weight="1"
          android:src="@drawable/multi_random"
        />

Это решение далеко от совершенства, но работает для моих нужд. Может быть, я мог бы помочь кому-то с этим.

person puelo    schedule 01.11.2014
comment
как добавить два текстовых представления в вертикальном порядке - person Erum; 07.08.2015

Недавно я реализовал эту библиотеку под названием ViewPagerAddons. Он включает в себя SlidingTabLayoutColors, это именно то, что вам нужно. Просто позвольте вашему адаптеру пейджера реализовать интерфейс SlidingTabLayouColors.ImageProvider, переопределить метод getPageImage и вернуть идентификаторы ресурсов изображения в соответствии с позициями страницы. Простой.

Вот ссылка на библиотеку (включая инструкции по настройке): https://bitbucket.org/энтузиаст94/viewpageraddons/src

person Manas Bajaj    schedule 03.09.2014

Я последовал ответу Джереми, и он отлично работает. AFAIK нет нового способа сделать это. Большинство приложений теперь полностью удаляют панель инструментов и добавляют пользовательскую скользящую вкладку, рекомендованную Google.

Если у вас есть какие-либо конкретные вопросы, дайте мне знать. iv получил мое приложение, очень похожее на скриншот, который вы опубликовали. Я чувствую, что это обновление материального дизайна было больше сосредоточено на включении пользовательских анимаций и эффектов.

person Varun Agarwal    schedule 15.08.2015

Просто возврат null из метода getPageTitle() сделал это для меня:

@Override
 public CharSequence getPageTitle(int position) {
    return null;
}
person Ojonugwa Jude Ochalifu    schedule 31.12.2015


Расширьте свою основную активность из FragmentActivity. Также реализуйте этот класс из ActionBar.TabListener, поскольку мы добавляем вкладки.

// Для добавления вкладок

for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setIcon(R.drawable.drawable_id)
.setTabListener(this));
}

Проверьте здесь.

person Zohra Khan    schedule 05.05.2014