前言
现在市面上几乎所有的app运行首页底部都是几个Tab切换的形式,有的可能还有显示消息未读数,小红点等的需求,本人之前在项目中还是使用RadioButton的形式来实现,切换不同的Tab,在代码中控制隐藏和显示不同的Fragment,代码量还是很大的,后来在GitHub了解到一个开源的库FlycoTabLayout,发现其功能也是非常强大,我们可以用他来实现各种效果的Tab切换,包括本篇提到的首页底部Tab切换效果,并且支持未读消息数,小红点等的支持,最主要的是代码量很少,你一定值得拥有。
本篇要实现的效果图展示
FlycoTabLayout简介
FlycoTabLayout是一个Android TabLayout库,目前包含了3个TabLayout,分别是:
1、SlidingTabLayout(依赖于ViewPager一起使用)
主要用于页面顶部导航Tab的实现,参照PagerSlidingTabStrip进行大量修改,新增了部分属性,支持多种风格的指示器显示,支持未读消息数和小红点显示,可以实现的效果如下图所示:
2、CommonTabLayout(不依赖ViewPager可以与其他控件自由搭配使用)
相比于SlidingTabLayout新增了对于Icon图标及其位置的支持,所以我们可以用它来搭建首页底部Tab导航栏,效果如下图:
3、SegmentTabLayout
仿照QQ消息列表头部tab切换的控件,可以实现的效果如下图:
实现步骤:
1、添加FlycoTabLayout库依赖
dependencies{
compile 'com.flyco.tablayout:FlycoTabLayout_Lib:2.0.2@aar'
}
2、首页布局文件activity_main.xml代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tl="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/flContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#b0c0c0"></View>
<com.flyco.tablayout.CommonTabLayout
android:id="@+id/tl_commen"
android:layout_width="match_parent"
android:layout_height="54dp"
android:background="#ffffff"
tl:tl_iconHeight="23dp"
tl:tl_iconWidth="23dp"
tl:tl_indicator_color="@color/title"
tl:tl_indicator_height="0dp"
tl:tl_textSelectColor="@color/title"
tl:tl_textUnselectColor="#66000000"
tl:tl_textsize="13sp"
tl:tl_underline_color="#DDDDDD"
tl:tl_underline_height="1dp" />
</LinearLayout>
自定义属性表如下:
tl_indicator_color color 设置显示器颜色
tl_indicator_height dimension 设置显示器高度
tl_indicator_width 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_indicator_gravity enum 设置显示器上方(TOP)还是下方(BOTTOM),只对常规显示器有用
tl_indicator_style enum 设置显示器为常规(NORMAL)或三角形(TRIANGLE)或背景色块(BLOCK)
tl_underline_color color 设置下划线颜色
tl_underline_height dimension 设置下划线高度
tl_underline_gravity enum 设置下划线上方(TOP)还是下方(BOTTOM)
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_iconWidth dimension 设置icon宽度(仅支持CommonTabLayout)
tl_iconHeight dimension 设置icon高度(仅支持CommonTabLayout)
tl_iconVisible boolean 设置icon是否可见(仅支持CommonTabLayout)
tl_iconGravity enum 设置icon显示位置,对应Gravity中常量值,左上右下(仅支持CommonTabLayout)
tl_iconMargin dimension 设置icon与文字间距(仅支持CommonTabLayout)
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_indicator_width_equal_title boolean 设置显示器与标题一样长(only for SlidingTabLayout)
3、MainActivity的具体实现代码:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tl_commen)
CommonTabLayout tlCommen;
@BindView(R.id.flContent)
FrameLayout flContent;
private ArrayList<Fragment> mFragments = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities = new ArrayList<>();
private String[] mTitles = {"资讯", "圈子", "宝库", "我的"};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initTab();
}
public void initTab() {
for (String mTitle : mTitles) {
if("资讯".equals(mTitle)){
//后面两个值是选中图标和未选中(R.drawable.xxx)不要图标就填0
mTabEntities.add(new TabEntity(mTitle, R.drawable.news_ok2, R.drawable.news_no));
mFragments.add(new FragmentTab01());
}else if("圈子".equals(mTitle)){
mTabEntities.add(new TabEntity(mTitle, R.drawable.dis_ok2, R.drawable.dis_no));
mFragments.add(new FragmentTab02());
}else if("宝库".equals(mTitle)){
mTabEntities.add(new TabEntity(mTitle, R.drawable.ware_ok2, R.drawable.ware_no));
mFragments.add(new FragmentTab03());
}else if("我的".equals(mTitle)){
mTabEntities.add(new TabEntity(mTitle, R.drawable.mine_ok2, R.drawable.mine_no));
mFragments.add(new FragmentTab04());
}
}
tlCommen.setTabData(mTabEntities, this, R.id.flContent, mFragments);
// 未读消息2位数
tlCommen.showMsg(0,32);
tlCommen.setMsgMargin(0, -5, 5);
// 未读消息3位数
tlCommen.showMsg(1, 100);
tlCommen.setMsgMargin(1, -5, 5);
// 设置未读消息红点
tlCommen.showDot(2);
MsgView rtv_2 = tlCommen.getMsgView(2);
if (rtv_2 != null) {
UnreadMsgUtils.setSize(rtv_2, dp2px(7.5f));
}
// 设置未读消息背景
tlCommen.showMsg(3, 5);
tlCommen.setMsgMargin(3, 0, 5);
MsgView rtv_3 = tlCommen.getMsgView(3);
if (rtv_3 != null) {
rtv_3.setBackgroundColor(Color.parseColor("#6D8FB0"));
}
//隐藏指定位置未读红点或消息
tlCommen.hideMsg(2);
}
}
4、TabEntity类的实现,需实现库的CustomTabEntity接口,代码如下:
public class TabEntity implements CustomTabEntity {
public String title;
private int selectedIcon;
private int unSelectedIcon;
public TabEntity(String title, int selectedIcon, int unSelectedIcon) {
this.title = title;
this.selectedIcon = selectedIcon;
this.unSelectedIcon = unSelectedIcon;
}
@Override
public String getTabTitle() {
return title;
}
@Override
public int getTabSelectedIcon() {
return selectedIcon;
}
@Override
public int getTabUnselectedIcon() {
return unSelectedIcon;
}
}
5、接下来就是编写不同Tab对应的Fragment,因为本片旨在实现切换效果,侧重点不在fragment的讲解,所以我们仅仅只需给各fragment在布局文件中设置不同的背景即可,下面我提供一个简单的Fragment的封装类BaseFragment,大家只需继承该BaseFragment实现自己的FragmentTab01.java,FragmentTab02.java,FragmentTab03java,FragmentTab04.java,BaseFragment代码如下:
public abstract class BaseFragment extends Fragment {
private View mRootView;
protected Activity mActivity;
private Unbinder bind;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mActivity = getActivity();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mRootView = inflater.inflate(getContentViewId(),container,false);
bind = ButterKnife.bind(this, mRootView);
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initData();
}
protected void initData(){}
protected abstract int getContentViewId();
@Override
public void onDestroyView() {
super.onDestroyView();
bind.unbind(); // 解绑butterknife
}
}
总结
可以看到在MainActivity中我们只用了一句代码:
tlCommen.setTabData(mTabEntities, this, R.id.flContent, mFragments);
就实现了首页底部Tab栏,是不是觉得很方便。
以上使用CommonTabLayout+FrameLayout+Fragment实现,当然如果你的项目有滑动切换Tab的需求,你也可以选择使用CommonTabLayout+ViewPager+Fragment实现(可能需要使用懒加载Fragment),并且CommonTabLayout还支持Indicator显示器动画效果。
懒加载BaseFragment.java代码
:
public abstract class BaseLazyLoadFragment extends Fragment {
private boolean isFirstLoad = false;
protected View view;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(setLayoutResourceID(),container,false);//让子类实现初始化视图
initView();//初始化事件
isFirstLoad = true;//视图创建完成,将变量置为true
if (getUserVisibleHint()) {//如果Fragment可见进行数据加载
onLazyLoad();
isFirstLoad = false;
}
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
isFirstLoad = false;//视图销毁将变量置为false
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isFirstLoad && isVisibleToUser) {//视图变为可见并且是第一次加载
onLazyLoad();
isFirstLoad = false;
}
}
//数据加载接口,留给子类实现
public abstract void onLazyLoad();
//初始化视图接口,子类必须实现
public abstract int setLayoutResourceID();
//初始化事件接口,留给子类实现
public void initView(){ }
}
如果当前的Fragment要用到懒加载功能的话,只要让他继承自BaseLazyLoadFragment,并重写对应的方法就可以了。