TabLayout 使用自定义视图更新选项卡的内容

28

我正在使用新材料设计的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;
}

而在活动中,我可以简化我的代码:

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

    }
});
选项卡的标题在应用程序启动时设置正确,但是当我更改选项卡时,内容仍然保持不变。

应用程序启动时选项卡的标题已经被正确设置,但是当我切换选项卡时,内容仍然保持不变。

5个回答

51

好的,我认为这是Android设计支持库v22上的一个错误。

因为你谈论了内容的更改,但我无法更改文本的颜色。一旦选项卡被创建并且如果您更改布局,则不会反映出来。

根据我所看到的代码,您正在使用setCustomView提供自定义布局。要更改文本,您会再次调用setTabView()方法。而不是那样做,应该有一个名为getCustomView()的方法,以便可以更改布局。虽然TabLayout.Tab.getCustomView中有一个方法,但它没有标识符,我已经报告了这个错误。

https://code.google.com/p/android/issues/detail?id=177492

[更新08-07-2015]

最终,Android错误源跟踪接受了此错误,并将其标记为待确认版本。因此,我们可以说将来的库中不再存在此错误,我们可以拥有方法getCustomView(),以便轻松获取我们的自定义视图。

[更新18-08-2015]

最终,错误已在v23支持库中解决。只需使用SDK管理器更新支持库即可,并将getCustomView()公开。

只需更改gradle中的此行

compile 'com.android.support:design:23.0.0'

并确保编译和目标SDK设置为23。

请检查我的代码是否对我有效。

TabLayout.Tab tab=tabLayout.getTabAt(position);         
View view=tab.getCustomView();         
TextView txtCount= (TextView) 
view.findViewById(R.id.txtCount);         
txtCount.setText(count+"");

好的,我会遵循它。 - Giorgio Antonioli
2
@Moinkhan,你可以将视图设置为标签以在TabLayout中使用。 - Muhammad Aamir Ali
是的,它可以工作!但有点奇怪,在Android开发者网站(https://developer.android.com/tools/support-library/features.html#design)上仍然显示最新版本为22.2.0。 - Spark.Bao
你有仔细检查过吗?他们正在展示23.0.1版本,列出了已解决的错误清单。 - Moinkhan
1
非常感谢@Moinkhan..!! 这个库支持:design 22有这个bug,已经浪费了我很多时间。 - Kannan_SJD
显示剩余3条评论

7

好的,可能因为我没有时间等待下一个支持库版本,所以我找到了一个非常棘手的解决方案。这是我做的:

……

  1. Set pager adapter to your TabLayout tabLayout.setTabsFromPagerAdapter(pagerAdapter);
  2. Add listener to your 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) {
        }
    });
    
  3. Add listener to your 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);
            }
        }
    });
    
  4. Add method for re-drawing all tabs

    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)时,它会检查视图的父级是否为空,如果不为空,则删除所有子视图。但是,如果你传递新实例化的项,你将添加另一个视图而不是替换旧视图:

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

因此,最终我自己设置了所有的监听器,并在页面/标签更改时重新创建了所有选项卡。这不是一个好的解决方案,但是由于不能等待Google更新他们的库 - 这可能是实现这种效果的唯一解决方案。(以防你想知道为什么这么复杂 - 我需要具有自定义视图(文本 + 图像)的选项卡,它们可以在选择/取消选择时更改其视图属性(字体颜色,图像)) 顺便说一句 - 这是第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;
}

附言:如果您知道更好的解决方案,请告诉我!

更新

可以通过使用反射来实现更好的解决方案:

  1. Easy setup

    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) {
        }
    });
    
  2. Update Tab

    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(),所以您不再需要反射!


我没有任何选项卡的布局,我只是使用库。那么我该如何绑定组件“final ImageView ivTabIcon =(ImageView)tabview.findViewById(R.id.iv_tab_icon);”,在哪里添加自定义布局? - Tara
@Tara,你能解释一下你想要实现什么吗? - Paul Freez
我正在使用直接库,没有为选项卡定义任何自定义布局,就像你定义的“R.layout.tab_sessioninfo”一样。如果我没有使用任何自定义布局,那么我将没有任何ImageView。我的目的是在单击选项卡时更改选项卡图标。有点像在选择选项卡之前,它的图标是灰色的,选择后图标变为蓝色。无论如何,我通过添加“addOnPageChangeListener和onPageSelected”方法解决了我的问题。 - Tara

0
请检查一下我的代码,对我来说它运行良好。
TabLayout.Tab tab=tabLayout.getTabAt(position);         
View view=tab.getCustomView();         
TextView txtCount= (TextView) view.findViewById(R.id.txtCount);         
txtCount.setText(count+"");

0

关于这个主题的文档非常少。我们可以使用选项卡的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()

0

请看一下你需要根据你的期望设置布局的答案。如果你想要更改选项卡内容,请在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) {

    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接