【问题标题】:How to scroll tablayout programmatically - Android如何以编程方式滚动 tablayout - Android
【发布时间】:2015-10-05 13:29:38
【问题描述】:

我使用 tablayout 创建了 30 个可滚动标签。

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

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

如何让 tablayout 滚动到最后一个标签?

【问题讨论】:

  • 你可以发布一些代码吗?

标签: android android-tabs android-tablayout android-scrollable-tabs


【解决方案1】:

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

【讨论】:

  • 选择选项卡工作得很好。问题是所选标签不可见。
  • 我正在使用 tabLayout.getTabAt(X).select();选择并显示选项卡 X。调用时您的应用会发生什么情况?
  • 它选择最后一个选项卡,但选项卡布局不会滚动到最后一个选项卡,因此所选选项卡仍然不可见。
  • 我同意 Mohit 的观点 - 呼叫 tabLayout.getTabAt(X).select(); 不起作用,即使从 View.post(Runnable{}) 内部呼叫也是如此。有效的是View.postDelayed(Runnable{},200)。这是一个皮塔饼
【解决方案2】:

我找到了解决办法。

首先我找到了 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);

【讨论】:

  • 对我来说它不起作用,mTabLayout.getWidth() 是视图的宽度,而不是内部所有“选项卡”的宽度......
  • 谢谢!第二个解决方案对我有用!我把它放在 Xamarin World 给那些感兴趣的人new Handler().PostDelayed(() => { _tabLayout.GetTabAt(TAB_NUMBER).Select(); }, 100);
  • 这是可行的,但我最初发现了一个混蛋
  • 第二种解决方案是最简单的解决方案,工作完美。
  • 第二个解决方案完美 new Handler().postDelayed( new Runnable() { @Override public void run() { mTab​​Layout.getTabAt(TAB_NUMBER).select(); } }, 100) ;
【解决方案3】:

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

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

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

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

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

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

希望这会有所帮助。

【讨论】:

  • @Kayan N 我做对了 = 每个孩子每次都是 0。
【解决方案4】:

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

如果布局尚未布局,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();

                }
            });
        }
    }
}

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

【讨论】:

  • 谢谢,伙计!这是一个非常完美的解决方案,没有任何拼凑。在过去的几个小时里,我一直在努力实现它。你真的解释得很好。我明白了这个概念。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

    在您的自定义 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();
                        }
                    });
    }
    

    【讨论】:

    • 很好,我一直在寻找这个。我不知道为什么它不起作用。我只是错过了可运行的帖子。谢谢
    【解决方案7】:

    viewpager.setItem(position)也应该设置tab的位置

    【讨论】:

      【解决方案8】:

      这个解决方案对我有用。我的情况有点不同。就我而言,我将 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;
          }
      }
      

      【讨论】:

        【解决方案9】:
        new Handler().postDelayed(
            new Runnable() {
                @Override public void run() {
                    mTabLayout.getTabAt(TAB_NUMBER).select();
                }
            }, 100);
        

        【讨论】:

          【解决方案10】:

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

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

          【讨论】:

            【解决方案11】:

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

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

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

            【讨论】:

              【解决方案12】:
              tab = tabLayout.getSelectedTabPosition();
                          tab++;
                          TabLayout.Tab tabs = tabLayout.getTabAt(tab);
                          if (tabs != null) {
                              tabs.select();
                          }
                          else {
                              tabLayout.getTabAt(0).select();
                          }
              

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

              【讨论】:

                【解决方案13】:

                下面的代码 sn-p 对我有用

                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() 中还没有测量小部件,您需要将调用推迟到布局完成。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-09-01
                  • 2011-01-15
                  • 2017-12-28
                  • 1970-01-01
                  • 2017-08-14
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-04
                  相关资源
                  最近更新 更多