How to scroll tablayout programmatically - Android

a 夏天 提交于 2019-12-04 15:42:22

问题


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?


回答1:


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



回答2:


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



回答3:


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



回答4:


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.




回答5:


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




回答6:


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.




回答7:


new Handler().postDelayed(
    new Runnable() {
        @Override public void run() {
            mTabLayout.getTabAt(TAB_NUMBER).select();
        }
    }, 100);



回答8:


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




回答9:


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



回答10:


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



回答11:


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".




回答12:


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




回答13:


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.



来源:https://stackoverflow.com/questions/31448504/how-to-scroll-tablayout-programmatically-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!