安卓项目实战之app首页底部菜单导航栏的实现,支持未读消息数,小红点,未读消息数背景设置

版权声明:转载必须注明本文转自郭子轩的博客 https://blog.csdn.net/gpf1320253667/article/details/82733402

前言

现在市面上几乎所有的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,并重写对应的方法就可以了。

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/82733402