自定义View

自定义 BottomNavigation(底部导航栏)

2020-08-18  本文已影响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"
        />
上一篇下一篇

猜你喜欢

热点阅读