TabLayout高端用法(二)

  • 接上一篇继续,有同学有在下面留言问tabIndicator这个指示条的长度怎么修改。关于这个问题只能又呵呵了,因为控件居然没有提供修改tabIndicator长度的API。如果非要强行修改就只能用反射了,但有时候需求就是这么操蛋。直接上代码吧,调用下面这个方法,传入左右间距即可,具体长度可以根据实际情况调试。

setIndicator(this, mTabLayout, 20, 20);

  public  void setIndicator(MainActivity context, TabLayout tabs, int leftDip, int rightDip) {
        Class tabLayout = tabs.getClass();
        Field tabStrip = null;
        try {
            tabStrip = tabLayout.getDeclaredField("mTabStrip");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        tabStrip.setAccessible(true);
        LinearLayout ll_tab = null;
        try {
            ll_tab = (LinearLayout) tabStrip.get(tabs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        int left = (int) (getDisplayMetrics(context).density * leftDip);
        int right = (int) (getDisplayMetrics(context).density * rightDip);

        for (int i = 0; i < ll_tab.getChildCount(); i++) {
            View child = ll_tab.getChildAt(i);
            child.setPadding(0, 0, 0, 0);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
            params.leftMargin = left;
            params.rightMargin = right;
            child.setLayoutParams(params);
            child.invalidate();
        }
    }

运行效果如下:

TabLayout高端用法(二)_第1张图片
1.jpg
  • 又有同学提到TabItem动态添加比较好,那是自然的,很多时候我们的数据可能是从服务器下来的,具体有多少个TabItem 有可能也是不太确定的,这个时候当然是动态添加比较好。关于TabItem 的动态添加也很简单。
 tabLayout.addTab(tabLayout.newTab().setText("A"));
 tabLayout.addTab(tabLayout.newTab().setText("B"));
 tabLayout.addTab(tabLayout.newTab().setText("C"));

或许有些时候是从一个集合里读取数据填充,那就要来个for循环了,这个根据自己的实际情况来定。

  • 到现在我们还没有上TabLayout的用法相关代码,下面我们来一个相对标准的使用简介,通常情况下大家会这么写。
public class MainActivity extends AppCompatActivity {

    ViewPager mViewPager;

    ListFragment mFragment1;

    ListFragment mFragment2;

    ListFragment mFragment3;

    PagerAdapter mPagerAdapter;

    private TabLayout mTabLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {

        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        mViewPager.setOffscreenPageLimit(2);
        mTabLayout = (TabLayout) findViewById(R.id.toolbar_tab);
        initFragment();
    }


    private void initFragment() {

        mTabLayout.addTab(mTabLayout.newTab().setText("A"));
        mTabLayout.addTab(mTabLayout.newTab().setText("B"));
        mTabLayout.addTab(mTabLayout.newTab().setText("C"));

        if (mFragment1 == null) {
            mFragment1 = new ListFragment();
        }
        if (mFragment2 == null) {
            mFragment2 = new ListFragment();
        }
        if (mFragment3 == null) {
            mFragment3 = new ListFragment();
        }
        mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
        mViewPager.setAdapter(mPagerAdapter);
        mTabLayout.setupWithViewPager(mViewPager);
    }


    public class PagerAdapter extends FragmentPagerAdapter {
        public PagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            if (position == 0) {
                return mFragment1;
            } else if (position == 1) {
                return mFragment2;
            } else if (position == 2) {
                return mFragment3;
            }

            return null;
        }

        @Override
        public int getCount() {
            return 3;
        }

    }
}

运行起来之后,居然是这个样子的。我的标题呢?

TabLayout高端用法(二)_第2张图片
2

官方推荐我们使用setupWithViewPager()方法,来完成这种Tablayout+Viewpager+Fragment组合的数据绑定,上面的代码实在看不出上面问题,但是我的标题呢?我们点进setupWithViewPager()方法查阅了下源码。

private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
            boolean implicitSetup) {
        if (mViewPager != null) {
            // If we've already been setup with a ViewPager, remove us from it
            if (mPageChangeListener != null) {
                mViewPager.removeOnPageChangeListener(mPageChangeListener);
            }
            if (mAdapterChangeListener != null) {
                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
            }
        }

        if (mCurrentVpSelectedListener != null) {
            // If we already have a tab selected listener for the ViewPager, remove it
            removeOnTabSelectedListener(mCurrentVpSelectedListener);
            mCurrentVpSelectedListener = null;
        }

        if (viewPager != null) {
            mViewPager = viewPager;

            // Add our custom OnPageChangeListener to the ViewPager
            if (mPageChangeListener == null) {
                mPageChangeListener = new TabLayoutOnPageChangeListener(this);
            }
            mPageChangeListener.reset();
            viewPager.addOnPageChangeListener(mPageChangeListener);

            // Now we'll add a tab selected listener to set ViewPager's current item
            mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
            addOnTabSelectedListener(mCurrentVpSelectedListener);

            final PagerAdapter adapter = viewPager.getAdapter();
            if (adapter != null) {
                // Now we'll populate ourselves from the pager adapter, adding an observer if
                // autoRefresh is enabled
                setPagerAdapter(adapter, autoRefresh);
            }

            // Add a listener so that we're notified of any adapter changes
            if (mAdapterChangeListener == null) {
                mAdapterChangeListener = new AdapterChangeListener();
            }
            mAdapterChangeListener.setAutoRefresh(autoRefresh);
            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);

            // Now update the scroll position to match the ViewPager's current item
            setScrollPosition(viewPager.getCurrentItem(), 0f, true);
        } else {
            // We've been given a null ViewPager so we need to clear out the internal state,
            // listeners and observers
            mViewPager = null;
            setPagerAdapter(null, false);
        }

        mSetupViewPagerImplicitly = implicitSetup;
    }

这个方法也不算太长,只是对viewPager的状态做了一些检查校验,设置了一些监听。看这一句:

 if (adapter != null) {
                // Now we'll populate ourselves from the pager adapter, adding an observer if
                // autoRefresh is enabled
                setPagerAdapter(adapter, autoRefresh);
            }

setPagerAdapter方法里又调用了populateFromPagerAdapter();

点进populateFromPagerAdapter() 方法第一句就是:

removeAllTabs();

    /**
     * Remove all tabs from the action bar and deselect the current tab.
     */
    public void removeAllTabs() {
        // Remove all the views
        for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
            removeTabViewAt(i);
        }

        for (final Iterator i = mTabs.iterator(); i.hasNext();) {
            final Tab tab = i.next();
            i.remove();
            tab.reset();
            sTabPool.release(tab);
        }

        mSelectedTab = null;
    }

就是这么简单粗暴,直接全部清理掉了,我们且不去探讨源码中的这个逻辑是否有问题,单就setupWithViewPager()这个方法来说,这也算是一个不大不小的坑。所以我们自己在写代码的时候要注意避让。那关于上面我们自己写的代码就要做些调整,我们修改下initFragment方法,主要是代码语句的顺序上做些调整,如下:

  private void initFragment() {

        mTabLayout.addTab(mTabLayout.newTab());
        mTabLayout.addTab(mTabLayout.newTab());
        mTabLayout.addTab(mTabLayout.newTab());

        if (mFragment1 == null) {
            mFragment1 = new ListFragment();
        }
        if (mFragment2 == null) {
            mFragment2 = new ListFragment();
        }
        if (mFragment3 == null) {
            mFragment3 = new ListFragment();
        }
        mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
        mViewPager.setAdapter(mPagerAdapter);
        mTabLayout.setupWithViewPager(mViewPager);

        mTabLayout.getTabAt(0).setText("A");
        mTabLayout.getTabAt(1).setText("B");
        mTabLayout.getTabAt(2).setText("C");
}

代码逻辑上,我们在setupWithViewPager()方法最终remove掉了标题栏之后再重新设置标题,这样就不会出现标题栏消失的问题了。

  • 那既然setupWithViewPager()这方法这么操蛋,我们可不可以不用这个方法来做数据视图绑定,当然也是可以的,但是官方还是推荐我们用setupWithViewPager()方法。我们先把setupWithViewPager()方法注掉看看会如何:
TabLayout高端用法(二)_第3张图片
C7415CAC-CEC5-4640-884D-9295C2F331D0-77553-000ACB2CD7593F7A.gif

好像ViewPager和TabLayout之间的纽带断了,不会联动了。那我们就模仿setupWithViewPager()方法的源码让它们联动起来。我们要做的就是点击TabLayout的时候ViewPager会跟着滚动。滑动ViewPager的时候TabLayout会跟着滚动,那就简单了,我们分别给这二位设置个监听就好了。

       mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));

       mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                mViewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                changeTabStatus(tab, false);
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });

比较起来好像还是setupWithViewPager()更方便些,虽然有坑。

  • 原本计划写自定义TabItem的,写着写着就跑偏了,那还是留在下篇吧。先到这里了。

完整代码:GitHub

你可能感兴趣的:(TabLayout高端用法(二))