Android Draw the custom view as in google pay

2020-02-01 05:11发布

问题:

This question has already been asked and an answer has been accepted here but the accepted answer is not what I am looking for. I want to use a customview in which the notch takes the width + some margin of the view which it is going over, like the pay icon in the above image. While looking into the bottomappbar which houses a fab like this I saw a class called the edge treatment class I guess that can be used as well. I am not posting my customview code rightnow as all I could draw is a rectangle.

回答1:

You need to draw the curves using Cubic Bézier Curve. A good article to know how to draw such curves is written by Bartosz Ciechanowski, here.

I have developed a View to draw a shape like google pay which inherits from FrameLayout. The source code is available on its github repository (but not documented yet!). However, add the following lines to your app level build.gradle file:

repositories {
    jcenter()
}

dependencies {
    implementation 'com.aminography:beziercurvebulgelayout:1.0.2'
}

Then you can use it in xml layout files as following:

<com.aminography.view.BezierCurveBulgeLayout
    android:id="@+id/bulgeLayout"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    app:bulgeType="bulge"
    app:bulgeColor="@color/colorPrimary"
    app:curveWidth="32dp"
    app:flatWidth="56dp"
    app:flatHeight="24dp">

    <android.support.v7.widget.AppCompatImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@android:drawable/ic_menu_compass"/>

</com.aminography.view.BezierCurveBulgeLayout>

.

Its shape and color is customizable to achieve the target shape by changing below attributes:



回答2:

In addition of above answer of @aminography

If anyone looking for answer in java here is the working code in JAVA

BezierCurveBulgeLayout

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.widget.FrameLayout;

public class BezierCurveBulgeLayout extends FrameLayout {

    AttributeSet attrs;
    int defStyleAttr = 0;
    int defStyleRes = 0;

    private int curveWidth;
    private int flatWidth;
    private int flatHeight;
    private int bulgeColor;
    private BulgeType bulgeType;


    private Path path = new Path();
    private Paint paint = new Paint();

    private Point startCurveStartPoint = new Point();
    private Point startCurveEndPoint = new Point();
    private Point startCurveFirstControlPoint = new Point();
    private Point startCurveSecondControlPoint = new Point();

    private Point endCurveStartPoint = new Point();
    private Point endCurveEndPoint = new Point();
    private Point endCurveFirstControlPoint = new Point();
    private Point endCurveSecondControlPoint = new Point();

    public BezierCurveBulgeLayout(@NonNull Context context) {
        super(context);
        init();
    }


    public BezierCurveBulgeLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.attrs = attrs;

        init();
    }

    public BezierCurveBulgeLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.attrs = attrs;
        this.defStyleAttr = defStyleAttr;
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public BezierCurveBulgeLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.attrs = attrs;
        this.defStyleAttr = defStyleAttr;
        this.defStyleRes = defStyleRes;
        init();
    }


    private void init() {

        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BezierCurveBulgeLayout, defStyleAttr, defStyleRes);

        curveWidth = a.getDimensionPixelSize(R.styleable.BezierCurveBulgeLayout_curveWidth, 0);
        flatWidth = a.getDimensionPixelSize(R.styleable.BezierCurveBulgeLayout_flatWidth, 0);
        flatHeight = a.getDimensionPixelSize(R.styleable.BezierCurveBulgeLayout_flatHeight, 0);
        bulgeType = BulgeType.values()[a.getInt(R.styleable.BezierCurveBulgeLayout_bulgeType, BulgeType.BULGE.ordinal())];
        bulgeColor = a.getColor(R.styleable.BezierCurveBulgeLayout_bulgeColor, Color.WHITE);

        a.recycle();

        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setColor(bulgeColor);
        setBackgroundColor(Color.TRANSPARENT);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        int viewWidth = getWidth();
        int viewHeight = getHeight();

        int baseY = 0;
        int flatY = 0;

        if (bulgeType == BulgeType.BULGE) baseY = flatHeight;
        else flatY = flatHeight;


        startCurveStartPoint.set(viewWidth / 2 - flatWidth / 2 - curveWidth * 7 / 6, baseY);
        startCurveEndPoint.set(viewWidth / 2 - flatWidth / 2, flatY);


        endCurveStartPoint.set(viewWidth / 2 + flatWidth / 2, flatY);
        endCurveEndPoint.set(viewWidth / 2 + flatWidth / 2 + curveWidth * 7 / 6, baseY);


        startCurveFirstControlPoint.set(startCurveStartPoint.x + curveWidth * 5 / 8, startCurveStartPoint.y);
        startCurveSecondControlPoint.set(startCurveEndPoint.x - curveWidth / 2, startCurveEndPoint.y);

        endCurveFirstControlPoint.set(endCurveStartPoint.x + curveWidth / 2, endCurveStartPoint.y);
        endCurveSecondControlPoint.set(endCurveEndPoint.x - curveWidth * 5 / 8, endCurveEndPoint.y);

        path.reset();
        path.moveTo(0f, baseY);
        path.lineTo((float) startCurveStartPoint.x, (float) startCurveStartPoint.y);

        path.cubicTo(
                (float) startCurveFirstControlPoint.x, (float) startCurveFirstControlPoint.y,
                (float) startCurveSecondControlPoint.x, (float) startCurveSecondControlPoint.y,
                (float) startCurveEndPoint.x, (float) startCurveEndPoint.y
        );

        path.lineTo((float) endCurveStartPoint.x, (float) endCurveStartPoint.y);

        path.cubicTo(
                (float) endCurveFirstControlPoint.x, (float) endCurveFirstControlPoint.y,
                (float) endCurveSecondControlPoint.x, (float) endCurveSecondControlPoint.y,
                (float) endCurveEndPoint.x, (float) endCurveEndPoint.y
        );

        path.lineTo((float) viewWidth, (float) baseY);
        path.lineTo((float) viewWidth, (float) viewHeight);
        path.lineTo(0f, (float) viewHeight);
        path.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(path, paint);
    }

    public enum BulgeType {
        BULGE,
        NOTCH
    }

}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BezierCurveBulgeLayout">
        <attr name="curveWidth" format="dimension"/>
        <attr name="flatWidth" format="dimension"/>
        <attr name="flatHeight" format="dimension"/>
        <attr name="bulgeColor" format="color"/>
        <attr name="bulgeType" format="enum">
            <enum name="bulge" value="0"/>
            <enum name="notch" value="1"/>
        </attr>
    </declare-styleable>
</resources>

Now use like this in your layout.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00fff7"
    android:gravity="center"
    tools:context=".MainActivity">

    <neel.com.demo.BezierCurveBulgeLayout
        android:id="@+id/bulgeLayout"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        app:bulgeColor="@color/colorPrimary"
        app:bulgeType="bulge"
        app:curveWidth="32dp"
        app:flatHeight="20dp"
        app:flatWidth="80dp">

        <ImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:src="@drawable/ic_fav" />

    </neel.com.demo.BezierCurveBulgeLayout>

</LinearLayout>

OUTPUT