FlycoTabLayout 简单分析

简介

FlycoTabLayout,是一个比Google原生TabLayout 功能更强大的TabLayout库。目前有3种TabLayout:

  • SlidingTabLayout
  • CommonTabLayout
  • SegmentTabLayout

具体介绍和使用方法参考开源库的Wiki

官方示例:
《FlycoTabLayout 简单分析》

源码分析

共有属性名称格式描述
tl_indicator_colorcolor设置显示器颜色
tl_indicator_heightdimension设置显示器高度
tl_indicator_margin_leftdimension设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_topdimension设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_rightdimension设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_bottomdimension设置显示器margin,当indicator_width大于0,无效
tl_indicator_corner_radiusdimension设置显示器圆角弧度
tl_divider_colorcolor设置分割线颜色
tl_divider_widthdimension设置分割线宽度
tl_divider_paddingdimension设置分割线的paddingTop和paddingBottom
tl_tab_paddingdimension设置tab的paddingLeft和paddingRight
tl_tab_space_equalboolean设置tab大小等分
tl_tab_widthdimension设置tab固定大小
tl_textsizedimension设置字体大小
tl_textSelectColorcolor设置字体选中颜色
tl_textUnselectColorcolor设置字体未选中颜色
tl_textBoldboolean设置字体加粗
tl_textAllCapsboolean设置字体全大写

1. SlidingTabLayout

1.1 特有属性

特有属性格式描述
tl_indicator_widthdimension设置显示器固定宽度
tl_indicator_gravityenum设置显示器上方(TOP)还是下方(BOTTOM),只对常规显示器有用
tl_indicator_styleenum设置显示器为常规(NORMAL)或三角形(TRIANGLE)或背景色块(BLOCK)
tl_indicator_width_equal_titleboolean设置显示器与标题一样长(only for SlidingTabLayout)
tl_underline_colorcolor设置下划线颜色
tl_underline_heightdimension设置下划线高度
tl_underline_gravityenum设置下划线上方(TOP)还是下方(BOTTOM)

1.2 类结构

1.2.1 构造方法

《FlycoTabLayout 简单分析》
第一个调用第二个,第二个调用第三个,第三个获取自定义属性值。

public SlidingTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口
    setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag
    setClipChildren(false);//不限制child在其范围内绘制
    setClipToPadding(false);//滚动时child可以绘制到padding区域

    this.mContext = context;
    mTabsContainer = new LinearLayout(context);//tab容器
    addView(mTabsContainer);//添加到HorizontalScrollView中

    obtainAttributes(context, attrs);//获取自定义属性,常用的方法,TypedArray记得回收

    //获取layout_height属性的值,这个方法比较溜,之前没见过
    String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height");

   //针对height做处理
   if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) {
    } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) {
    } else {
        int[] systemAttrs = {android.R.attr.layout_height};
        TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
        //获取高度
        mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
        a.recycle();
    }
}
1.2.2 ViewPager

《FlycoTabLayout 简单分析》
《FlycoTabLayout 简单分析》

方法描述
setViewPager(ViewPager vp)设置ViewPager内容
public void setViewPager(ViewPager vp, String[] titles)设置ViewPager内容和标签页的标题
    /** 关联ViewPager */
    public void setViewPager(ViewPager vp) {
        if (vp == null || vp.getAdapter() == null) {
            throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !");
        }
        /*本地赋值*/
        this.mViewPager = vp;
        /*重新绑定OnPageChangeListener*/
        this.mViewPager.removeOnPageChangeListener(this);
        this.mViewPager.addOnPageChangeListener(this);
        /*viewpager变化,tab页响应处理处理*/
        notifyDataSetChanged();
    }
    /** 更新数据 */
    public void notifyDataSetChanged() {
        mTabsContainer.removeAllViews();//清空tab
        this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();//获取tab数量,优先级mTitles > ViewPager的默认标题

        /*添加tab*/
        View tabView;
        for (int i = 0; i < mTabCount; i++) {
            tabView = View.inflate(mContext, R.layout.layout_tab, null);
            CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(i) : mTitles.get(i);
            addTab(i, pageTitle.toString(), tabView);
        }
        //更新选中未选中状态更新tab
        updateTabStyles();
    }
  /** 创建并添加tab */
    private void addTab(final int position, String title, View tabView) {
        //设置标题
        TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
        if (tv_tab_title != null) {
            if (title != null) tv_tab_title.setText(title);
        }
        //绑定点击事件,与ViewPager联动
        tabView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = mTabsContainer.indexOfChild(v);
                if (position != -1) {
                    if (mViewPager.getCurrentItem() != position) {
                        if (mSnapOnTabClick) {
                           // transition immediately
                           mViewPager.setCurrentItem(position, false);
                        } else {
                            //smoothly scroll to
                            mViewPager.setCurrentItem(position);
                        }

                        if (mListener != null) {
                            //自定义tab点击事件处理
                            mListener.onTabSelect(position);
                        }
                    } else {
                        if (mListener != null) {
                            //自定义Reselect事件处理
                            mListener.onTabReselect(position);
                        }
                    }
                }
            }
        });

        /** 每一个Tab的布局参数,mTabSpaceEqual 属性控制是否均分 */
        LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ?
                new LinearLayout.LayoutParams(0,  
                LayoutParams.MATCH_PARENT, 1.0f) :
                new 
                LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT,    
                LayoutParams.MATCH_PARENT);
        if (mTabWidth > 0) {
            lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT);
        }
        //添加到Tab容器
        mTabsContainer.addView(tabView, position, lp_tab);
    }
    private void updateTabStyles() {
        //遍历设置标题选中颜色,未选中颜色,字体大小,大小写,粗体字
        for (int i = 0; i < mTabCount; i++) {
            View v = mTabsContainer.getChildAt(i);
            TextView tv_tab_title = (TextView) v.findViewById(R.id.tv_tab_title);
            if (tv_tab_title != null) {
                tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor);
                tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize);
                tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
                if (mTextAllCaps) {
                    tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase());
                }

                if (mTextBold == TEXT_BOLD_BOTH) {
                    tv_tab_title.getPaint().setFakeBoldText(true);
                } else if (mTextBold == TEXT_BOLD_NONE) {
                    tv_tab_title.getPaint().setFakeBoldText(false);
                }
            }
        }
    }
方法描述
setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList< Fragment > fragments)设置ViewPager,标题内容,FragmentActivity和用于显示的Fragment,用来设置ViewPager的Adapter
    /** 关联ViewPager,用于连适配器都不想自己实例化的情况 */
    public void setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList<Fragment> fragments) {
        if (vp == null) {
            throw new IllegalStateException("ViewPager can not be NULL !");
        }

        if (titles == null || titles.length == 0) {
            throw new IllegalStateException("Titles can not be EMPTY !");
        }

        this.mViewPager = vp;
        /*通过传入的参数构建FragmentPagerAdapter,设置到ViewPager*/
        this.mViewPager.setAdapter(new InnerPagerAdapter(fa.getSupportFragmentManager(), fragments, titles));

        this.mViewPager.removeOnPageChangeListener(this);
        this.mViewPager.addOnPageChangeListener(this);
        notifyDataSetChanged();
    }

看下这个内部的InnerPagerAdapter,静态Fragment,不销毁重建,只更新数据内容。

    class InnerPagerAdapter extends FragmentPagerAdapter {
        private ArrayList<Fragment> fragments = new ArrayList<>();
        private String[] titles;

        public InnerPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments, String[] titles) {
            super(fm);
            this.fragments = fragments;
            this.titles = titles;
        }

        @Override
        public int getCount() {
            return fragments.size();
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return titles[position];
        }

        @Override
        public Fragment getItem(int position) {
            return fragments.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁
            // super.destroyItem(container, position, object);
        }

        @Override
        public int getItemPosition(Object object) {
            return PagerAdapter.POSITION_NONE;
        }
    }
方法描述
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)页面滚动,position为当前位置,positionOffset范围[0,1),从当前到下一页,positionOffsetPixels从当前位置滚动的offset,单位px
onPageSelected(int position)选中位置
onPageScrollStateChanged(int state)滚动状态改变

SCROLL_STATE_IDLE(pager处于空闲状态)
SCROLL_STATE_DRAGGING( pager处于正在拖拽中)
SCROLL_STATE_SETTLING(pager正在自动沉降,相当于松手后,pager恢复到一个完整pager的过程)

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        /** * position:当前View的位置 * mCurrentPositionOffset:当前View的偏移量比例.[0,1) */
        this.mCurrentTab = position;
        this.mCurrentPositionOffset = positionOffset;
        /*标签栏根据ViewPager的滚动状态联动,滚动到对应位置*/
        scrollToCurrentTab();
        /*触发重绘*/
        invalidate();
    }

    @Override
    public void onPageSelected(int position) {
        /*根据ViewPager选中状态调整标签页的选中状态*/
        updateTabSelection(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }
  /** HorizontalScrollView滚到当前tab,并且居中显示 */
    private void scrollToCurrentTab() {
        if (mTabCount <= 0) {
            return;
        }

        int offset = (int) (mCurrentPositionOffset * mTabsContainer.getChildAt(mCurrentTab).getWidth());
        /**当前Tab的left+当前Tab的Width乘以positionOffset*/
        int newScrollX = mTabsContainer.getChildAt(mCurrentTab).getLeft() + offset;

        if (mCurrentTab > 0 || offset > 0) {
            /**HorizontalScrollView移动到当前tab,并居中*/
            newScrollX -= getWidth() / 2 - getPaddingLeft();
            calcIndicatorRect();
            newScrollX += ((mTabRect.right - mTabRect.left) / 2);
        }

        if (newScrollX != mLastScrollX) {
            mLastScrollX = newScrollX;
            /** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量 * x:表示离起始位置的x水平方向的偏移量 * y:表示离起始位置的y垂直方向的偏移量 */
            scrollTo(newScrollX, 0);
        }
    }
    /*根据是否选中设置字体颜色和粗体*/
    private void updateTabSelection(int position) {
        for (int i = 0; i < mTabCount; ++i) {
            View tabView = mTabsContainer.getChildAt(i);
            final boolean isSelect = i == position;
            TextView tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);

            if (tab_title != null) {
                tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor);
                if (mTextBold == TEXT_BOLD_WHEN_SELECT) {
                    tab_title.getPaint().setFakeBoldText(isSelect);
                }
            }
        }
    }
1.2.3 Tab相关

《FlycoTabLayout 简单分析》
《FlycoTabLayout 简单分析》
《FlycoTabLayout 简单分析》

方法描述
addNewTab(String title)提供给外部使用的新增tab的方法
    public void addNewTab(String title) {
        View tabView = View.inflate(mContext, R.layout.layout_tab, null);
        if (mTitles != null) {
            mTitles.add(title);
        }

        CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(mTabCount) : mTitles.get(mTabCount);
        addTab(mTabCount, pageTitle.toString(), tabView);
        this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();

        updateTabStyles();
    }
方法描述
setCurrentTab(int currentTab)跳转到指定tab,是否平滑的滚动过去由系统控制
setCurrentTab(int currentTab, boolean smoothScroll)跳转到制指定tab, smoothScroll控制是否平滑的滚动过去
    public void setCurrentTab(int currentTab) {
        this.mCurrentTab = currentTab;
        mViewPager.setCurrentItem(currentTab);

    }

    public void setCurrentTab(int currentTab, boolean smoothScroll) {
        this.mCurrentTab = currentTab;
        mViewPager.setCurrentItem(currentTab, smoothScroll);
    }
1.2.4 Getter和Setter

不做赘述

1.2.5 MsgView相关(未读信息)

《FlycoTabLayout 简单分析》

方法描述
showMsg(int position, int num)显示未读消息,position为tab位置,num小于等于0显示红点,num大于0显示数字
showDot(int position)显示未读红点, position为tab位置
hideMsg(int position)隐藏未读消息, position为tab位置
setMsgMargin(int position, float leftPadding, float bottomPadding)设置未读消息偏移,原点为文字的右上角.当控件高度固定,消息提示位置易控制,显示效果佳
getMsgView(int position)当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置

2. CommonTabLayout

2.1 特有属性

特有属性格式描述
tl_indicator_widthdimension设置显示器固定宽度
tl_indicator_gravityenum设置显示器上方(TOP)还是下方(BOTTOM),只对常规显示器有用
tl_indicator_styleenum设置显示器为常规(NORMAL)或三角形(TRIANGLE)或背景色块(BLOCK)
tl_indicator_anim_enableboolean设置显示器支持动画(only for CommonTabLayout)
tl_indicator_anim_durationinteger设置显示器动画时间(only for CommonTabLayout)
tl_indicator_bounce_enableboolean设置显示器支持动画回弹效果(only for CommonTabLayout)
tl_underline_colorcolor设置下划线颜色
tl_underline_heightdimension设置下划线高度
tl_underline_gravityenum设置下划线上方(TOP)还是下方(BOTTOM)
tl_iconWidthdimension设置icon宽度(仅支持CommonTabLayout)
tl_iconHeightdimension设置icon高度(仅支持CommonTabLayout)
tl_iconVisibleboolean设置icon是否可见(仅支持CommonTabLayout)
tl_iconGravityenum设置icon显示位置,对应Gravity中常量值,左上右下(仅支持CommonTabLayout),LEFT,RIGHT,TOP,BOTTOM
tl_iconMargindimension设置icon与文字间距(仅支持CommonTabLayout)

2.2 区别于SlidingTabLayout

  • 不依赖于ViewPager,可以与其他组件搭配
  • 支持自定义Tab样式,主要体现在常用的图标+文字的形式。
  • SlidingTabLayout继承HorizontalScrollView而CommonTabLayout继承FrameLayout

2.3 类结构

2.3.1 构造方法

《FlycoTabLayout 简单分析》
与SlidingTabLayout实现类似,获取的属性值不太一样而已。多出一个动画的内容,点击某一个Tab后,indicator的移动动画效果

mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP);
mValueAnimator.addUpdateListener(this);
2.3.2 动画相关
class IndicatorPoint {
    public float left;
    public float right;
}

private IndicatorPoint mCurrentP = new IndicatorPoint();
private IndicatorPoint mLastP = new IndicatorPoint();

class PointEvaluator implements TypeEvaluator<IndicatorPoint> {
    @Override
    public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) {
        float left = startValue.left + fraction * (endValue.left - startValue.left);
        float right = startValue.right + fraction * (endValue.right - startValue.right);
        IndicatorPoint point = new IndicatorPoint();
        point.left = left;
        point.right = right;
        return point;
    }
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
    View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
    IndicatorPoint p = (IndicatorPoint) animation.getAnimatedValue();
    mIndicatorRect.left = (int) p.left;
    mIndicatorRect.right = (int) p.right;

    if (mIndicatorWidth < 0) {   //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip

    } else {//indicatorWidth大于0时,圆角矩形以及三角形
        float indicatorLeft = p.left + (currentTabView.getWidth() - mIndicatorWidth) / 2;

        mIndicatorRect.left = (int) indicatorLeft;
        mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth);
    }
    invalidate();
}
2.3.3 Tab相关

《FlycoTabLayout 简单分析》

方法描述
setTabData(ArrayList< CustomTabEntity> tabEntitys)设置tab entity
setTabData(ArrayList< CustomTabEntity> tabEntitys, FragmentActivity fa, int containerViewId, ArrayList< Fragment> fragments)关联数据支持同时切换fragments
notifyDataSetChanged()更新数据
setCurrentTab(int currentTab)设置当前tab
public void setTabData(ArrayList<CustomTabEntity> tabEntitys) {
    if (tabEntitys == null || tabEntitys.size() == 0) {
        throw new IllegalStateException("TabEntitys can not be NULL or EMPTY !");
    }

    this.mTabEntitys.clear();
    /*设置tab标签*/
    this.mTabEntitys.addAll(tabEntitys);
    /*更新数据*/
    notifyDataSetChanged();
}
/** 更新数据 */
public void notifyDataSetChanged() {
    /*清空容器中的tab*/
    mTabsContainer.removeAllViews();
    this.mTabCount = mTabEntitys.size();
    View tabView;
    /*根据图标的gravity构建不同的tab样式,图标支持上下左右*/
    for (int i = 0; i < mTabCount; i++) {
        if (mIconGravity == Gravity.LEFT) {
            tabView = View.inflate(mContext, R.layout.layout_tab_left, null);
        } else if (mIconGravity == Gravity.RIGHT) {
            tabView = View.inflate(mContext, R.layout.layout_tab_right, null);
        } else if (mIconGravity == Gravity.BOTTOM) {
            tabView = View.inflate(mContext, R.layout.layout_tab_bottom, null);
        } else {
            tabView = View.inflate(mContext, R.layout.layout_tab_top, null);
        }
        /*i添加到tag,但从这个类的方法上看,这一步没有什么必要,因为addTab会传入i,addTab中直接使用i就好,但是如果我们在外部拿到tabView,就可以直接指导它的position,不用循环遍历,还是挺方便的*/
        tabView.setTag(i);
        /*添加tab*/
        addTab(i, tabView);
    }
    /*根据选中未选中状态更新tab显示效果*/
    updateTabStyles();
}
/** 创建并添加tab */
private void addTab(final int position, View tabView) {
    /*设置文本内容,title*/
    TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
    tv_tab_title.setText(mTabEntitys.get(position).getTabTitle());
    /*设置图标内容,添加未选中内容,后面根据选中未选中重新刷一次图片*/
    ImageView iv_tab_icon = (ImageView) tabView.findViewById(R.id.iv_tab_icon);
    iv_tab_icon.setImageResource(mTabEntitys.get(position).getTabUnselectedIcon());

    /*设置tabView的点击事件*/
    tabView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            /*前面设置的tab,也就是position*/
            int position = (Integer) v.getTag();
            if (mCurrentTab != position) {
                /*设置当前tab*/
                setCurrentTab(position);
                //有OnTabSelectListener则执行对应的处理
                if (mListener != null) {
                    mListener.onTabSelect(position);
                }
            } else {
                if (mListener != null) {
                    mListener.onTabReselect(position);
                }
            }
        }
    });

    /** 每一个Tab的布局参数 ,根据是否均分设置不同的布局,若宽度不为0,则设置宽度*/
    LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ?
            new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) :
            new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    if (mTabWidth > 0) {
        lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT);
    }
    mTabsContainer.addView(tabView, position, lp_tab);
}
/*和SlidingTabLayout相似,多了一个图标的处理*/
private void updateTabStyles() {
    for (int i = 0; i < mTabCount; i++) {
        View tabView = mTabsContainer.getChildAt(i);
        tabView.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
        TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
        tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor);
        tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize);
//            tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
        if (mTextAllCaps) {
            tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase());
        }

        if (mTextBold == TEXT_BOLD_BOTH) {
            tv_tab_title.getPaint().setFakeBoldText(true);
        } else if (mTextBold == TEXT_BOLD_NONE) {
            tv_tab_title.getPaint().setFakeBoldText(false);
        }

        ImageView iv_tab_icon = (ImageView) tabView.findViewById(R.id.iv_tab_icon);
        if (mIconVisible) {
            iv_tab_icon.setVisibility(View.VISIBLE);
            CustomTabEntity tabEntity = mTabEntitys.get(i);
            iv_tab_icon.setImageResource(i == mCurrentTab ? tabEntity.getTabSelectedIcon() : tabEntity.getTabUnselectedIcon());
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                    mIconWidth <= 0 ? LinearLayout.LayoutParams.WRAP_CONTENT : (int) mIconWidth,
                    mIconHeight <= 0 ? LinearLayout.LayoutParams.WRAP_CONTENT : (int) mIconHeight);
            if (mIconGravity == Gravity.LEFT) {
                lp.rightMargin = (int) mIconMargin;
            } else if (mIconGravity == Gravity.RIGHT) {
                lp.leftMargin = (int) mIconMargin;
            } else if (mIconGravity == Gravity.BOTTOM) {
                lp.topMargin = (int) mIconMargin;
            } else {
                lp.bottomMargin = (int) mIconMargin;
            }

            iv_tab_icon.setLayoutParams(lp);
        } else {
            iv_tab_icon.setVisibility(View.GONE);
        }
    }
}

另外一个setTabData

/** 关联数据支持同时切换fragments */
public void setTabData(ArrayList<CustomTabEntity> tabEntitys, FragmentActivity fa, int containerViewId, ArrayList<Fragment> fragments) {
    /*拿到一个mFragmentChangeManager ,后面setCurrentTab的时候 ,如果这个值不为空,根据这个来切换fragment,实现一种类似FragmentPagerAdapter的效果*/
    mFragmentChangeManager = new FragmentChangeManager(fa.getSupportFragmentManager(), containerViewId, fragments);
    setTabData(tabEntitys);
}
public void setCurrentTab(int currentTab) {
    mLastTab = this.mCurrentTab;
    this.mCurrentTab = currentTab;
    /*遍历更新tab选中未选中状态*/
    updateTabSelection(currentTab);
    /*如果mFragmentChangeManager 不为空,就根据当前选中的tab显示对应的Fragment*/
    if (mFragmentChangeManager != null) {
        mFragmentChangeManager.setFragments(currentTab);
    }
    /*indicator动画效果,计算后重绘*/
    if (mIndicatorAnimEnable) {
        calcOffset();
    } else {
        invalidate();
    }
}
2.3.4 FragmentChangeManager

《FlycoTabLayout 简单分析》

public class FragmentChangeManager {
    private FragmentManager mFragmentManager;
    private int mContainerViewId;
    /** Fragment切换数组 */
    private ArrayList<Fragment> mFragments;
    /** 当前选中的Tab */
    private int mCurrentTab;

    /*构造方法,setTabData的时候看到过*/
    public FragmentChangeManager(FragmentManager fm, int containerViewId, ArrayList<Fragment> fragments) {
        this.mFragmentManager = fm;
        this.mContainerViewId = containerViewId;
        this.mFragments = fragments;
        initFragments();
    }

    /** 初始化fragments */
    private void initFragments() {
        for (Fragment fragment : mFragments) {
            mFragmentManager.beginTransaction().add(mContainerViewId, fragment).hide(fragment).commit();
        }

        setFragments(0);
    }

    /** 界面切换控制,CommonTabLayout中的setCurrentTab方法可以控制*/
    public void setFragments(int index) {
        for (int i = 0; i < mFragments.size(); i++) {
            FragmentTransaction ft = mFragmentManager.beginTransaction();
            Fragment fragment = mFragments.get(i);
            if (i == index) {
                ft.show(fragment);
            } else {
                ft.hide(fragment);
            }
            ft.commit();
        }
        mCurrentTab = index;
    }

    public int getCurrentTab() {
        return mCurrentTab;
    }

    public Fragment getCurrentFragment() {
        return mFragments.get(mCurrentTab);
    }
}
2.3.5 Getter,Setter以及MsgView先关

Getter和Setter方法是属性值的获取和设置,MsgView相关方法和SlidingTabLayout比较相似。

3. SegmentTabLayout

3.1 特有属性

特有属性格式描述
tl_indicator_anim_enableboolean设置显示器支持动画
tl_indicator_anim_durationinteger设置显示器动画时间
tl_indicator_bounce_enableboolean设置显示器支持动画回弹效果
tl_bar_colorcolor设置整体颜色
tl_bar_stroke_colorcolor设置边框颜色
tl_bar_stroke_widthdimension设置边框粗细

3.2 区别于CommonTabLayout

  • 不支持图标,但是可以看做是一个特殊的CommonTabLayout.

3.3 类结构

整体来说,内容基本上和CommonTabLayout,只是少了Icon的对应处理,多出的是Segment样式的处理。

4. MsgView

4.1 自定义属性

属性值格式描述
mv_backgroundColorcolor圆角矩形背景色
mv_cornerRadiusdimension圆角弧度,单位dp
mv_strokeWidthdimension边框粗细,单位dp
mv_strokeColorcolor圆角边框颜色
mv_isRadiusHalfHeightboolean圆角弧度是高度一半
mv_isWidthHeightEqualboolean圆角矩形宽高相等,取较宽高中大值

4.2 类结构

《FlycoTabLayout 简单分析》

项目使用

个人项目Gank.io Android 客户端中使用效果,底部使用的CommonTabLayout,顶部使用的是SlidingTabLayout。整体而言,日常开发过程中,FlycoTabLayout还是很实用的。

《FlycoTabLayout 简单分析》

《FlycoTabLayout 简单分析》

    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/58f70734a0bb9f006ab653b6
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞