Android selects the bottom navigation bar that highlights the background effect

Today I saw a bottom navigation selection highlight effect in the group like this

I just thought about how to do this. I remember that something like salted fish is protruding in the middle, unlike this one, whichever is selected will be prominent.

the first method

Simple and fast, let the UI help cut a few pictures with prominent backgrounds,

Select to switch pictures is simple and rude

Find a small partner in the group and ask for the cut image of the UI. The 6 pictures are the same size, without prominent background.

So I wondered if there is a second way to achieve

Baidu has been working for a long time, maybe the method I am looking for is wrong, maybe no one has encountered such a UI.

What to do, think for yourself, calm down and look at the UI effect, and found that the prominent place is a bit like a Bezier curve

Analyze in detail, if the highlight is a Bezier curve, then how to draw a straight line so that the fixed position is prominent?

Bezier curve is the api in Path, and Path can draw lines continuously,

Then it's easy to achieve, set the starting point directly in front 

mPath.moveTo(0, 0);//起始点

Then there is a direct call in the middle 

mPath.lineTo(x,y);

Call the second-order Bezier curve if you need to highlight

mPath.quadTo(x1,y1,x2,y2);

Sure enough, the effect is like this

Yes, it’s the first step, but after careful observation, there is a white background underneath the house, and the prominent place should also have a white background. How to do it!

I checked Path and Paint Api again and found that there is a way to achieve this effect

mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

The brush should be set to this style

mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.close(); //封闭path路径

Path path is fully occupied

Then you can achieve the effect

Remember to set the brush color to white

mPaint.setColor(Color.WHITE);

It really works!

The effect of a meal layout is like this

So ugly

But the first step to success has been taken, continue to improve

First of all, this prominent arc seems to be different from the UI

It was another analysis and found that there are three curves when it is prominent.

Then there will be three control points

The painting is a bit ugly

a b c 都是控制点

1-2 是第一段

2-3 是第二段

3-4 是第三段

Three segments correspond to three control points

So we have to draw a fourth-order Bezier curve

As a result, up to three levels are supported in Path. . . . . . .

There is no way but to split into three

According to the figure, the positions of the abc control points and the 1 2 3 4 points can be calculated

The phone screen length is assumed to be w

Now there are three modules at the bottom, so the distance occupied by one module is i=w/3

Then 1 is the starting point 

b is the center point of i

4 is point i

The highest height in the Y direction is -y (note that it is a negative number)

If the lengths of the three Bezier curves are the same, the positions of the points are

1(0,0)

2(i/2/2,y/2)

3(i-i/2/2,y/2)

4(i,0)

a(i/2/2/2,y/2/2/2)

b(i/2,y)

c(i-i/2/2/2,y/2/2/2)

Then we put these points into the Bezier curve

//第一条贝塞尔曲线                    a                                     2
mPath.quadTo(i / 2 / 2 / 2 , -(minHeight / 2 / 2 / 2), i / 2 / 2 , -(minHeight / 2));
//第二条贝塞尔曲线                 b                       3
mPath.quadTo(i / 2 + i , -minHeight, i - i / 2 / 2 + i , -(minHeight / 2));
//第三条贝塞尔曲线                   c                       4
mPath.quadTo(i - i / 2 / 2 / 2 , -(minHeight / 2 / 2 / 2), i + i * (count - 1), 0);

Then this is the first module, the calculation of the subsequent modules is to add a few i-values

Module starts from 1, now there are 3 module values ​​(1 2 3) 

//第一条贝塞尔曲线                    a                                     2
mPath.quadTo(i / 2 / 2 / 2 + i * (count - 1), -(minHeight / 2 / 2 / 2), i / 2 / 2 + i * (count - 1) + minHeight / 5, -(minHeight / 2));
//第二条贝塞尔曲线                 b                       3
mPath.quadTo(i / 2 + i * (count - 1), -minHeight, i - i / 2 / 2 + i * (count - 1) - minHeight / 5, -(minHeight / 2));
//第三条贝塞尔曲线                   c                       4
mPath.quadTo(i - i / 2 / 2 / 2 + i * (count - 1), -(minHeight / 2 / 2 / 2), i + i * (count - 1), 0);

In this way, you can directly set the count value and redraw to complete the click switch

All codes

package com.wavewave.mylibrary;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

/**
 * @author wavewave
 * @CreateDate: 2020/10/28 10:23 AM
 * @Description: 底部导航 选中突出View 背景
 * @Version: 1.0
 */
public class BottomOutNavigation extends View {
    private Paint mPaint;
    //起始点
    private int beginY = dip2px(0);
    //边距
    private int margin = dip2px(0);
    /**
     * 默认 突出最高点 Y
     */
    private int minHeight = dip2px(40);

    //第几个从0开始
    private int count = 1;
    /**
     * 默认3个 根据实际情况写
     */
    private int maxCount = 3;
    public static String TAG = "LineView";
    private int height;
    private int width;
    private Path mPath;

    public BottomOutNavigation(Context context) {
        this(context, null);
    }

    public BottomOutNavigation(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BottomOutNavigation(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPath = new Path();
        mPaint = new Paint();
//        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(Color.WHITE);
        mPaint.setAntiAlias(true);//抗锯齿
        //2、通过Resources获取
        DisplayMetrics dm = getResources().getDisplayMetrics();
        height = dm.heightPixels;
        width = dm.widthPixels;
    }

    /**
     * 设置选择
     *
     * @param count
     */
    public void setCount(int count) {
        this.count = count;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int i = width / maxCount;//单个所占大小
        Log.d(TAG, "i:" + i);
        mPath.reset();
        mPath.moveTo(0, 0);//起始点
        mPath.lineTo(margin + i * (count - 1), 0);
//
        //第一条贝塞尔曲线                    a                                     2
        mPath.quadTo(i / 2 / 2 / 2 + i * (count - 1), -(minHeight / 2 / 2 / 2), i / 2 / 2 + i * (count - 1) + minHeight / 5, -(minHeight / 2));
        //第二条贝塞尔曲线                 b                       3
        mPath.quadTo(i / 2 + i * (count - 1), -minHeight, i - i / 2 / 2 + i * (count - 1) - minHeight / 5, -(minHeight / 2));
        //第三条贝塞尔曲线                   c                       4
        mPath.quadTo(i - i / 2 / 2 / 2 + i * (count - 1), -(minHeight / 2 / 2 / 2), i + i * (count - 1), 0);

        mPath.lineTo(width, beginY);
        mPath.lineTo(getWidth(), getHeight());
        mPath.lineTo(0, getHeight());
        mPath.close(); //封闭path路径
        canvas.drawPath(mPath, mPaint);
    }
    /**
     * 根据屏幕的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int dip2px(float dpValue) {
        final float scale = getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

}

That's it. I calculated the part that stood out in the middle by dividing it evenly and subtracting a little distance. Upper gif

I put the code on github, you can download and run the demo directly to find out!

github code link

Guess you like

Origin blog.csdn.net/Small_Wave_Wave/article/details/109328072