美文网首页Android研究院Android_Speak深入浅出Android
Android TabContainerView 实现底部导航栏

Android TabContainerView 实现底部导航栏

作者: 伪文艺大叔 | 来源:发表于2017-05-07 15:49 被阅读963次

    我们先来看看效果图


    xiaoguo.gif

    从效果图中可以看出V2.0和V1.0的区别
    V2.0的Tab UI是可以实现高度定制的,上面的效果图中Tab UI共有三种样式。
    V1.0的Tab UI是定死的

    实现

    项目的整体架构基本没变,不了解项目架构的可以先看看V1.0的文章(http://www.jianshu.com/p/7cccb5c054da#
    V2.0相对于V1.0来说就是删除了Tab类,新增了一个抽象类AbsTab,同时修改了BaseAdapter类的逻辑,下面我们先来看看AbsTab类

    public abstract class AbsTab {
    
        protected Context mContext;
        /**
         *  Tab在TabHost中的位置,属于第mIndex个
         */
        private int mIndex;
    
        /**
         *  Tab类的资源View布局
         */
        private View mRootView;
    
        /**
         *  当前Tab是否选中
         */
        protected boolean mIsSelected;
    
        /**
         *  Tab选中监听
         */
        private OnTabSelectedListener onTabSelectedListener;
    
        public AbsTab(Context context, int index) {
            mContext = context;
            mIndex = index;
        }
    
        /**
         *  初始化View资源,得到RootView,并添加响应事件
         * @param tab
         * @param layoutResId
         * @return
         */
        protected void inflaterView(final AbsTab tab, @LayoutRes int layoutResId) {
            mRootView = LayoutInflater.from(mContext).inflate(layoutResId, null);
            LinearLayout.LayoutParams rootViewLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            rootViewLp.weight = 1;
            mRootView.setLayoutParams(rootViewLp);
    
            initView(mRootView);
            mRootView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onTabSelectedListener != null) {
                        onTabSelectedListener.onTabSelected(tab);
                    }
                }
            });
        }
    
        /**
         *  设置选中监听
         * @param onTabSelectedListener
         */
        public void setOnTabSelectedListener(OnTabSelectedListener onTabSelectedListener) {
            this.onTabSelectedListener = onTabSelectedListener;
        }
    
    
    
        /**
         *  得到Tab 的RootView
         * @return  View
         */
        public View getTabRootView() {
            return mRootView;
        }
    
        /**
         *  得到Tab Index
         * @return int
         */
        public int getTabIndex() {
            return  mIndex;
        }
    
        /**
         *  显示消息提示
         * @param show 是否显示
         * @param count 显示数量
         */
        public void showMessageTip(boolean show, int count) {};
    
        /**
         * 是否选中该Tab
         * @param isSelected
         */
        protected abstract void tabSelected(boolean isSelected);
        
         /**
         *  初始化RootView里的布局
         * @param rootView
         */
         protected abstract void initView(View rootView);
    }
    

    AbsTab类中的属性和方法上都有明确的注释,无需过多的解释,下面再来看看BaseAdapter类做了什么修改

    public abstract class BaseAdapter {
    
        private Fragment[] mFragmentArray;
        private FragmentManager mFragmentManager;
        protected Context mContext;
    
        public BaseAdapter(Context context, Fragment[] fragments, FragmentManager fragmentManager) {
            mContext = context;
            mFragmentArray = fragments;
            mFragmentManager = fragmentManager;
        }
    
        /**
         *  tab数量
         */
        public int getCount() {
            return mFragmentArray != null ? mFragmentArray.length : 0;
        }
    
        /**
         * fragment 数组
         */
        public Fragment[] getFragmentArray() {
            return mFragmentArray;
        }
    
        public FragmentManager getFragmentManager() {
            return mFragmentManager;
        }
    
        /**
         *  得到tab
         * @return
         */
        public abstract AbsTab getTab(int index);
    

    BaseAdapter类删除了getTextArray,getIconImageArray,getSelectedIconImageArray三个方法,新增加了getTab抽象方法。

    使用

    首先创建一个继承自AbsTab类的Tab类(项目中我默认实现了一个DefaultTab),然后创建一个继承自BaseAdapter类的适配器类(项目中我默认实现了一个DefaultAdapter)

     TabContainerView tabContainerView = (TabContainerView) findViewById(R.id.tab_containerview_main);
     tabContainerView.setAdapter(new DefaultAdapter(this, fragments, getSupportFragmentManager(), getResources().getStringArray(R.array.titleArray),
     getResources().getColor(R.color.colorPrimary), iconImageArray, selectedIconImageArray));
    

    在使用上和V1.0是一致的,最大的不同的是适配器,我们来看看DefaultAdapter适配器

    public class DefaultAdapter extends BaseAdapter {
    
        private String[] mTextArray;
        private int mTextColor = Color.BLACK, mSelectedTextColor;
        private int[] mIconImageArray;
        private int[] mSelectedIconImageArray;
    
        public DefaultAdapter(Context context, Fragment[] fragmentArray, FragmentManager fragmentManager, String[] textArray, int selectTextColor,
                              int[] iconImageArray, int[] selectedIconImageArray) {
           super(context, fragmentArray, fragmentManager);
    
            mTextArray = textArray;
            mSelectedTextColor = selectTextColor;
            mIconImageArray = iconImageArray;
            mSelectedIconImageArray = selectedIconImageArray;
        }
    
    
        @Override
        public AbsTab getTab(int index) {
            DefaultTab defaultTab = new DefaultTab(mContext, index);
            defaultTab.setText(mTextArray[index]);
            defaultTab.setTextColor(mTextColor, mSelectedTextColor);
            defaultTab.setIconImage(mIconImageArray[index], mSelectedIconImageArray[index]);
            return defaultTab;
        }
    
    }
    

    重点来看看getTab方法,方法中创建了一个DefaultTab类,DefaultTab是什么呢?我们进来看看他的具体实现

    public class DefaultTab extends AbsTab {
    
        /**
         *  tab布局信息
         */
        private ImageView mIvIcon;
        private TextView mTvText;
        private MessageCircle mMessageCircleTip;
    
        /**
         *  tab 文本颜色和图片切换资源
         */
        private int mTextColor;
        private int mSelectedTextColor;
        private int mIconImage;
        private int mSelectedIconImage;
    
        public DefaultTab(Context context, int index) {
           super(context, index);
    
            inflaterView(this, R.layout.tab_default);
        }
    
        @Override
        public void tabSelected(boolean isSelected) {
            if (this.mIsSelected == isSelected) return;
    
            mIvIcon.setImageResource(isSelected ? mSelectedIconImage : mIconImage);
            mTvText.setTextColor(isSelected ? mSelectedTextColor : mTextColor);
            this.mIsSelected = isSelected;
        }
    
        @Override
        public void showMessageTip(boolean show, int count) {
            mMessageCircleTip.setVisibility(show ? View.VISIBLE : View.GONE);
            if (count == -1) {
                mMessageCircleTip.setText("");
            } else {
                mMessageCircleTip.setText(count >= 1000 ? "999+" : count + "");
            }
        }
    
        @Override
        protected void initView(View rootView) {
            mIvIcon = (ImageView) rootView.findViewById(R.id.iv_icon);
            mTvText = (TextView) rootView.findViewById(R.id.tv_title);
            mMessageCircleTip = (MessageCircle) rootView.findViewById(R.id.mc_circle);
        }
    
        public void setTextColor(int textColor, int selectedTextColor) {
            mTextColor = textColor;
            mSelectedTextColor = selectedTextColor;
    
            mTvText.setTextColor(mTextColor);
        }
    
        public void setText(String text) {
            mTvText.setText(text);
        }
    
        public void setIconImage(int iconImage, int selectedIconImage) {
            mSelectedIconImage = selectedIconImage;
            mIconImage = iconImage;
    
            mIvIcon.setImageResource(iconImage);
        }
    }
    

    它继承了AbsTab类,重写AbsTab中的抽象方法,getTab方法就是返回一个AbsTab的子类,这个子类需要开发者去创建

    还有setAdapter方法逻辑也做了修改,修改最大的地方就是TabHost中的addTabs方法,V1.0版本中添加Tab是创建一个Tab类,而现在是通过适配器的getTab方法获得AbsTab类,然后添加AbsTab到TabHost当中。

    总结

    到此V2.0中修改的类和业务逻辑就都讲完了;开发中如果需要定制Tab UI, 首先新建一个继承自AbsTab的Tab类(项目中我默认实现了一个DefaultTab), 在新建的Tab类中创建UI布局, 给布局添加数据, 设置布局选中和不选中的UI样式, 然后新建一个继承自BaseAdapter 的适配器类, 重写getTab方法返回一个AbsTab类,就是刚创建的继承自AbsTab的Tab类,然后调用setAdapter的方法即可。

    完整代码:https://github.com/chenpengfei88/TabContainerView/tree/v2.0
    欢迎大家Star,Follow,谢谢。

    相关文章

      网友评论

      • e3b167dad818:楼主 我如果从activity 跳转到 底部导航 其中一个页面 应该怎么操作呀?
        e3b167dad818:@伪文艺大叔 已经找到了 谢谢拉:smile:
        伪文艺大叔:有设置当前选中的方法
      • 金鲨:博主,怎么设置只能点击切换,不让他滑动切换!
        伪文艺大叔:@金鲨 暂时没这个方法
      • e3b167dad818:博主 怎么样 修改图标的大小呢?
        e3b167dad818:@伪文艺大叔 找到修改大小的地方啦 谢谢楼主哦 支持一下:+1:
        e3b167dad818:@伪文艺大叔 我放的自己的图片 稍微有点大 我想调大小 :sob:
        伪文艺大叔: @cs9420 图标是你自己的图片啊

      本文标题:Android TabContainerView 实现底部导航栏

      本文链接:https://www.haomeiwen.com/subject/oasctxtx.html