如何以编程方式滚动 tablayout - Android
How to scroll tablayout programmatically - Android
我已经使用 tablayout 创建了 30 个可滚动的标签。
因此前三个选项卡在屏幕上可见,其余选项卡不可见,可以使用滑动手势滚动。
问题是当我以编程方式选择最后一个选项卡时它不可见(选项卡布局未滚动到最后一个选项卡)。
如何让 tablayout 滚动到最后一个标签页?
到 select 最后一个选项卡,使用
tabLayout.getTabAt(X).select();其中 X 是最后一个标签索引
我找到了解决方案。
首先我找到了 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);
上面的答案是行不通的,因为首先正如 agirardello 提到的,你不应该使用 mTabLayout.getWidth()
因为它 return 不是我们需要的(这是 child 你想滚动到)并且由于 TabLayout 中的错误(报告 here),更新的解决方案并不总是有效,但解决方法很简单。
tabLayout 上的选项卡不是 TabLayout 的直接 children,因此我们需要更深入地使用
((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(YOUR_DESIRED_TAB_INDEX).getRight()
tabLayout 的唯一 child 是 TabLayout.SlidingTabStrip
,它也是 ViewGroup
,getRight()
将为我们提供所需选项卡视图的最右侧位置。因此滚动到那个位置会给我们想要的东西。这是完整的代码:
int right = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(4).getRight();
mTabLayout.scrollTo(right,0);
mTabLayout.getTabAt(4).select();
注意:确保在布局被淹没后调用这些方法(如 onResume 而不是 onCreate)
希望对您有所帮助。
您是在实际测量和绘制 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();
}
});
}
}
}
有什么建议欢迎评论
我找到了适合我的解决方案:
TabLayout tabLayout = activity.getTabLayout();
tabLayout.setSmoothScrollingEnabled(true);
tabLayout.setScrollPosition(targetChannelPosition, 0f, true);
此外,如果您收到此错误:"Only the original thread that created a view hierarchy can touch its views.",您可以使用此代码,以便 运行 在 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();
}
});
在您的自定义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();
}
});
}
viewpager.setItem(position)
也应该设置标签的位置
这个解决方案对我有用。不过我的情况有点不同;就我而言,我将 TabLayout 与 ViewPager 一起使用并添加更多视图并调用 notifyDataSetChange()。
解决方法是在TabLayout的观察者上设置一个回调,当children真正添加到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;
}
}
new Handler().postDelayed(
new Runnable() {
@Override public void run() {
mTabLayout.getTabAt(TAB_NUMBER).select();
}
}, 100);
我想知道这个答案是否相关,因为它来得太晚了。我实际上使用 Xamarin 在 C# 中实现了它。
tabs.GetChildAt(0).Selected = true;
viewPager.SetCurrentItem(0, true);
如果您的 TabLayout
与 ViewPager
结合使用,这很常见,只需在 Activity 的 onCreate()
方法中添加以下内容:
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout);
您的某些选项卡未显示表示 tabMode 属性设置为 app:tabMode="scrollable"
。
tab = tabLayout.getSelectedTabPosition();
tab++;
TabLayout.Tab tabs = tabLayout.getTabAt(tab);
if (tabs != null) {
tabs.select();
}
else {
tabLayout.getTabAt(0).select();
}
如果你想要点击事件的下一个选项卡然后使用此代码它的工作 perfactly
下面的代码片段适合我
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()中widgets还没有被测量,需要推迟调用直到布局完成。
我已经使用 tablayout 创建了 30 个可滚动的标签。
因此前三个选项卡在屏幕上可见,其余选项卡不可见,可以使用滑动手势滚动。
问题是当我以编程方式选择最后一个选项卡时它不可见(选项卡布局未滚动到最后一个选项卡)。
如何让 tablayout 滚动到最后一个标签页?
到 select 最后一个选项卡,使用 tabLayout.getTabAt(X).select();其中 X 是最后一个标签索引
我找到了解决方案。
首先我找到了 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);
上面的答案是行不通的,因为首先正如 agirardello 提到的,你不应该使用 mTabLayout.getWidth()
因为它 return 不是我们需要的(这是 child 你想滚动到)并且由于 TabLayout 中的错误(报告 here),更新的解决方案并不总是有效,但解决方法很简单。
tabLayout 上的选项卡不是 TabLayout 的直接 children,因此我们需要更深入地使用
((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(YOUR_DESIRED_TAB_INDEX).getRight()
tabLayout 的唯一 child 是 TabLayout.SlidingTabStrip
,它也是 ViewGroup
,getRight()
将为我们提供所需选项卡视图的最右侧位置。因此滚动到那个位置会给我们想要的东西。这是完整的代码:
int right = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(4).getRight();
mTabLayout.scrollTo(right,0);
mTabLayout.getTabAt(4).select();
注意:确保在布局被淹没后调用这些方法(如 onResume 而不是 onCreate)
希望对您有所帮助。
您是在实际测量和绘制 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();
}
});
}
}
}
有什么建议欢迎评论
我找到了适合我的解决方案:
TabLayout tabLayout = activity.getTabLayout();
tabLayout.setSmoothScrollingEnabled(true);
tabLayout.setScrollPosition(targetChannelPosition, 0f, true);
此外,如果您收到此错误:"Only the original thread that created a view hierarchy can touch its views.",您可以使用此代码,以便 运行 在 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();
}
});
在您的自定义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();
}
});
}
viewpager.setItem(position)
也应该设置标签的位置
这个解决方案对我有用。不过我的情况有点不同;就我而言,我将 TabLayout 与 ViewPager 一起使用并添加更多视图并调用 notifyDataSetChange()。
解决方法是在TabLayout的观察者上设置一个回调,当children真正添加到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;
}
}
new Handler().postDelayed(
new Runnable() {
@Override public void run() {
mTabLayout.getTabAt(TAB_NUMBER).select();
}
}, 100);
我想知道这个答案是否相关,因为它来得太晚了。我实际上使用 Xamarin 在 C# 中实现了它。
tabs.GetChildAt(0).Selected = true;
viewPager.SetCurrentItem(0, true);
如果您的 TabLayout
与 ViewPager
结合使用,这很常见,只需在 Activity 的 onCreate()
方法中添加以下内容:
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout);
您的某些选项卡未显示表示 tabMode 属性设置为 app:tabMode="scrollable"
。
tab = tabLayout.getSelectedTabPosition();
tab++;
TabLayout.Tab tabs = tabLayout.getTabAt(tab);
if (tabs != null) {
tabs.select();
}
else {
tabLayout.getTabAt(0).select();
}
如果你想要点击事件的下一个选项卡然后使用此代码它的工作 perfactly
下面的代码片段适合我
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()中widgets还没有被测量,需要推迟调用直到布局完成。