Android 自定义带箭头对话框背景

简介

自定义drawable,带箭头对话框背景,三角形+矩形组合。应用于对话框背景、提示语背景等。

可设置箭头显示方向、箭头大小、箭头导圆角尺寸、矩形尺寸、矩形导圆角尺寸、背景颜色、drawable padding值(影响宿主控件padding)。

希望能给大家带来方便。

一、效果图

示例代码:

        ArrowsDrawable drawable = new ArrowsDrawable(ArrowsDrawable.BOTTOM, ConvertUtils.dp2px(8));
        drawable.setArrowsRadius(ConvertUtils.dp2px(3));
        drawable.setArrowsHeight(ConvertUtils.dp2px(8));
        drawable.setArrowsWidth(ConvertUtils.dp2px(12));
        drawable.setPadding(ConvertUtils.dp2px(10));
        drawable.setColor(Color.DKGRAY);
        textView.setBackground(drawable);

二、API详解

构造方法

//箭头方向,箭头高度 
public ArrowsDrawable(@Position int arrowsPosition, float arrowsHeight)

设置drawable padding

drawable设置padding之后,会影响使用该drawable控件的padding。例如示例中drawable设置了padding,即使用者TextView设置了相同的padding值,这是drawable的特性。另外,该api将箭头高度加到对应的padding值中了,比如箭头方向朝上,drawable设置4个padding均为10dp, 那么drawable paddingTop的值 = 10dp + 箭头高度值,其余均是10dp。

 public void setPadding(int p) 
 public void setPadding(int left, int top, int right, int bottom) 
复制代码
设置箭头方向
复制代码
LEFT, RIGHT, TOP, BOTTOM
   public void setArrowsPosition(@Position int arrowsPosition)
复制代码
设置箭头圆角

设置箭头突出角的圆角尺寸,给该角导圆角

 public void setArrowsRadius(float arrowsRadius)

其它API

//设置箭头高度
        drawable.setArrowsHeight(ConvertUtils.dp2px(8));
        //设置箭头宽度,默认是箭头高度的2倍
        drawable.setArrowsWidth(ConvertUtils.dp2px(12));
        //设置背景色
        drawable.setColor(Color.DKGRAY);

扩展

如果 drawable需要设置margin效果, 可以通过InsetDrawable实现。

        InsetDrawable insetDrawable = new InsetDrawable(arrowsDrawable, padding);
        tv.setBackground(insetDrawable);

三、源码

package com.ttkx.deviceinfo.bkchart;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.GradientDrawable;
import android.view.Gravity;

import com.blankj.utilcode.util.ConvertUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import androidx.annotation.IntDef;


/**
 * 带箭头的Drawable
 * <p>
 * Created by liuyu
 * on 2021/2/4
 */
public class ArrowsDrawable extends GradientDrawable {

    //箭头显示位置
    public static final int LEFT = Gravity.LEFT;
    public static final int TOP = Gravity.TOP;
    public static final int RIGHT = Gravity.RIGHT;
    public static final int BOTTOM = Gravity.BOTTOM;
    public static final int CENTER = Gravity.CENTER;

    @IntDef({LEFT, RIGHT, TOP, BOTTOM})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Position {
    }

    private Paint mPaint;
    private int arrowsPosition = BOTTOM;
    private float arrowsRadius = ConvertUtils.dp2px(2);
    private float arrowsHeight = ConvertUtils.dp2px(5);
    private float arrowsWidth;
    private float mRadius = ConvertUtils.dp2px(8);
    private float arrowsPadding = mRadius + ConvertUtils.dp2px(10);//箭头padding
    private Path mPath;

    public ArrowsDrawable(@Position int arrowsPosition, float arrowsHeight) {
        this.arrowsPosition = arrowsPosition;
        this.arrowsHeight = arrowsHeight;
        initPaint();
    }

    private Rect mPadding;

    @Override
    public boolean getPadding(Rect padding) {
        if (mPadding != null) {
            padding.set(mPadding);
            return true;
        } else {
            return super.getPadding(padding);
        }
    }

    public void setPadding(int p) {
        setPadding(p, p, p, p);
    }

    /**
     * 设置drawable padding
     *
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    public void setPadding(int left, int top, int right, int bottom) {
        if (mPadding == null) {
            mPadding = new Rect();
        }
        switch (arrowsPosition) {
            case LEFT:
                left += arrowsHeight;
                break;
            case TOP:
                top += arrowsHeight;
                break;
            case RIGHT:
                right += arrowsHeight;
                break;
            default:
                bottom += arrowsHeight;
                break;
        }
        mPadding.set(left, top, right, bottom);
        invalidateSelf();
        //super.setPadding(left, top, right, bottom);api=29,10.0
    }

    /**
     * 设置箭头方向
     *
     * LEFT, RIGHT, TOP, BOTTOM
     * @param arrowsPosition
     */
    public void setArrowsPosition(@Position int arrowsPosition) {
        this.arrowsPosition = arrowsPosition;
        invalidateSelf();
    }

    /**
     * 设置箭头圆角
     *
     * @param arrowsRadius
     */
    public void setArrowsRadius(float arrowsRadius) {
        this.arrowsRadius = arrowsRadius;
        invalidateSelf();
    }

    /**
     * 设置箭头高度
     *
     * @param arrowsHeight
     */
    public void setArrowsHeight(float arrowsHeight) {
        this.arrowsHeight = arrowsHeight;
        invalidateSelf();
    }

    /**
     * 设置箭头宽度
     *
     * @param arrowsWidth
     */
    public void setArrowsWidth(float arrowsWidth) {
        this.arrowsWidth = arrowsWidth;
    }

    /**
     * 设置箭头离矩形边的距离(参照:矩形的左边或上边)
     *
     * @param arrowsPadding
     */
    public void setArrowsPadding(float arrowsPadding) {
        this.arrowsPadding = arrowsPadding;
        invalidateSelf();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(1);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);

        mPath = new Path();
    }

    @Override
    protected void onBoundsChange(Rect r) {
        super.onBoundsChange(r);
        int width = r.width();
        int height = r.height();

        float aw = this.arrowsWidth;
        if (aw <= 0) {
            aw = arrowsHeight * 2;//默认情况下箭头宽度是高度的2倍
        }
        float ar = aw / 2;

        //三个点的坐标和矩形
        float px1 = r.left + arrowsPadding;
        float py1 = r.top + height - this.arrowsHeight;
        float px2 = px1 + aw;
        float py2 = py1;
        float px3 = px1 + ar;
        float py3 = py1 + this.arrowsHeight;
        //矩形起始点坐标
        float sx = r.left;
        float sy = r.top;
        //矩形宽高度
        float rectWidth = width;
        float rectHeight = height - this.arrowsHeight;
        switch (arrowsPosition) {
            case LEFT:
                px1 = r.left + this.arrowsHeight;
                py1 = r.top + arrowsPadding + this.arrowsHeight;
                px2 = px1;
                py2 = py1 + aw;
                px3 = px1 - this.arrowsHeight;
                py3 = py1 + ar;

                sx = px1;
                sy = r.top;
                rectWidth = width - this.arrowsHeight;
                rectHeight = height;
                break;
            case TOP:
                px1 = r.left + arrowsPadding;
                py1 = r.top + this.arrowsHeight;
                px2 = px1 + aw;
                py2 = py1;
                px3 = px1 + ar;
                py3 = py1 - this.arrowsHeight;

                sx = r.left;
                sy = r.top + this.arrowsHeight;
                rectWidth = width;
                rectHeight = height - this.arrowsHeight;
                break;
            case RIGHT:
                px1 = r.left + width - this.arrowsHeight;
                py1 = r.top + arrowsPadding;
                px2 = px1;
                py2 = py1 + aw;
                px3 = px1 + this.arrowsHeight;
                py3 = py1 + ar;

                sx = r.left;
                sy = r.top;
                rectWidth = width - this.arrowsHeight;
                rectHeight = height;
                break;
        }

        mPath.reset();
        addTrianglePath(mPath, arrowsRadius, px1, py1, px2, py2, px3, py3);

        RectF rectF = new RectF(sx, sy, sx + rectWidth, sy + rectHeight);
        mPath.addRoundRect(rectF, mRadius, mRadius, Path.Direction.CW);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        canvas.drawPath(mPath, mPaint);
    }

    /**
     * 添加三角形path
     *
     * @param path  路径
     * @param radiu 三角形突出角圆角半径
     * @param x1    与内容框连接1点X坐标 (起始点)
     * @param y1    与内容框连接1点y坐标
     * @param x2    与内容框连接2点X坐标
     * @param y2    与内容框连接2点Xy坐标
     * @param x3    三角形突出角x坐标
     * @param y3    三角形突出角y坐标
     */
    private void addTrianglePath(Path path, float radiu, float x1, float y1, float x2, float y2, float x3, float y3) {
        //起始点
        path.moveTo(x1, y1);
        path.lineTo(x2, y2);
        float[] ps2 = getCornerCoordinateEnd(radiu, x2, y2, x3, y3);
        path.lineTo(ps2[0], ps2[1]);
        float[] ps3 = getCornerCoordinateStart(radiu, x3, y3, x1, y1);
        //绘制圆角
        path.cubicTo(ps2[0], ps2[1], x3, y3, ps3[0], ps3[1]);
        //关闭三角形
        path.close();
    }

    /**
     * 获取圆角坐标(圆角半径长度相对于起始点x1,y1)
     *
     * @param radiu 圆角半径
     * @param x1    起始点x坐标
     * @param y1    起始点y坐标
     * @param x2    结束点x坐标
     * @param y2    结束点y坐标
     * @return
     */
    private float[] getCornerCoordinateStart(float radiu, float x1, float y1, float x2, float y2) {
        double degree = MathHelper.getDegree(x1, y1, x2, y2);
        double lx = MathHelper.getRightSideFromDegree(degree, radiu);
        double ly = MathHelper.getLeftSideFromDegree(degree, radiu);
        double v2 = x1 + lx;
        double v3 = y1 + ly;
        return new float[]{(float) v2, (float) v3};
    }

    /**
     * 获取圆角坐标(圆角半径长度相对于结束点x2,y2)
     *
     * @param radiu 圆角半径
     * @param x1    起始点x坐标
     * @param y1    起始点y坐标
     * @param x2    结束点x坐标
     * @param y2    结束点y坐标
     * @return
     */
    private float[] getCornerCoordinateEnd(float radiu, float x1, float y1, float x2, float y2) {
        double degree = MathHelper.getDegree(x1, y1, x2, y2);
        double lx = MathHelper.getRightSideFromDegree(degree, radiu);
        double ly = MathHelper.getLeftSideFromDegree(degree, radiu);
        double v2 = x2 - lx;
        double v3 = y2 - ly;
        return new float[]{(float) v2, (float) v3};
    }

    @Override
    public void setColor(int color) {
        if (mPaint != null) {
            mPaint.setColor(color);
        }
        invalidateSelf();
    }

    /**
     * 设置矩形的圆角
     *
     * @param radius
     */
    @Override
    public void setCornerRadius(float radius) {
        mRadius = radius;
    }

}
相关推荐
Estar.Lee2 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
温辉_xh2 小时前
uiautomator案例
android
工业甲酰苯胺3 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做3434 小时前
Android 不同情况下使用 runOnUiThread
android·java
Estar.Lee5 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯6 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey7 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!9 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟9 小时前
Android音频采集
android·音视频
小白也想学C11 小时前
Android 功耗分析(底层篇)
android·功耗