26

我使用 tablayout 创建了 30 个可滚动的选项卡。

因此,前三个选项卡在屏幕上可见,其余选项卡不可见,可以使用滑动手势滚动。

问题是当我以编程方式选择最后一个选项卡但它不可见时(选项卡布局不会滚动到最后一个选项卡)。

如何使 tablayout 滚动到最后一个选项卡?

4

13 回答 13

61

我找到了解决方案。

首先,我找到了 tablayout 的宽度并将它的 x 位置滚动到宽度,然后调用了最后一个选项卡的 select() 方法。

它工作正常。

下面是代码。

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

更新

如果上面不起作用,您也可以使用下面的代码,它也可以正常工作。

new Handler().postDelayed(
        new Runnable() {
            @Override public void run() {
                mTabLayout.getTabAt(TAB_NUMBER).select();
            }
        }, 100);
于 2015-07-16T11:22:16.267 回答
7

在您的自定义 tablayout 中编写此方法(您自己的扩展 tablayout 的布局)。因此,将来您可以在需要重复代码时使用此方法

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

如果您不想使用 CustomLayout。你可以这样做

final Tab currentTab = mTabLayout.getTabAt(tabIndex);
if(currentTab != null){
     mTabLayout.post(new Runnable() {
                    @Override
                    public void run() {
                        currentTab.select();
                    }
                });
}
于 2016-05-09T10:02:12.950 回答
6

我为我找到了这个解决方案:

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

此外,如果您收到此错误:“只有创建视图层次结构的原始线程才能触摸其视图。”,您可以使用此代码,以便在 Ui 线程上运行:

    // 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();
        }
    });
于 2016-02-17T12:09:04.027 回答
5

您是tab.select()在 TabLayout 及其子项实际被测量和绘制之前调用吗?如果是这样,您的 TabLayout 将不会使用tab.select()(或 Kayvan N 的建议scrollTo())对选择进行动画处理。使用 Handler 可能会起作用,但这不是一个理想的解决方案。

如果布局尚未布局,aViewTreeObserver将允许您在布局过程完成后移动到您选择的选项卡。

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

                }
            });
        }
    }
}

如果您有任何建议,请发表评论。

于 2016-01-14T02:16:43.407 回答
3

上面的答案不起作用,因为首先正如 agirardello 提到的,您不应该使用mTabLayout.getWidth()它,因为它不会返回我们需要的内容(这是您要滚动到的孩子的位置),并且更新的解决方案并不总是有效,因为TabLayout 中的一个错误(在此处报告),但解决方法很简单。

tabLayout 上的选项卡不是 TabLayout 的直接子级,因此我们需要更深入地使用

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

tabLayout 的唯一子元素是 a TabLayout.SlidingTabStrip,它也是 a ViewGroupgetRight()它将为我们提供所需选项卡视图的最右侧位置。因此,滚动到那个位置会给我们想要的东西。这是一个完整的代码:

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

注意:确保在布局被淹没后调用这些方法(如 onResume 而不是 onCreate)

希望这可以帮助。

于 2015-09-21T21:09:32.817 回答
2

下面的代码片段对我有用

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

我深入研究了 Tab.select(),发现 Android 使用 Tablayout.setScrollPosition() 来执行此滚动。而在 onCreate() 中,小部件尚未测量,您需要推迟调用,直到布局完成。

于 2019-02-28T11:46:00.477 回答
2
new Handler().postDelayed(
    new Runnable() {
        @Override public void run() {
            mTabLayout.getTabAt(TAB_NUMBER).select();
        }
    }, 100);
于 2016-06-14T09:55:04.880 回答
1

要选择最后一个选项卡,请使用 tabLayout.getTabAt(X).select(); 其中 X 是最后一个标签索引

于 2015-07-16T08:15:31.737 回答
1

如果 yourTabLayout与 a 一起使用,这很常见,只需在 ActivityViewPager的方法中添加以下内容:onCreate()

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

您的某些选项卡未显示表明 tabMode 属性设置为app:tabMode="scrollable"

于 2017-03-30T21:17:09.437 回答
0

我想知道这是否是相关的答案,因为它来得太晚了。我实际上使用 Xamarin 在 C# 中实现了它。

tabs.GetChildAt(0).Selected = true;
 viewPager.SetCurrentItem(0, true);
于 2017-01-03T22:52:37.160 回答
0
tab = tabLayout.getSelectedTabPosition();
            tab++;
            TabLayout.Tab tabs = tabLayout.getTabAt(tab);
            if (tabs != null) {
                tabs.select();
            }
            else {
                tabLayout.getTabAt(0).select();
            }

如果您想要单击事件的下一个选项卡,则使用此代码完美地工作

于 2019-01-10T16:58:29.920 回答
0

viewpager.setItem(position)还应该设置选项卡的位置

于 2016-05-09T10:53:54.520 回答
0

这个解决方案对我有用。我的情况有点不同。就我而言,我将 TabLayout 与 ViewPager 一起使用,并添加更多视图并调用 notifyDataSetChange()。

解决方案是在 TabLayout 的观察者上设置回调,并在子项实际添加到 TabLayout 时滚动。这是我的例子:

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

如果您也需要它,这是我的 PagerAdapter 代码:

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;
    }
}
于 2016-06-03T20:17:31.393 回答