• Android底部导航栏突出(贝塞尔曲线)


    项目中底部导航栏有UI定制需求,效果如下

     在此记录一下实现方案

    1.首先用组合控件的方式把图中图标按位置摆放好

    xml文件如下

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="71dp"
        android:clipChildren="false"
        android:paddingBottom="10dp"
        android:background="#00000000"
        android:gravity="bottom"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
    
    
            <ImageView
                android:id="@+id/first"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_weight="1"
                android:src="@drawable/tab_home_selected2x"/>
    
            <ImageView
                android:id="@+id/second"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_weight="1"
                android:src="@drawable/tab_find_unselected2x"/>
    
            <ImageView
                android:layout_width="44dp"
                android:layout_height="44dp"
                android:src="@drawable/plus"
                android:layout_marginBottom="9dp"
                android:id="@+id/centerIcon"/>
    
    
            
            <ImageView
                android:id="@+id/third"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_weight="1"
                android:src="@drawable/tab_message_unselected2x" />
    
            <ImageView
                android:id="@+id/forth"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_weight="1"
                android:src="@drawable/tab_my_unselected2x"/>
            
    </LinearLayout>

    Android studio内渲染效果

     此处记得把最外层的LinearLayout背景设置为透明:android:background="#00000000"

    2.自定义组合控件BottomNavigationBar继承自LinearLayout,代码如下:

    public class BottomNavigationBar extends LinearLayout implements View.OnClickListener {
    
        private Paint paint;
        private Path path;
        private float width;
        private int currentPosition = 0;
        private onBottomNavClickListener listener;
    
    //    private String[] tabText = {"打卡", "发现", "消息", "我的"};
        //未选中icon
        private int[] normalIcon = {R.drawable.tab_home_unselected2x, R.drawable.tab_find_unselected2x, R.drawable.tab_message_unselected2x, R.drawable.tab_my_unselected2x};
        //选中时icon
        private int[] selectIcon = {R.drawable.tab_home_selected2x, R.drawable.tab_find_selected2x, R.drawable.tab_message_selected2x, R.drawable.tab_my_selected2x};
    
        private ImageView img1, img2, imgCenter, img3, img4;
    
        private ViewPager viewPager;
    
        public BottomNavigationBar(Context context) {
            super(context);
            init(context);
        }
    
        public BottomNavigationBar(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
    
        private void init(Context context) {
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            path = new Path();
            paint.setStyle(Paint.Style.FILL_AND_STROKE);
            paint.setColor(Color.WHITE);
            View view = LayoutInflater.from(context).inflate(R.layout.bottom_navigator, this);
    
            img1 = view.findViewById(R.id.first);
            img2 = view.findViewById(R.id.second);
            imgCenter = view.findViewById(R.id.centerIcon);
            img3 = view.findViewById(R.id.third);
            img4 = view.findViewById(R.id.forth);
            setWillNotDraw(false);
    
            //2、通过Resources获取
            DisplayMetrics dm = getResources().getDisplayMetrics();
            width = dm.widthPixels;
    
            img1.setOnClickListener(this::onClick);
            img2.setOnClickListener(this::onClick);
            img3.setOnClickListener(this::onClick);
            img4.setOnClickListener(this::onClick);
            imgCenter.setOnClickListener(this::onClick);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            paint.setColor(getResources().getColor(R.color.White));
            paint.setShadowLayer(30,0,20,Color.BLACK);
            path.moveTo(0, dip2px(28));
    
            path.lineTo(dip2px(150), dip2px(28));
            path.quadTo(width / 2 - dip2px(30), dip2px(28), width / 2 - dip2px(25), dip2px(18));
            path.quadTo(width / 2, -45, width / 2 + dip2px(25), dip2px(18));
            path.quadTo(width / 2 + dip2px(30), dip2px(28), width - dip2px(150), dip2px(28));
            path.lineTo(width, dip2px(28));
            path.lineTo(width, dip2px(71));
            path.lineTo(0, dip2px(71));
            path.close();
            canvas.drawPath(path, paint);
            super.onDraw(canvas);
        }
    
        /**
         * 根据屏幕的分辨率从 dp 的单位 转成为 px(像素)
         */
        private int dip2px(float dpValue) {
            final float scale = getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
        public void setUpWithViewPager(ViewPager viewPager) {
            this.viewPager = viewPager;
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.first:
                    if (currentPosition == 0) break;
                    setUnSelect(currentPosition);
                    currentPosition = 0;
                    viewPager.setCurrentItem(currentPosition,true);
                    img1.setImageResource(selectIcon[currentPosition]);
                    break;
                case R.id.second:
                    if (currentPosition == 1) break;
                    setUnSelect(currentPosition);
                    currentPosition = 1;
                    viewPager.setCurrentItem(currentPosition,true);
                    img2.setImageResource(selectIcon[currentPosition]);
                    break;
                case R.id.third:
                    if (currentPosition == 2) break;
                    setUnSelect(currentPosition);
                    currentPosition = 2;
                    viewPager.setCurrentItem(currentPosition,true);
                    img3.setImageResource(selectIcon[currentPosition]);
                    break;
                case R.id.forth:
                    if (currentPosition == 3) break;
                    setUnSelect(currentPosition);
                    currentPosition = 3;
                    viewPager.setCurrentItem(currentPosition,true);
                    img4.setImageResource(selectIcon[currentPosition]);
                    break;
                case R.id.centerIcon:
                    if (listener != null) listener.onCenterIconClick();
                    break;
            }
        }
    
        private void setUnSelect(int position) {
            switch (position) {
                case 0:
                    img1.setImageResource(normalIcon[0]);
                    break;
                case 1:
                    img2.setImageResource(normalIcon[1]);
                    break;
                case 2:
                    img3.setImageResource(normalIcon[2]);
                    break;
                case 3:
                    img4.setImageResource(normalIcon[3]);
                    break;
            }
        }
    
        public interface onBottomNavClickListener {
            void onCenterIconClick();
        }
    
        public void setOnListener(onBottomNavClickListener listener){
            this.listener = listener;
        }
    }

    其中只需关注和UI有关的方法(其余方法用于配合ViewPager),也就是init方法、dip2px方法和onDraw方法

    init初始化paint、ptah,并获取屏幕宽度,为onDraw方法画贝塞尔曲线做准备

    onDraw方法绘制按照xml中的尺寸绘制path,由于xml使用的是dp,而实际绘制时需要以px(像素)为单位,所以需要通过dip2px

    进行转换,注意paint.setShadowLayer(30,0,20,Color.BLACK);用于设置阴影,否则颜色相近的情况下边界不明显

    path.quadTo()方法用于绘制贝塞尔曲线,其中的坐标参数是我根据UI给的效果图手动计算滴

    不了解path.quadTo()的同学可以戳这个传送门

    还有一件事,由于viewgroup默认不触发onDraw方法,需要加一句:setWillNotDraw(false);(我的代码中在init方法里面)

    最终效果:

     拿下~

    最最最最后一句,使用的时候不能直接把上方的布局放在BottomNavigationBar之上,因为BottomNavigationBar的高度是按最高的地方算的,直接放上去会出现突起的地方左右侧是空白,建议使用相对布局,然后上方的控件使用marginBottom来卡距离~

    有帮助的话记得点个赞~

  • 相关阅读:
    IO模型(一)
    协程(效率最快、重点)--初识协程、gevent模块、协程爬虫、协程socket(一)
    servlet工作原理解析
    servlet:servletconfig对象和它在开发过程中的应用场景
    servlet:线程安全问题
    servlet:启动的时机
    servlet:第一个demo
    安装myeclipse的一些配置
    同时安装32和64位的jdk
    fiddler:网络限速
  • 原文地址:https://www.cnblogs.com/999625696397qq/p/14652435.html
Copyright © 2020-2023  润新知