Add Icons to SlidingTabLayout instead of Text
Asked Answered
K

11

19

I'm implementing a SlidingTabLayout in my android application. What my query is that I'd like to implement icons in my Sliding Tabs instead of texts for navigation. I searched heavily on the internet for any such tutorial or sample but found none. I also searched a previous question on stackoverflow: Over Here - SlidingTabLayout with Icons. It was slightly informative but didn't really help me out.

To be clear. I require my tabs to consist of icons only. No text.

As I am new to this whole setup, I'd appreciate if you'd post proper code with an explanation. Thank you for your time and effort!

P.S. I also heard about the pagerslidingtabstrip by Andreaz Stuetz and wondered if that would be more suitable for the type of thing I'm going for...

Also: Here is what I would like my sliding tabs to look like. Check out the top of this image.

EDIT : NOW THAT LOLLIPOP (WITH MATERIAL DESIGN) HAS COME OUT. I HAVE SEEN A LOT OF APPS USING A NEW "ONLY ICON" SLIDING-TAB-LAYOUT BELOW THEIR TOOLBAR (ANDROID 5.0 ACTION-BAR). IS THEIR ANY NEW WAY TO IMPLEMENT THIS?? THANKS ONCE AGAIN!

See the Sliding Tabs on the top?

Kinelski answered 5/5, 2014 at 12:42 Comment(3)
Check this mkyong.com/android/android-tablayout-exampleBuster
#28126294Breann
I needed it, so I changed codes of SlidingTabLayout little bit to make it easy to use icon for tabs github.com/kimkevin/SlidingIconTabLayoutCicatrix
F
41

The key to this is to return a SpannableString, containing your icon in an ImageSpan, from your PagerAdapter's getPageTitle(position) method:

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

Normally this be would be enough, but the default tab created by SlidingTabLayout makes a call to TextView#setAllCaps(true) which effectively disables all ImageSpans, so you'll have to use a custom tab view instead:

res/layout/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"
    />

and where ever you setup/bind to your ViewPager:

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

(make sure to call setCustomTabView before setViewPager)

Florin answered 27/5, 2014 at 15:50 Comment(11)
Can we make SlidingTabLayout which should fill width of it tabs and make them equally parts?.Bharat
@jeremy How i set different image for selected tab?Banas
@VikalpPatel using weights for the width of your custom tab should do the trick: replace android:layout_width="wrap_content" with android:layout_width="0dp" and android:layout_weight="1"Florin
@Banas adjust what you return from getPageTitle based on whether the position is the currently selected one or not. ViewPager.getCurrentItem() is one way of finding out which position is selected.Florin
This works perfectly! Can u just tell me how can I change the drawable of the selected tab? I want the drawables of my selections to be other color than the unselected ones... +1Abuttal
I tried setting a selector drawable for the ImageSpan but that did not work.. Does anyone know a solution for this problem?Abuttal
I managed to do this by modifying the SlidingTabLayout class. The changes that I made were for this class to accept ImageViews for the tabs. As inspiration I used the ViewPagerAddons library, mentioned in the answer by Manas Bajaj. If it is too much trouble for any of you, you can just use the library in your project.Abuttal
How to set icon at the center.Greyhen
@CodeLord if the custom tab is larger than its content, then using the gravity attribute should work: android:gravity="center" (note that this is not the android:layout_gravity attribute)Florin
@jeremy, using gravity to solve the problem, before gravity I have to set its width to match parent. Then icon is placed at the center.Greyhen
@JeremyDowdall Is there any way use this while getting images from Glide rather than drawable files ?Durr
J
6

I solved same problem, but also with changing drawable on selected tab.

Create your drawables for tabs, with two states. 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>

Create your own pager adapter, extends from 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];
    }
    //...
}

Change code of 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);
    }
}

And make really selected TextView title also in SlidingTabLayout in 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);
        }
    }

Hope it will be helpful for somebody.

Jerol answered 14/11, 2014 at 16:14 Comment(6)
How would you center the icon if the tab's width takes 50% of SlidingTabLayout width. Do i need to create a custom view?Breann
I did not center it. The tabs in my case starts from left and if it takes 50%, it would not fill all width.Jerol
@Breann Try to add android:layout_weight="1" to your custom tab layout.Jerol
I've already solved it. #28126294 Thanks anyway!Breann
hi @v1k, your code is perfect, however, it has a bug. For the first time, when you swipe between tabs, the first & second tab, both shows selected state. This behavior disappears after you switch from second to first tab. The reason for this behavior is that you have used tabTitleView.setSelected(true); in your populateTabStrip() which is causing the behavior. If you change it to tabView.setSelected(true); then it works fine. I am editing your code to reflect this.Shoer
Thanks a lot, I didn't observe this issue, in my case I selected second tab after initialization.Jerol
H
3

To customize SlidingTabLayout the way you want, you only need to modify the method 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);
        }
    }

Your layout could be something like that:

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

The way you implement the getResourceArray() method is up to you. Here is how I did:

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

    ...

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

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

In the activity:

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

Be aware that in order to have access to R.layout.tab_layout*, you have to import yourpackage.R instead of android.R as it is by default in SlidingTabStrip.

Handler answered 14/11, 2014 at 17:32 Comment(0)
S
1

Ok.. there are many ways of implementing this change. This is the most quick and dirty one, just for the idea..

Substitue the method SlidingTabLayout.populateTabStrip() for this..

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

Create each layout this way LinearLayout > ImageView elements, src pointing to icon..

Subarctic answered 12/6, 2014 at 1:50 Comment(2)
Hi. Can you tell me what the mtabstrip is and what the adapter and onclicklistener are for?Kinelski
Part of the original implementation of SlidingTabLayout.. the listener is used to add onclick functionality to the tabs and there is no need for the adapter since your don't want to set page title in tabsSubarctic
N
1

I know that this thread if fairly old, but i wanted to share my solution to this problem nevertheless, because it might be helpful for some. It works quite nice, even for different drawables depending on the selected state.

First of i did define two array inside the SlidingTabLayout class (this could easily be outsourced to another class):

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;

Now we alter the populateTabStrip() method inside the 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);
        }

To update the image according to which tab is selected we add some lines to the onPageSelected method

// 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;

Finally we need to add a custom Tab View:

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

Like this one

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

This solution is far from perfect, but works for my needs. Maybe i could help someone with this.

Nnw answered 1/11, 2014 at 23:21 Comment(1)
how to add two textviews in vertical orderSubjective
E
1

Your Adapter should implements PagerSlidingTabStrip.IconTabProvider.

Then in getPageIconResIdyou can return id of image resource:

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

Keep in mind that only image will be shown, text won't.

Elide answered 6/2, 2015 at 23:51 Comment(0)
E
0

I recently implemented this library called ViewPagerAddons. It includes SlidingTabLayoutColors, which is exactly what you need. Simply let your pager adapter implement SlidingTabLayouColors.ImageProvider interface, override getPageImage method and return the image resource ids according to the page positions. Simple.

Here's the link to the library (set up instructions included): https://bitbucket.org/enthusiast94/viewpageraddons/src

Endogen answered 3/9, 2014 at 16:56 Comment(0)
H
0

I followed Jeremy's answer and its working fine. AFAIK there is no new way of doing this. Most apps are now removing the toolbar altogether and adding a custom slidingtabstrip recommended by google.

If you have any specific questions let me know. iv got my app looking a lot like the screenshot you have posted. I feel this material design update was focusing more on enabling custom animations and effects.

Hedrick answered 15/8, 2015 at 6:14 Comment(0)
S
0

Just returning null from the getPageTitle() method did it for me:

@Override
 public CharSequence getPageTitle(int position) {
    return null;
}
Stockroom answered 31/12, 2015 at 5:53 Comment(0)
L
0

Check this solution link androidhive about material design tabs /viewpager

A very good website about android solutions. Hope you will find a solution.

Limburg answered 16/2, 2016 at 9:37 Comment(0)
B
-1

Extend your main activity from FragmentActivity. Also implement this class from ActionBar.TabListener as we are adding Tabs

// For Adding Tabs

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

Check here.

Buster answered 5/5, 2014 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.