TabLayout update tab content with a custom view

49,488

Solution 1

Ok I think it's bug on android design support library v22.

Because You are talking about the changes in content but I can't change the color of Text. Once the tab are created and if you change the layout it will not reflecting.

And As i have seen your code you are giving the custom layout using setCustomView. and for changing a text you are calling again the method setTabView(). Instead of that there should be method getCustomView() so that you can change the layout. There is a method in a TabLayout.Tab.getCustomView but it doesn't have identifier and I have report this bug.

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

[Update 08-07-2015]

Finally bug is accepted by android bug source traking and marked as Future Release . So we can say that bug will no more exist on future library. and we can have method getCustomView() so that we can easily get our custom view.

[Update 18-08-2015]

Finally the bug is resolved Released in the v23 support libs . Just update support library using SDK manager and you have getCustomView() as a public.

just change this line in gradle

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

and make sure compile and target sdk set to 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+"");

Solution 2

Well ok, I might have found a very tricky solution just because I didn't have any time to wait for next support-library version. Here's what I did:

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

Ok, so let me explain know a little bit. First of all, you might wonder why did I used setTabsFromPagerAdapter() instead of setupWithViewPager. In the beginning I used setupWithViewPager but somehow my it didn't work correctly with my pager and the 1st item wasn't been able to be selected.

The second thing you might admit - every time I'm selecting a tab I'm deleting all of them. Well, that was kind of easy problem, you see I android sources when you call Tab.setCustomView(View) it checks the parent of the view and if it's not null - it removes all childviews. However if you pass newly-instantiated item you'll ADD another view instead of replacing the old one:

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

So, I ended up with setting all listeners by myself and re-creating all tabs every time on page/tab changes. That's NOT good solution, but as far as can't wait for Google to update their lib - this's probably the only solution to achieve such effect. (Just in case you're wondering why is this so complicated - I needed to have Tabs with custom views (text + image) which could change their view-properties (font color, image) when they're selected/unselected)

BTW - Here's the getTabView(...) method from 4th step:

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. If you know better solution for all this stuff let me know!

UPDATE

Better solution could be approached by using Reflection:

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

UPDATE

New support library has public method for getCustomView(), so you don't need reflection no more!

Share:
49,488
Giorgio Antonioli
Author by

Giorgio Antonioli

My open source projects: KPermissions for Android: https://github.com/fondesa/kpermissions RecyclerViewDivider for Android: https://github.com/fondesa/recycler-view-divider Lyra for Android: https://github.com/fondesa/lyra

Updated on May 31, 2021

Comments

  • Giorgio Antonioli
    Giorgio Antonioli almost 3 years

    I'm using TabLayout of the new material design and i have a problem, i can't update tab content of a custom view once the tab is created:

    I can simplify my method inside my PagerAdapter with

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

    And in activity i can simplify my code with:

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

    The tabs' titles are set right when the app starts but when i change tab, the content still remains the same.