美文网首页自定义View
自定义 BottomNavigation(底部导航栏)

自定义 BottomNavigation(底部导航栏)

作者: 夜沐下的星雨 | 来源:发表于2020-08-18 15:07 被阅读0次

    在Android开发中,我们页面的切换都是使用tabLayout 和viewPager 配合使用,那么我想自定义底部导航栏实现与tabLayout 相似的功能
    这次直接写完美,可以让用户自己定义想要的效果

    1.在valuse 下创建 .xml 文件
    2.创建自定义BottomNavigation 的类
    3.使用 及效果

    在valuse 下创建 .xml 文件

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="BottomNavigationView">
            <!-- private int mMarginLeft;
        private int mMarginTop;
        private int mMarginBottom;
        private int mMarginRight;-->
            <attr name="bottomNavigationLeftMargin" format="dimension|reference"/>
            <attr name="bottomNavigationRightMargin" format="dimension|reference"/>
            <attr name="bottomNavigationTopMargin" format="dimension|reference"/>
            <attr name="bottomNavigationBottomMargin" format="dimension|reference"/>
            <attr name="bottomNavigationDrawableMargin" format="dimension|reference"/>
            <attr name="bottomNavigationDrawableWidth" format="dimension|reference"/>
            <attr name="bottomNavigationDrawableHeight" format="dimension|reference"/>
            <attr name="bottomNavigationDeliverLine" format="boolean"/>
            <attr name="bottomNavigationDeliverLineWidth" format="dimension|reference"/>
            <attr name="bottomNavigationDeliverLineColor" format="color|reference"/>
    
    
            <attr name="bottomNavigationTextColor" format="color|reference"/>
            <attr name="bottomNavigationTextSize" format="dimension|reference"/>
        </declare-styleable>
    </resources>
    

    创建自定义BottomNavigation 的类

    
    public class BottomNavigationView extends ConstraintLayout {
        //水平的dp
        private static final int HORIZONTAL_MARGIN_DP = 32;
        //竖直的dp
        private static final int VERTICAL_MARGIN_DP = 12;
        //list集合
        private List<Integer> mList = new ArrayList<>();
        //集合
        private List<String> mTitle = new ArrayList<>();
        private int mMarginLeft;
        private int mMarginTop;
        private int mMarginBottom;
        private int mMarginRight;
        private int mTextSize;
    
        private int mDrawableMargin;
        private int mDrawableIconWidth;
        private int mDrawableIconHeight;
    
        private int mDeliverLineWidth;//线的宽和高
        private int mDeliverLineColor;//线的颜色
        private ColorStateList mTextColorStateList;
        //适配器
        private NavigationAdapter simpleNavigationAdapter;
        //监听
        private OnTabSelectedListener mTabSelectedListener;
        private boolean mHasDeliverLine; // 是否显示分割线
        private int mCurrentPosition = 0;
        private Paint mLinePaint;
        public BottomNavigationView(Context context) {
            super(context);
        }
    
        public BottomNavigationView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initValue(attrs);
        }
    
        public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initValue(attrs);
        }
    
        private void initValue(AttributeSet attrs) {
            TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.BottomNavigationView);
    
            mMarginLeft = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationLeftMargin,dip2px(HORIZONTAL_MARGIN_DP));
            mMarginRight = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationRightMargin,dip2px(HORIZONTAL_MARGIN_DP));
            mMarginTop = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationTopMargin,dip2px(VERTICAL_MARGIN_DP));
            mMarginBottom = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationBottomMargin, dip2px(VERTICAL_MARGIN_DP));
            mDrawableIconWidth = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationDrawableWidth, 0);
            mDrawableIconHeight = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationDrawableHeight, 0);
    
            mDrawableMargin = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationDrawableMargin,0);
            mTextSize = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationTextSize,0);
    
            mTextColorStateList = array.getColorStateList(R.styleable.BottomNavigationView_bottomNavigationTextColor);
            mHasDeliverLine = array.getBoolean(R.styleable.BottomNavigationView_bottomNavigationDeliverLine, true);
            mDeliverLineWidth = array.getDimensionPixelSize(R.styleable.BottomNavigationView_bottomNavigationDeliverLineWidth, dip2px( 1));
            mDeliverLineColor = array.getColor(R.styleable.BottomNavigationView_bottomNavigationDeliverLineColor, Color.GRAY);
    
            array.recycle();
            mLinePaint = new Paint();
            mLinePaint.setAntiAlias(true);
            mLinePaint.setColor(mDeliverLineColor);
            mLinePaint.setStrokeWidth(mDeliverLineWidth);
            setWillNotDraw(!mHasDeliverLine);
    }
        public void setTabSelectedListener(OnTabSelectedListener tabSelectedListener) {
            this.mTabSelectedListener = tabSelectedListener;
            //如果适配器不等于空
            if (simpleNavigationAdapter != null) {
                //获取每个View
                View tabView = simpleNavigationAdapter.getHolderByPosition(mCurrentPosition).mItemView;
                //传入View 和  控件的tag (presenter)
                mTabSelectedListener.onTabSelect(tabView, (Integer) tabView.getTag());
    
            }
        }
    
        public BottomNavigationView addItem(int drawableId, String title){
            mList.add(drawableId);
            mTitle.add(title);
            return this;
        }
        public void apply(){
            //把drawableId  和 title 应用到布局中
            if (mList.size()>0){
                ArrayList<TabBean> tabBeans = new ArrayList<>();
                for (int i = 0; i < mList.size(); i++) {
                    tabBeans.add(new TabBean(mList.get(i),mTitle.get(i)));
                }
                apply(new SimpleNavigationAdapter(tabBeans));
            }
        }
        public void apply(NavigationAdapter<? extends TabHolder> adapter){
            simpleNavigationAdapter=adapter;
                initView();
            }
        private void initView() {
            removeAllViews();
    
            int minTabWidth = Integer.MAX_VALUE; // 所有tab 中最小的那一个的宽度
            int maxTabHeight = 0; // 所有tab 中最高哪一个的高度
            int maxTabHeightIndex = 0; // 最高tab 的index
            //循环获取适配器中的item
            for (int i = 0; i < simpleNavigationAdapter.getCount(); i++) {
                TabHolder holder = simpleNavigationAdapter.createHolder(this, i);
                addView(holder.mItemView);
                simpleNavigationAdapter.bindData(holder,i);
    
                //计算每一个tab 的宽和高
                holder.mItemView.measure(MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED),MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED));
                if (holder.mItemView.getMeasuredWidth()<minTabWidth){
                    minTabWidth=holder.mItemView.getMeasuredWidth();
                }
                if (holder.mItemView.getMeasuredHeight()>maxTabHeight){
                    maxTabHeight=holder.mItemView.getMeasuredHeight();
                    maxTabHeightIndex=i;
                }
    
            }
    
            //添加约束条件
            ConstraintSet constraintSet = new ConstraintSet();
            constraintSet.clone(this);
            int previousId = 0; // 用于记录上一个 id
            View view;
            int ids [] = new int[simpleNavigationAdapter.getCount()];
    
            // 在使用链的时候,如果是水平的链,那么就只需要把链上的所有控件的垂直方向上的约束添加上即可
            //先把最高的那一个tab 固定好
            view = simpleNavigationAdapter.getHolderByPosition(maxTabHeightIndex).mItemView;
            constraintSet.connect(view.getId(),ConstraintSet.TOP,ConstraintSet.PARENT_ID,ConstraintSet.TOP);
            constraintSet.connect(view.getId(),ConstraintSet.BOTTOM,ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM);
            ids[maxTabHeightIndex] =view.getId();
            previousId=view.getId();
    
            //最高的左边
            if (maxTabHeightIndex>0){
                for (int i=maxTabHeightIndex-1;i>=0;i--){
                    view = simpleNavigationAdapter.getHolderByPosition(i).mItemView;
                    constraintSet.connect(view.getId(), ConstraintSet.BOTTOM, previousId, ConstraintSet.BOTTOM);
                    previousId=view.getId();
                    ids[i]=view.getId();
                }
            }
            if (maxTabHeightIndex<simpleNavigationAdapter.getCount()-1){
                for (int i = maxTabHeightIndex+1; i <simpleNavigationAdapter.getCount() ; i++) {
                    view = simpleNavigationAdapter.getHolderByPosition(i).mItemView;
                    constraintSet.connect(view.getId(),ConstraintSet.BOTTOM,previousId,ConstraintSet.BOTTOM);
                    previousId=view.getId();
                    ids[i]=view.getId();
                }
            }
    
    
            // 第一个参数: 你这个链的左端需要连接到的控件的Id
            // 第二个参数: 你这个链的左端需要连接到第一个参数指定的控件的那一边
            // 第三个参数: 你这个链的右端需要连接大的控件的Id
            // 第四个参数: 你这个链的右端需要连接到第三个参数指定的控件的那一边
            // 第五个参数: 你这个链上多有控件的Id 的一个数组
            // 第六个参数: 权重
            // 第七个参数: 链的模式
            constraintSet.createHorizontalChain(ConstraintSet.PARENT_ID,ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.RIGHT,ids,null,ConstraintSet.CHAIN_SPREAD_INSIDE);
            constraintSet.applyTo(this);
            //获取最大的数值   当padding
            int padding = Math.max(mMarginBottom, mMarginTop);
            //通过最小tab的宽度/2     获取左边距离,右边距离的最小
            int paddingOffset = Math.min(minTabWidth / 2, Math.min(mMarginLeft, mMarginRight));
            //如果有分割线  就让padding+线的宽度, 没有就padding
            int paddingTop = mHasDeliverLine ? padding + mDeliverLineWidth : padding;
            //对适配器监听
            for (int i = 0; i < simpleNavigationAdapter.getCount(); i++) {
                simpleNavigationAdapter.getHolderByPosition(i).mItemView.setPadding(paddingOffset,paddingTop,paddingOffset,padding);
            }
             setPadding(mMarginLeft-paddingOffset,0,mMarginRight-paddingOffset,0);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (mHasDeliverLine){
                canvas.drawLine(0,0,getWidth(),0,mLinePaint);
            }
        }
    
        //创建适配器
        public static class SimpleNavigationAdapter implements NavigationAdapter <SimpleNavigationAdapter.SimpleTabHolder>{
            private int mInd=1000;
            private ArrayList<TabBean> list;
            private ArrayList<SimpleTabHolder> simpleTabHolders=new ArrayList<>();
            private boolean isFirst=true;
            private CheckBox mPreCheckedTab;
            public SimpleNavigationAdapter(ArrayList<TabBean> list) {
                this.list = list;
            }
    
            @Override
            public SimpleTabHolder createHolder(ViewGroup parent, int position) {
                final BottomNavigationView bottomNavigationView=(BottomNavigationView)parent;
                CheckBox checkBox = new CheckBox(parent.getContext());
                checkBox.setTag(position);//得到tag
                checkBox.setId(mInd++);//id
                checkBox.setButtonDrawable(null);//取消掉小方格
                checkBox.setGravity(Gravity.CENTER);
                checkBox.setTextColor(bottomNavigationView.mTextColorStateList);
                int mTextSize = bottomNavigationView.mTextSize;
                if (mTextSize>0){
                    checkBox.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
                }
                //监听
                checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        //判断监听不为空
                        if (bottomNavigationView.mTabSelectedListener != null) {
                            //如果為true
                            if (isChecked) {
                                //判断当前的view 是否是上一个view  不是
                                if (mPreCheckedTab != buttonView) {
                                    //设置点击成功
                                    bottomNavigationView.mTabSelectedListener.onTabSelect(buttonView, (Integer) buttonView.getTag());
                                    //如果上一个tab 不为空
                                    if (mPreCheckedTab!=null){
                                        //取消
                                        bottomNavigationView.mTabSelectedListener.onTabUnSelect(mPreCheckedTab, (Integer) mPreCheckedTab.getTag());
    
                                        //以为再次点击后上一次没有设置为false 又因为点击了当前  没法将再次执行下面代码
                                        //所有定义一个空的变量并将mPreCheckedTab赋值
                                        CheckBox temp = mPreCheckedTab;
                                        //将当前的赋值给mPreCheckedTab
                                        mPreCheckedTab= (CheckBox) buttonView;
                                        //temp 是否选中设为false
                                        temp.setChecked(false);
                                        //当不上多次点击就不必要向下执行
                                        return;
                                    }
                                    //当当前的view 复值给mPreCheckedTab 后 取消的没法执行mPreCheckedTab= (CheckBox) buttonView;代码
                                    mPreCheckedTab= (CheckBox) buttonView;
                                }
    
                            } else {
                                if(mPreCheckedTab==buttonView){
                                    bottomNavigationView.mTabSelectedListener.onTabReSelected(buttonView, (Integer) buttonView.getTag());
                                    mPreCheckedTab.setChecked(true);
                                }
                            }
                        }
                    }
                });
    
    
                SimpleTabHolder simpleTabHolder = new SimpleTabHolder(checkBox);
                simpleTabHolders.add(simpleTabHolder);
                return simpleTabHolder;
            }
    
            @Override
            public void bindData(SimpleTabHolder view, final int position) {
                //获取icon
                Drawable drawable = view.mItemView.getResources().getDrawable(list.get(position).getDrawable());
                //获取当前的 父布局
                BottomNavigationView navigationView = (BottomNavigationView) view.mItemView.getParent();
                //进行判断 如果icon 的宽和高都大于0
                if (navigationView.mDrawableIconWidth > 0 && navigationView.mDrawableIconHeight > 0) {
                    //就设置icon 的边距
                    drawable.setBounds(0, 0, navigationView.mDrawableIconWidth, navigationView.mDrawableIconHeight);
                    view.mItemView.setCompoundDrawables(null, drawable, null, null);
                } else {
                    view.mItemView.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
                }
    
                view.mItemView.setText(list.get(position).getTitle());
    
                if (navigationView.mCurrentPosition==position){
                    view.mItemView.setChecked(true);
                    if (navigationView.mTabSelectedListener==null){
                        mPreCheckedTab=view.mItemView;
                    }
                }
            }
    
            @Override
            public int getCount() {
                return list==null?0:list.size();
            }
    
            @Override
            public SimpleTabHolder getHolderByPosition(int position) {
                return simpleTabHolders.size()==0?null:simpleTabHolders.get(position);
            }
    
            public class SimpleTabHolder extends TabHolder<CheckBox> {
                SimpleTabHolder(CheckBox view) {
                    super(view);
                }
            }
    
        }
    
        private static abstract class TabHolder<T extends View>{
             protected T  mItemView;
            public TabHolder(T t){
             this.mItemView=t;
            }
        }
        //创建适配器的接口
        public interface NavigationAdapter <TH extends TabHolder>{
            TH createHolder(ViewGroup parent, int position);
            void bindData(TH view, int position);
            int getCount();
            TH getHolderByPosition(int position);
        }
    
        //创建bean类
        public static class TabBean{
            private int drawable;
            private String title;
    
            public TabBean(int drawable, String title) {
                this.drawable = drawable;
                this.title = title;
            }
    
            public int getDrawable() {
                return drawable;
            }
    
            public void setDrawable(int drawable) {
                this.drawable = drawable;
            }
    
            public String  getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
        }
    
        //dp  转px
        public  int dip2px(float dpValue) {
            final float scale = getContext().getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
        public interface OnTabSelectedListener {
    
            void onTabSelect(View tab, int position);
    
            void onTabUnSelect(View tab, int position);
    
            void onTabReSelected(View tab, int position);
        }
    
    }
    
    

    使用 及效果

     <com.wds.bannerlib.navigation.BottomNavigationView
            android:id="@+id/navigation"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:bottomNavigationTextColor="@color/bottom_navigation_text_color"
            app:bottomNavigationTextSize="10dp"
            app:bottomNavigationLeftMargin="27dp"
            app:bottomNavigationRightMargin="27dp"
            app:bottomNavigationTopMargin="6dp"
            app:bottomNavigationBottomMargin="6dp"
            app:bottomNavigationDeliverLine="true"
            app:bottomNavigationDeliverLineWidth="1dp"
            app:bottomNavigationDeliverLineColor="@color/gray"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            />
    

    相关文章

      网友评论

        本文标题:自定义 BottomNavigation(底部导航栏)

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