TabLayout 使用自定义视图更新选项卡内容
TabLayout update tab content with a custom view
我正在使用新 material 设计的 TabLayout
,但我有一个问题,创建选项卡后我无法更新自定义视图的选项卡内容:
我可以使用
在我的 PagerAdapter 中简化我的方法
public View setTabView(int position, boolean selected) {
View v = LayoutInflater.from(context).inflate(R.layout.default_tab_view, null);
tv = (TextView) v.findViewById(R.id.tabTextView);
if(selected)
tv.setText("SELECTED");
else
tv.setText("UNSELECTED");
return v;
}
在 activity 中,我可以通过以下方式简化我的代码:
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
tabLayout.setupWithViewPager(pager);
for (int i = 0; i < tabLayout.getTabCount(); i++) {
boolean isFirstTab = i == 0;
TabLayout.Tab tab = tabLayout.getTabAt(i);
View v;
v = adapter.setTabView(i, isFirstTab);
v.setSelected(isFirstTab);
tab.setCustomView(v);
}
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
adapter.setTabView(tab.getPosition(), true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
adapter.setTabView(tab.getPosition(), false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
标签的标题在应用程序启动时设置正确,但当我更改标签时,内容仍然保持不变。
好的 我认为这是 android 设计支持库 v22 上的错误。
因为你说的是内容的变化,但我不能改变文本的颜色。创建选项卡后,如果您更改布局,它将不会反映出来。
正如我所见,您使用 setCustomView 提供了自定义布局。为了更改文本,您再次调用方法 setTabView()。而不是应该有方法 getCustomView() 以便您可以更改布局。 TabLayout.Tab.getCustomView 中有一个方法,但它没有标识符,我已报告此错误。
https://code.google.com/p/android/issues/detail?id=177492
[更新 08-07-2015]
最终 bug 被 android bug source traking 接受并标记为 Future Release 。所以我们可以说未来的库中将不再存在错误。
我们可以有方法 getCustomView()
这样我们就可以很容易地得到我们的自定义视图。
[更新 18-08-2015]
终于解决了 bug 在 v23 支持库 中发布。
只需使用 SDK 管理器更新支持库,您就可以将 getCustomView() 作为 public.
只需更改 gradle
中的这一行
compile 'com.android.support:design:23.0.0'
并确保编译和目标 sdk 设置为 23。
please check my code working fine for me
TabLayout.Tab tab=tabLayout.getTabAt(position);
View view=tab.getCustomView();
TextView txtCount= (TextView)
view.findViewById(R.id.txtCount);
txtCount.setText(count+"");
好吧,我可能找到了一个非常棘手的解决方案,只是因为我没有时间等待下一个支持库版本。这是我所做的:
- 将寻呼机适配器设置为您的 TabLayout
tabLayout.setTabsFromPagerAdapter(pagerAdapter);
将侦听器添加到您的 ViewPager
customViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
tabLayout.getTabAt(position).select();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
将侦听器添加到您的 TabLayout
tabLayout.setOnTabSelectedListener(new
TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (!isTabUpdating) {
isTabUpdating = true;
final int pos = tab.getPosition();
customViewPager.setCurrentItem(pos);
// Clears and populates all tabs
tabLayout.setTabsFromPagerAdapter(pagerAdapter);
invalidateTabs(pos);
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
if (!isTabUpdating) {
isTabUpdating = true;
final int pos = tab.getPosition();
customViewPager.setCurrentItem(pos);
// Clears and populates all tabs
tabLayout.setTabsFromPagerAdapter(pagerAdapter);
invalidateTabs(pos);
}
}
});
添加重新绘制所有选项卡的方法
private void invalidateTabs(int selected) {
for (int i = 0; i < tabLayout.getTabCount(); i++) {
tabLayout.getTabAt(i).setCustomView(pagerAdapter.getTabView(i, i == selected));
}
isTabUpdating = false;
}
好的,让我解释一下。首先,您可能想知道为什么我使用 setTabsFromPagerAdapter()
而不是 setupWithViewPager
。一开始我使用 setupWithViewPager
但不知何故我的寻呼机无法正常工作,无法选择第一项。
您可能承认的第二件事 - 每次我选择一个选项卡时,我都会删除所有选项卡。好吧,这是一个简单的问题,当您调用 Tab.setCustomView(View)
时,您会看到我 android 来源,它会检查视图的父视图,如果它不为空 - 它会删除所有子视图。但是,如果您传递新实例化的项目,您将添加另一个视图而不是替换旧视图:
View custom = tab.getCustomView();
if(custom != null) {
ViewParent icon = custom.getParent();
if(icon != this) {
if(icon != null) {
// You wont get here
((ViewGroup)icon).removeView(custom);
}
this.addView(custom);
}
...
因此,我最终自己设置了所有侦听器,并在每次 page/tab 更改时重新创建所有选项卡。这不是好的解决方案,但就等不及 Google 更新他们的库而言——这可能是实现这种效果的唯一解决方案。 (以防万一你想知道为什么这么复杂——我需要有带有自定义视图(文本+图像)的选项卡,当它们是selected/unselected)
顺便说一句 - 这是第 4 步的 getTabView(...)
方法:
public View getTabView(int pos, boolean isSeleted) {
View tabview = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout
.tab_sessioninfo,
null, false);
final ImageView ivTabIcon = (ImageView) tabview.findViewById(R.id.iv_tab_icon);
final TextView tvTabTittle = (TextView) tabview.findViewById(R.id.tv_tab_title);
if (isSeleted) {
tvTabTittle.setTextColor(context.getResources().getColor(R.color.tab_indicator));
}
switch (pos) {
case 0:
tvTabTittle.setText("1st Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon1_selected : R.drawable.ic_icon1);
break;
case 1:
tvTabTittle.setText("2nd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon2_selected : R.drawable.ic_icon2);
break;
case 2:
tvTabTittle.setText("3rd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon3_selected : R.drawable.ic_icon3);
break;
}
return tabview;
}
P.S。如果您知道所有这些东西的更好解决方案,请告诉我!
更新
更好的解决方案可以通过使用反射来实现:
设置简单
tabLayout.setupWithViewPager(customViewPager);
customViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
for (int i = 0; i <tabLayout.getTabCount(); i++){
updateTab(tabLayout.getTabAt(i), position == i);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
更新选项卡
private void updateTab(TabLayout.Tab tab, boolean isSelected){
Method method = null;
try {
method = TabLayout.Tab.class.getDeclaredMethod("getCustomView", null);
method.setAccessible(true);
View tabview = (View) method.invoke(tab, null);
final ImageView ivTabIcon = (ImageView) tabview.findViewById(R.id.iv_tab_icon);
final TextView tvTabTittle = (TextView) tabview.findViewById(R.id.tv_tab_title);
if (isSeleted) {
tvTabTittle.setTextColor(context.getResources().getColor(R.color.tab_indicator));
}
switch (pos) {
case 0:
tvTabTittle.setText("1st Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon1_selected : R.drawable.ic_icon1);
break;
case 1:
tvTabTittle.setText("2nd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon2_selected : R.drawable.ic_icon2);
break;
case 2:
tvTabTittle.setText("3rd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon3_selected : R.drawable.ic_icon3);
break;
}
tab.setCustomView(tabview);
} catch (Exception e) {
e.printStackTrace();
}
}
更新
新的支持库为 getCustomView()
提供了 public 方法,因此您不再需要反射!
please check my code working fine for me
TabLayout.Tab tab=tabLayout.getTabAt(position);
View view=tab.getCustomView();
TextView txtCount= (TextView) view.findViewById(R.id.txtCount);
txtCount.setText(count+"");
看看 Answer 您需要按照您的预期设置布局。如果您想更改选项卡内容,请在 Tab.setOnTabSelectedListener()
中进行更改
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if(tab.getPosition()==1){
tabLayout.setTabTextColors(ContextCompat.getColor(getActivity(), R.color.unselected_tab),ContextCompat.getColor(getActivity(), R.color.tab_selection));
}else{
tabLayout.setTabTextColors(ContextCompat.getColor(getActivity(), R.color.unselected_tab),ContextCompat.getColor(getActivity(), R.color.white));
}
// same thing goes with other tabs too
// Just change your tab text on selected/deselected as above
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
tab.setCustomView(null);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
有关此主题的文档非常少。我们可以使用选项卡的 setCustomView
方法来设置自定义视图。以下是一个无需创建自定义适配器的工作示例:
tab_layout.xml
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height"
android:background="@color/primary_dark" />
custom_tab_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height"
android:orientation="horizontal"
android:padding="@dimen/tab_padding">
<ImageView
android:id="@+id/tabIcon"
android:layout_width="@dimen/tab_icon"
android:layout_height="@dimen/tab_icon"
android:layout_centerVertical="true"/>
<TextView
android:id="@+id/tabTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/tabIcon"
android:textColor="@color/white" />
<TextView
android:id="@+id/tabSubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tabTitle"
android:layout_toEndOf="@+id/tabIcon"
android:textColor="@color/white" />
</RelativeLayout>
MainActivity.kt
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
when (position) {
0 -> {
tab.setCustomView(R.layout.tab_item)
tab.customView?.findViewById<ImageView>(R.id.tabIcon)
?.setImageResource(R.drawable.tab1)
tab.customView?.findViewById<TextView>(R.id.tabTitle)?.setText(R.string.tab1)
}
1 -> {
tab.setCustomView(R.layout.tab_item)
tab.customView?.findViewById<ImageView>(R.id.tabIcon)
?.setImageResource(R.drawable.tab2)
tab.customView?.findViewById<TextView>(R.id.tabTitle)
?.setText(R.string.tab2)
}
}
}.attach()
我正在使用新 material 设计的 TabLayout
,但我有一个问题,创建选项卡后我无法更新自定义视图的选项卡内容:
我可以使用
在我的 PagerAdapter 中简化我的方法public View setTabView(int position, boolean selected) {
View v = LayoutInflater.from(context).inflate(R.layout.default_tab_view, null);
tv = (TextView) v.findViewById(R.id.tabTextView);
if(selected)
tv.setText("SELECTED");
else
tv.setText("UNSELECTED");
return v;
}
在 activity 中,我可以通过以下方式简化我的代码:
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
tabLayout.setupWithViewPager(pager);
for (int i = 0; i < tabLayout.getTabCount(); i++) {
boolean isFirstTab = i == 0;
TabLayout.Tab tab = tabLayout.getTabAt(i);
View v;
v = adapter.setTabView(i, isFirstTab);
v.setSelected(isFirstTab);
tab.setCustomView(v);
}
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
adapter.setTabView(tab.getPosition(), true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
adapter.setTabView(tab.getPosition(), false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
标签的标题在应用程序启动时设置正确,但当我更改标签时,内容仍然保持不变。
好的 我认为这是 android 设计支持库 v22 上的错误。
因为你说的是内容的变化,但我不能改变文本的颜色。创建选项卡后,如果您更改布局,它将不会反映出来。
正如我所见,您使用 setCustomView 提供了自定义布局。为了更改文本,您再次调用方法 setTabView()。而不是应该有方法 getCustomView() 以便您可以更改布局。 TabLayout.Tab.getCustomView 中有一个方法,但它没有标识符,我已报告此错误。
https://code.google.com/p/android/issues/detail?id=177492
[更新 08-07-2015]
最终 bug 被 android bug source traking 接受并标记为 Future Release 。所以我们可以说未来的库中将不再存在错误。
我们可以有方法 getCustomView()
这样我们就可以很容易地得到我们的自定义视图。
[更新 18-08-2015]
终于解决了 bug 在 v23 支持库 中发布。 只需使用 SDK 管理器更新支持库,您就可以将 getCustomView() 作为 public.
只需更改 gradle
中的这一行compile 'com.android.support:design:23.0.0'
并确保编译和目标 sdk 设置为 23。
please check my code working fine for me
TabLayout.Tab tab=tabLayout.getTabAt(position);
View view=tab.getCustomView();
TextView txtCount= (TextView)
view.findViewById(R.id.txtCount);
txtCount.setText(count+"");
好吧,我可能找到了一个非常棘手的解决方案,只是因为我没有时间等待下一个支持库版本。这是我所做的:
- 将寻呼机适配器设置为您的 TabLayout
tabLayout.setTabsFromPagerAdapter(pagerAdapter);
将侦听器添加到您的 ViewPager
customViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { tabLayout.getTabAt(position).select(); } @Override public void onPageScrollStateChanged(int state) { } });
将侦听器添加到您的 TabLayout
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { if (!isTabUpdating) { isTabUpdating = true; final int pos = tab.getPosition(); customViewPager.setCurrentItem(pos); // Clears and populates all tabs tabLayout.setTabsFromPagerAdapter(pagerAdapter); invalidateTabs(pos); } } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { if (!isTabUpdating) { isTabUpdating = true; final int pos = tab.getPosition(); customViewPager.setCurrentItem(pos); // Clears and populates all tabs tabLayout.setTabsFromPagerAdapter(pagerAdapter); invalidateTabs(pos); } } });
添加重新绘制所有选项卡的方法
private void invalidateTabs(int selected) { for (int i = 0; i < tabLayout.getTabCount(); i++) { tabLayout.getTabAt(i).setCustomView(pagerAdapter.getTabView(i, i == selected)); } isTabUpdating = false; }
好的,让我解释一下。首先,您可能想知道为什么我使用 setTabsFromPagerAdapter()
而不是 setupWithViewPager
。一开始我使用 setupWithViewPager
但不知何故我的寻呼机无法正常工作,无法选择第一项。
您可能承认的第二件事 - 每次我选择一个选项卡时,我都会删除所有选项卡。好吧,这是一个简单的问题,当您调用 Tab.setCustomView(View)
时,您会看到我 android 来源,它会检查视图的父视图,如果它不为空 - 它会删除所有子视图。但是,如果您传递新实例化的项目,您将添加另一个视图而不是替换旧视图:
View custom = tab.getCustomView();
if(custom != null) {
ViewParent icon = custom.getParent();
if(icon != this) {
if(icon != null) {
// You wont get here
((ViewGroup)icon).removeView(custom);
}
this.addView(custom);
}
...
因此,我最终自己设置了所有侦听器,并在每次 page/tab 更改时重新创建所有选项卡。这不是好的解决方案,但就等不及 Google 更新他们的库而言——这可能是实现这种效果的唯一解决方案。 (以防万一你想知道为什么这么复杂——我需要有带有自定义视图(文本+图像)的选项卡,当它们是selected/unselected)
顺便说一句 - 这是第 4 步的 getTabView(...)
方法:
public View getTabView(int pos, boolean isSeleted) {
View tabview = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout
.tab_sessioninfo,
null, false);
final ImageView ivTabIcon = (ImageView) tabview.findViewById(R.id.iv_tab_icon);
final TextView tvTabTittle = (TextView) tabview.findViewById(R.id.tv_tab_title);
if (isSeleted) {
tvTabTittle.setTextColor(context.getResources().getColor(R.color.tab_indicator));
}
switch (pos) {
case 0:
tvTabTittle.setText("1st Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon1_selected : R.drawable.ic_icon1);
break;
case 1:
tvTabTittle.setText("2nd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon2_selected : R.drawable.ic_icon2);
break;
case 2:
tvTabTittle.setText("3rd Tab");
ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon3_selected : R.drawable.ic_icon3);
break;
}
return tabview;
}
P.S。如果您知道所有这些东西的更好解决方案,请告诉我!
更新
更好的解决方案可以通过使用反射来实现:
设置简单
tabLayout.setupWithViewPager(customViewPager); customViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { for (int i = 0; i <tabLayout.getTabCount(); i++){ updateTab(tabLayout.getTabAt(i), position == i); } } @Override public void onPageScrollStateChanged(int state) { } });
更新选项卡
private void updateTab(TabLayout.Tab tab, boolean isSelected){ Method method = null; try { method = TabLayout.Tab.class.getDeclaredMethod("getCustomView", null); method.setAccessible(true); View tabview = (View) method.invoke(tab, null); final ImageView ivTabIcon = (ImageView) tabview.findViewById(R.id.iv_tab_icon); final TextView tvTabTittle = (TextView) tabview.findViewById(R.id.tv_tab_title); if (isSeleted) { tvTabTittle.setTextColor(context.getResources().getColor(R.color.tab_indicator)); } switch (pos) { case 0: tvTabTittle.setText("1st Tab"); ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon1_selected : R.drawable.ic_icon1); break; case 1: tvTabTittle.setText("2nd Tab"); ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon2_selected : R.drawable.ic_icon2); break; case 2: tvTabTittle.setText("3rd Tab"); ivTabIcon.setImageResource(isSeleted ? R.drawable.ic_icon3_selected : R.drawable.ic_icon3); break; } tab.setCustomView(tabview); } catch (Exception e) { e.printStackTrace(); } }
更新
新的支持库为 getCustomView()
提供了 public 方法,因此您不再需要反射!
please check my code working fine for me
TabLayout.Tab tab=tabLayout.getTabAt(position);
View view=tab.getCustomView();
TextView txtCount= (TextView) view.findViewById(R.id.txtCount);
txtCount.setText(count+"");
看看 Answer 您需要按照您的预期设置布局。如果您想更改选项卡内容,请在 Tab.setOnTabSelectedListener()
中进行更改tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if(tab.getPosition()==1){
tabLayout.setTabTextColors(ContextCompat.getColor(getActivity(), R.color.unselected_tab),ContextCompat.getColor(getActivity(), R.color.tab_selection));
}else{
tabLayout.setTabTextColors(ContextCompat.getColor(getActivity(), R.color.unselected_tab),ContextCompat.getColor(getActivity(), R.color.white));
}
// same thing goes with other tabs too
// Just change your tab text on selected/deselected as above
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
tab.setCustomView(null);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
有关此主题的文档非常少。我们可以使用选项卡的 setCustomView
方法来设置自定义视图。以下是一个无需创建自定义适配器的工作示例:
tab_layout.xml
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height"
android:background="@color/primary_dark" />
custom_tab_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height"
android:orientation="horizontal"
android:padding="@dimen/tab_padding">
<ImageView
android:id="@+id/tabIcon"
android:layout_width="@dimen/tab_icon"
android:layout_height="@dimen/tab_icon"
android:layout_centerVertical="true"/>
<TextView
android:id="@+id/tabTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/tabIcon"
android:textColor="@color/white" />
<TextView
android:id="@+id/tabSubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tabTitle"
android:layout_toEndOf="@+id/tabIcon"
android:textColor="@color/white" />
</RelativeLayout>
MainActivity.kt
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
when (position) {
0 -> {
tab.setCustomView(R.layout.tab_item)
tab.customView?.findViewById<ImageView>(R.id.tabIcon)
?.setImageResource(R.drawable.tab1)
tab.customView?.findViewById<TextView>(R.id.tabTitle)?.setText(R.string.tab1)
}
1 -> {
tab.setCustomView(R.layout.tab_item)
tab.customView?.findViewById<ImageView>(R.id.tabIcon)
?.setImageResource(R.drawable.tab2)
tab.customView?.findViewById<TextView>(R.id.tabTitle)
?.setText(R.string.tab2)
}
}
}.attach()