24

I have created 30 scrollable tabs using tablayout.

So first three tabs are visible on screen and rest of them are invisible which can be scroll using swipe gesture.

The problem is when I am selecting last tab programmatically but it is not get visible (tab layout not get scrolled to last tab).

How can I make tablayout to scroll to last tab?

Cœur
  • 32,421
  • 21
  • 173
  • 232
Mohit Charadva
  • 2,335
  • 1
  • 18
  • 28

13 Answers13

60

I found the solution.

First I had found the width of tablayout and scroll it's x position to width and than called the select() method of last tab.

And it works fine.

Below is the code.

mTabLayout.setScrollX(mTabLayout.getWidth());
mTabLayout.getTabAt(lastTabIndex).select();

Updated:

If above is not working you can use the below code as well, it is also working fine.

new Handler().postDelayed(
        new Runnable() {
            @Override public void run() {
                mTabLayout.getTabAt(TAB_NUMBER).select();
            }
        }, 100);
Kayvan N
  • 6,660
  • 3
  • 25
  • 37
Mohit Charadva
  • 2,335
  • 1
  • 18
  • 28
7

write this method in your custom tablayout (Your own layout which extends tablayout). So, in future you can use this method whenever you need instad of code duplication

public void selectTabAt(int tabIndex) {
        if (tabIndex >= 0 && tabIndex < getTabCount() && getSelectedTabPosition() != tabIndex) {
            final Tab currentTab = getTabAt(tabIndex);
            if (currentTab != null) {
                this.post(new Runnable() {
                    @Override
                    public void run() {
                        currentTab.select();
                    }
                });
            }
        }
    }

If you don't want yo use CustomLayout. you can just do this

final Tab currentTab = mTabLayout.getTabAt(tabIndex);
if(currentTab != null){
     mTabLayout.post(new Runnable() {
                    @Override
                    public void run() {
                        currentTab.select();
                    }
                });
}
Ashok Varma
  • 3,159
  • 2
  • 24
  • 41
  • Nice, I was looking for this for a while. I didn't know why it wasn't working. I was just missing the post runnable. Thank you – codeskraps Jul 14 '16 at 14:51
6

I found this solution for me:

    TabLayout tabLayout = activity.getTabLayout();
    tabLayout.setSmoothScrollingEnabled(true);
    tabLayout.setScrollPosition(targetChannelPosition, 0f, true);

Also, if you receive this error: "Only the original thread that created a view hierarchy can touch its views.", you can use this code, in order to run on Ui thread:

    // find a way to get the activity containing the tab layout
    TabLayout tabLayout = activity.getTabLayout();
    activity.runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            TabLayout.Tab tab = tabLayout.getTabAt(targetChannelPosition);
            tab.select();
        }
    });
sunlover3
  • 1,754
  • 1
  • 15
  • 23
5

Are you calling tab.select() before the TabLayout and its children have actually been measured and drawn? If so, your TabLayout won't animate to the selection with tab.select() (or Kayvan N's suggestion of scrollTo()). Using a Handler will probably work, but that's not an ideal solution.

Provided the layout hasn't been laid out yet, a ViewTreeObserver will allow you to move to your selected tab after the layout process is finished.

private void scrollToTabAfterLayout(final int tabIndex) {
    if (getView() != null) {
        final ViewTreeObserver observer = mTabLayout.getViewTreeObserver();

        if (observer.isAlive()) {
            observer.dispatchOnGlobalLayout(); // In case a previous call is waiting when this call is made
            observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        observer.removeOnGlobalLayoutListener(this);
                    } else {
                        //noinspection deprecation
                        observer.removeGlobalOnLayoutListener(this);
                    }

                    mTabLayout.getTabAt(tabIndex).select();

                }
            });
        }
    }
}

Please comment if you have any suggestions.

  • 1
    Thanks, man! It's a really perfect solution without any patchwork. I have been trying to achieve it for the past few hours. You really explained it well. and I got the concept. – Dhara Vamja Dec 12 '19 at 06:19
3

The above answer wouldn't work because first As agirardello mentioned you should not use mTabLayout.getWidth() since it doesn't return what we need (which is the position of the child you want to scroll to) and the updated solution doesn't always work because of a bug in TabLayout (reported here) but a work around is simple.

The tabs on the tabLayout are not direct children of the TabLayout so we need to go one level deeper using

((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(YOUR_DESIRED_TAB_INDEX).getRight()

the only child of tabLayout is a TabLayout.SlidingTabStrip which is also a ViewGroup and getRight() will give us the right most position of our desired tab view. Thus scrolling to that position will give us what we desire. Here is a complete code:

int right = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(4).getRight();
mTabLayout.scrollTo(right,0);
mTabLayout.getTabAt(4).select();

NOTE: Make sure you are calling these methods after the layout has been drown (like onResume and not onCreate)

Hope this helps.

Kayvan N
  • 6,660
  • 3
  • 25
  • 37
2
new Handler().postDelayed(
    new Runnable() {
        @Override public void run() {
            mTabLayout.getTabAt(TAB_NUMBER).select();
        }
    }, 100);
Hai Rom
  • 1,511
  • 13
  • 9
2

The code snippet below works for me

class TriggerOnceListener(private val v: View, private val block: () -> Unit) : ViewTreeObserver.OnPreDrawListener {
    override fun onPreDraw(): Boolean {
        block()
        v.viewTreeObserver.removeOnPreDrawListener(this)
        return true
    }
}

fun onCreate() {
    val position = ***The tab position you want to scroll to, 29 for your case here***
    tabLayout.let { it.viewTreeObserver.addOnPreDrawListener(TriggerOnceListener(it)
    { it.setScrollPosition(position, 0f, true) } ) }
}

I dived into Tab.select(), and found Android uses Tablayout.setScrollPosition() to do this scrolling. And in onCreate() the widgets have not been measured, you need to postpone the call until layout is complete.

Kun Zhang
  • 21
  • 1
1

To select the last tab, use tabLayout.getTabAt(X).select(); where X is the last tab index

jomartigcal
  • 803
  • 1
  • 6
  • 11
  • 1
    Selecting tab is working perfectly fine. problem is that selected tab is not get visible. – Mohit Charadva Jul 16 '15 at 08:19
  • I am using tabLayout.getTabAt(X).select(); to select and display tab X. What happens to your app when you call it? – jomartigcal Jul 16 '15 at 08:36
  • 1
    It select that last tab but tab layout does not scroll to last tab so that selected tab remains invisible. – Mohit Charadva Jul 16 '15 at 10:02
  • I agree with Mohit - calling `tabLayout.getTabAt(X).select();` doesn't work, even calling from within `View.post(Runnable{})`. What does work is `View.postDelayed(Runnable{},200)`. It's a PITA – Someone Somewhere Aug 20 '16 at 20:29
1

If your TabLayout is used in conjunction with a ViewPager, which is common, simply add the following in the onCreate() method in your Activity:

tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout);

That some of your tabs are not being shown indicates the tabMode attribute is set to app:tabMode="scrollable".

barnabas
  • 109
  • 2
  • 4
0

viewpager.setItem(position) should also set the position of the tab

ρяσѕρєя K
  • 127,886
  • 50
  • 184
  • 206
locomain
  • 187
  • 1
  • 15
0

This solution worked for me. My situation is a little bit different though; in my case, I am using the TabLayout with a ViewPager and adding more views and calling notifyDataSetChange().

The solution is to set a callback on the observer of TabLayout and scroll when the children are actually added to the TabLayout. Here is my example:

/**
    Keep in mind this is how I set my TabLayout up...

    PagerAdapter pagerAdapter = new PagerAdapter(...);
    ViewPager pager = (ViewPager)findViewById(...);
    pager.setAdapter(pagerAdapter);

    TabLayout tabLayout = (TabLayout)findViewById(...);
    tabLayout.setupWithViewPager(pager);
*/
public void loadTabs(String[] topics) {
    animateTabsOpen(); // Irrelevant to solution

    // Removes fragments from ViewPager
    pagerAdapter.clear();

    // Adds new fragments to ViewPager
    for (String t : topics)
         pagerAdapter.append(t, new TestFragment());

    // Since we need observer callback to still animate tabs when we
    // scroll, it is essential to keep track of the state. Declare this
    // as a global variable
    scrollToFirst = true;

    // Alerts ViewPager data has been changed
    pagerAdapter.notifyOnDataSetChanged();

    // Scroll to the beginning (or any position you need) in TabLayout
    // using its observer callbacks
    tabs.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            /**
                 We use onGlobalLayout() callback because anytime a tab
                 is added or removed the TabLayout triggers this; therefore,
                 we use it to scroll to the desired position we want. In my
                 case I wanted to scroll to the beginning position, but this
                 can easily be modified to scroll to any position.
             */
            if (scrollToFirst) {
                tabs.getTabAt(0).select();
                tabs.scrollTo(0, 0);
                scrollToFirst = false;
            }
        }
    });
}

Here is my code for the PagerAdapter if you need it too lol:

public class PagerAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> fragments;
    private List<String> titles;


    public PagerAdapter(FragmentManager fm) {
        super(fm);
        this.fragments = new ArrayList<>();
        this.titles = new ArrayList<>();
    }

    /**
     * Adds an adapter item (title and fragment) and
     * doesn't notify that data has changed.
     *
     * NOTE: Remember to call notifyDataSetChanged()!
     * @param title Fragment title
     * @param frag Fragment
     * @return This
     */
    public PagerAdapter append(String title, Fragment frag) {
        this.titles.add(title);
        this.fragments.add(frag);
        return this;
    }

    /**
     * Clears all adapter items and doesn't notify that data
     * has changed.
     *
     * NOTE: Rememeber to call notifyDataSetChanged()!
     * @return This
     */
    public PagerAdapter clear() {
        this.titles.clear();
        this.fragments.clear();
        return this;
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }

    @Override
    public int getItemPosition(Object object) {
        int position = fragments.indexOf(object);
        return (position >= 0) ? position : POSITION_NONE;
    }
}
Tyler
  • 11
  • 5
0

I wonder if this is answer will be relevant since its coming very late. i actually achieved it in C# using Xamarin.

tabs.GetChildAt(0).Selected = true;
 viewPager.SetCurrentItem(0, true);
Uchenna Nnodim
  • 342
  • 2
  • 10
0
tab = tabLayout.getSelectedTabPosition();
            tab++;
            TabLayout.Tab tabs = tabLayout.getTabAt(tab);
            if (tabs != null) {
                tabs.select();
            }
            else {
                tabLayout.getTabAt(0).select();
            }

if you want next tab on click event then use this code its work perfactly

RanaUmer
  • 311
  • 2
  • 5