简体   繁体   English

如何以编程方式滚动tablayout - Android

[英]How to scroll tablayout programmatically - Android

I have created 30 scrollable tabs using tablayout. 我使用tablayout创建了30个可滚动的选项卡。

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? 如何使tablayout滚动到最后一个标签?

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. 首先,我找到了tablayout的宽度,并将其x位置滚动到宽度,然后调用最后一个选项卡的select()方法。

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

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: 此外,如果您收到此错误:“只有创建视图层次结构的原始线程可以触摸其视图。”,您可以使用此代码,以便在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();
        }
    });

write this method in your custom tablayout (Your own layout which extends tablayout). 在自定义tablayout中编写此方法(您自己的布局扩展了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. 如果你不想使用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();
                    }
                });
}

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. 上面的答案是行不通的,因为首先如agirardello提到你不应该使用mTabLayout.getWidth()因为它不会返回我们需要的东西(这是你要滚动到的孩子的位置),并且更新的解决方案没有因为TabLayout中的一个错误( 这里报告)总是起作用,但解决方法很简单。

The tabs on the tabLayout are not direct children of the TabLayout so we need to go one level deeper using tabLayout上的选项卡不是TabLayout的直接子项,因此我们需要更深入地使用

((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. tabLayout的唯一子节点是TabLayout.SlidingTabStrip ,它也是一个ViewGroupgetRight()将为我们提供所需标签视图的最右侧位置。 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) 注意:确保在布局被淹没后调用这些方法(如onResume而不是onCreate)

Hope this helps. 希望这可以帮助。

To select the last tab, use tabLayout.getTabAt(X).select(); 要选择最后一个选项卡,请使用tabLayout.getTabAt(X).select(); where X is the last tab index 其中X是最后一个标签索引

Are you calling tab.select() before the TabLayout and its children have actually been measured and drawn? 您是否在tab.select()之前调用tab.select()并且其子项实际上已经被测量和绘制了? If so, your TabLayout won't animate to the selection with tab.select() (or Kayvan N's suggestion of scrollTo() ). 如果是这样,你的TabLayout将不会使用tab.select() (或Kayvan N的scrollTo()建议tab.select()为选择设置动画。 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. 如果布局尚未布局, ViewTreeObserver将允许您在布局过程完成后移动到所选选项卡。

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. 如果您有任何建议,请评论。

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

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

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(). 在我的例子中,我使用带有ViewPager的TabLayout并添加更多视图并调用notifyDataSetChange()。

The solution is to set a callback on the observer of TabLayout and scroll when the children are actually added to the TabLayout. 解决方案是在TabLayout的观察者上设置回调,并在子项实际添加到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: 如果你需要它,这是我的PagerAdapter的代码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;
    }
}

I wonder if this is answer will be relevant since its coming very late. 我想知道这个答案是否具有相关性,因为它来得很晚。 i actually achieved it in C# using Xamarin. 我实际上使用Xamarin在C#中实现了它。

tabs.GetChildAt(0).Selected = true;
 viewPager.SetCurrentItem(0, true);

If your TabLayout is used in conjunction with a ViewPager , which is common, simply add the following in the onCreate() method in your Activity: 如果您的TabLayoutViewPager一起使用(这是常见的),只需在Activity中的onCreate()方法中添加以下内容:

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" . 您的某些选项卡未显示表示tabMode属性设置为app:tabMode="scrollable"

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 如果您想要点击事件的下一个标签,那么请将此代码用于其工作

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. 我潜入Tab.select(),发现Android使用Tablayout.setScrollPosition()来做这个滚动。 And in onCreate() the widgets have not been measured, you need to postpone the call until layout is complete. 在onCreate()中,小部件尚未测量,您需要推迟调用直到布局完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM