简介

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

  • SlidingTabLayout
  • CommonTabLayout
  • SegmentTabLayout

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

官方示例:


源码分析

共有属性名称 格式 描述
tl_indicator_color color 设置显示器颜色
tl_indicator_height dimension 设置显示器高度
tl_indicator_margin_left dimension 设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_top dimension 设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_right dimension 设置显示器margin,当indicator_width大于0,无效
tl_indicator_margin_bottom dimension 设置显示器margin,当indicator_width大于0,无效
tl_indicator_corner_radius dimension 设置显示器圆角弧度
tl_divider_color color 设置分割线颜色
tl_divider_width dimension 设置分割线宽度
tl_divider_padding dimension 设置分割线的paddingTop和paddingBottom
tl_tab_padding dimension 设置tab的paddingLeft和paddingRight
tl_tab_space_equal boolean 设置tab大小等分
tl_tab_width dimension 设置tab固定大小
tl_textsize dimension 设置字体大小
tl_textSelectColor color 设置字体选中颜色
tl_textUnselectColor color 设置字体未选中颜色
tl_textBold boolean 设置字体加粗
tl_textAllCaps boolean 设置字体全大写

1. SlidingTabLayout

1.1 特有属性

特有属性 格式 描述
tl_indicator_width dimension 设置显示器固定宽度
tl_indicator_gravity enum 设置显示器上方(TOP)还是下方(BOTTOM),只对常规显示器有用
tl_indicator_style enum 设置显示器为常规(NORMAL)或三角形(TRIANGLE)或背景色块(BLOCK)
tl_indicator_width_equal_title boolean 设置显示器与标题一样长(only for SlidingTabLayout)
tl_underline_color color 设置下划线颜色
tl_underline_height dimension 设置下划线高度
tl_underline_gravity enum 设置下划线上方(TOP)还是下方(BOTTOM)

1.2 类结构

1.2.1 构造方法


第一个调用第二个,第二个调用第三个,第三个获取自定义属性值。

public SlidingTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flagsetClipChildren(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


方法 描述
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();//清空tabthis.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);}//更新选中未选中状态更新tabupdateTabStyles();}
  /** 创建并添加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() {@Overridepublic void onClick(View v) {int position = mTabsContainer.indexOfChild(v);if (position != -1) {if (mViewPager.getCurrentItem() != position) {if (mSnapOnTabClick) {// transition immediatelymViewPager.setCurrentItem(position, false);} else {//smoothly scroll tomViewPager.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;}@Overridepublic int getCount() {return fragments.size();}@Overridepublic CharSequence getPageTitle(int position) {return titles[position];}@Overridepublic Fragment getItem(int position) {return fragments.get(position);}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {// 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁// super.destroyItem(container, position, object);}@Overridepublic 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的过程)

    @Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {/*** position:当前View的位置* mCurrentPositionOffset:当前View的偏移量比例.[0,1)*/this.mCurrentTab = position;this.mCurrentPositionOffset = positionOffset;/*标签栏根据ViewPager的滚动状态联动,滚动到对应位置*/scrollToCurrentTab();/*触发重绘*/invalidate();}@Overridepublic void onPageSelected(int position) {/*根据ViewPager选中状态调整标签页的选中状态*/updateTabSelection(position);}@Overridepublic 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相关



方法 描述
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相关(未读信息)

方法 描述
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_width dimension 设置显示器固定宽度
tl_indicator_gravity enum 设置显示器上方(TOP)还是下方(BOTTOM),只对常规显示器有用
tl_indicator_style enum 设置显示器为常规(NORMAL)或三角形(TRIANGLE)或背景色块(BLOCK)
tl_indicator_anim_enable boolean 设置显示器支持动画(only for CommonTabLayout)
tl_indicator_anim_duration integer 设置显示器动画时间(only for CommonTabLayout)
tl_indicator_bounce_enable boolean 设置显示器支持动画回弹效果(only for CommonTabLayout)
tl_underline_color color 设置下划线颜色
tl_underline_height dimension 设置下划线高度
tl_underline_gravity enum 设置下划线上方(TOP)还是下方(BOTTOM)
tl_iconWidth dimension 设置icon宽度(仅支持CommonTabLayout)
tl_iconHeight dimension 设置icon高度(仅支持CommonTabLayout)
tl_iconVisible boolean 设置icon是否可见(仅支持CommonTabLayout)
tl_iconGravity enum 设置icon显示位置,对应Gravity中常量值,左上右下(仅支持CommonTabLayout),LEFT,RIGHT,TOP,BOTTOM
tl_iconMargin dimension 设置icon与文字间距(仅支持CommonTabLayout)

2.2 区别于SlidingTabLayout

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

2.3 类结构

2.3.1 构造方法


与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> {@Overridepublic 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相关

方法 描述
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() {@Overridepublic 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

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_enable boolean 设置显示器支持动画
tl_indicator_anim_duration integer 设置显示器动画时间
tl_indicator_bounce_enable boolean 设置显示器支持动画回弹效果
tl_bar_color color 设置整体颜色
tl_bar_stroke_color color 设置边框颜色
tl_bar_stroke_width dimension 设置边框粗细

3.2 区别于CommonTabLayout

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

3.3 类结构

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


4. MsgView

4.1 自定义属性

属性值 格式 描述
mv_backgroundColor color 圆角矩形背景色
mv_cornerRadius dimension 圆角弧度,单位dp
mv_strokeWidth dimension 边框粗细,单位dp
mv_strokeColor color 圆角边框颜色
mv_isRadiusHalfHeight boolean 圆角弧度是高度一半
mv_isWidthHeightEqual boolean 圆角矩形宽高相等,取较宽高中大值

4.2 类结构


项目使用

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


《Android 开源库》 FlycoTabLayout 从头到脚相关推荐

  1. Android开源库集合(控件)

    RecycleView: RecycleView功能增强 https://github.com/Malinskiy/SuperRecyclerView RecycleView功能增强(拖拽,滑动删除, ...

  2. 关于Android开源库分享平台,(GitClub)微信小程序的开发体验

    七八月份的深圳一直在下雨,总有人说雨天适合窝在家看书,对于程序开发者来说更是难得的学习机会.我们502工作室的小伙伴利用这个时间学习了一下微信小程序开发,并上线了一个GitClub小程序,目前功能有些 ...

  3. Android 开源库获取途径整理

    最新内容请见原文: http://www.trinea.cn/android/android-open-project-summary/ 介绍目前收藏 Android 开源库比较多的 GitHub 项 ...

  4. Android开源库集合(UI效果)

    动画效果 粒子动画效果 https://github.com/glomadrian/Grav 水波式loading等待动画 https://github.com/race604/WaveLoading ...

  5. android 日历翻页动画,Android开源库合集:轻松实现Android动态,炫目:日历效果...

    前言: 了解过那种动态,炫目的日历效果吗?你知道是怎么 操作的嘛?是否想过,用UI就可以实现,对,也许你说的对,不过UI只是都是动态效果的一部分.那么今天用Annroid开源库,来告诉你android ...

  6. Android开源库V - Layout:淘宝、天猫都在用的UI框架,赶紧用起来吧!

    前言 V- Layout 是阿里出品的基础 UI 框架,用于快速实现页面的复杂布局,在手机天猫 Android版 内广泛使用 让人激动的是,在上个月V- Layout终于在Github上开源! Git ...

  7. GitHub 上排名前 100 的 Android 开源库介绍

    转自:http://www.codeceo.com/article/github-top-100-android-libs.html 本项目主要对目前 GitHub 上排名前 100 的 Androi ...

  8. Android开源库总结

    自己总结的Android开源项目及库. github排名https://github.com/trending, github搜索:https://github.com/search UI Aweso ...

  9. 排名前100的Android开源库

    本项目主要对目前GitHub上排名前100的Android开源库进行简单的介绍,至于排名完全是根据GitHub搜索Java语言选择「BestMatch」得到的结果,然后过滤了跟Android不相关的项 ...

  10. GitHub 上排名前 100 的 Android 开源库进行简单的介绍

    本文转载于:https://github.com/Freelander/Android_Data/blob/master/Android-Librarys-Top-100.md 本项目主要对目前 Gi ...

最新文章

  1. python怎么画简单图片-python中简单易学的绘图:用turtle画太极图
  2. iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge
  3. jQuery 文件上传插件:uploadify、swfupload
  4. C++编程风格(一)
  5. 经典算法研究系列:十、从头到尾彻底理解傅里叶变换算法、上
  6. 兵乓球- 经典街机游戏-python小游戏源码下载
  7. Fortran基础练习02--循环2
  8. 关于code footprint-reduction-techniques
  9. msclass 文字滚动_MSClass (通用不间断滚动JS封装类)
  10. r4烧录卡内核安装_R4烧录卡NDS内核,绝对可用
  11. 135编辑器html点击图片播放音乐,135编辑器怎么给文章添加音频和视频?135编辑器给文章添加音频和视频教程...
  12. StretchDIBits
  13. Markdown文件中图片自动转云图片和自动生成标题序号
  14. 中间件技术及双十一实践·EagleEye篇
  15. 分享个PS快速替换背景颜色的方法
  16. Linux之恢复删除的数据
  17. Java序列化,碰到serialVersionUID不一致怎么处理?
  18. 抖音最新风控体系研究
  19. [算法设计题] 双栈结构
  20. “System.NullReferenceException”类型的异常在 App_Web_2tjb2nqh.dll 中发生,但未在用户代码中进行处理(C#开发)

热门文章

  1. offer?三方协议?两方协议?毁约?
  2. 男人三十而立,被动收入,越早开始越好
  3. 分享一款CHROME极速下载管理器插件
  4. 上高职业技术学校计算机学几年,上高县职业技术学校简介|上高县职业技术学校介绍...
  5. 关于Java文件路径问题
  6. DevOps读书清单:十本应该放入书架的经典
  7. 计算机系统记忆部件是,什么是计算机系统的记忆部件
  8. STM32通信:IIC (二)
  9. win10安装xshell免费版
  10. 去水印小程序源码【2021年8月更新】