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

news2024/11/26 2:39:24

简介

自定义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;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/765667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欧姆龙以太网模块如何设置ip连接 Kepware opc步骤

在数字化和自动化的今天&#xff0c;PLC在工业控制领域的作用日益重要。然而&#xff0c;PLC通讯口的有限资源成为了困扰工程师们的问题。为了解决这一问题&#xff0c;捷米特推出了JM-ETH-CP转以太网模块&#xff0c;让即插即用的以太网通讯成为可能&#xff0c;不仅有效利用了…

排序算法之冒泡排序详解-python版

冒泡排序&#xff1a;通过比较2个相邻元素之间的大小&#xff0c;交换元素顺序&#xff0c;从而达到排序目的。 从百度百科摘抄下来的冒泡排序原理如下&#xff1a; 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 对每一对相邻元素做同样的工作&#xf…

ComboBox基本用法

作用&#xff1a;是一个下拉框&#xff0c;用于以下拉列表的方式展示数据。 常用属性&#xff1a; 常用事件&#xff1a; 下拉列表框内容选择变化时触发 后台代码示范&#xff1a; private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){//获取被选中的…

怎样优雅地增删查改(七):按用户查询

文章目录 实现使用测试 实现 定义按用户查询&#xff08;IUserOrientedFilter&#xff09;接口 public interface IUserOrientedFilter {public string EntityUserIdIdiom { get; }Guid? UserId { get; set; } }EntityUserIdIdiom&#xff1a;语义上的UserId&#xff0c;用于…

使用Pandas简化数据探索性分析

大家好&#xff0c;本文将探讨数据探索性分析的两个基本方面&#xff1a;数据集形状和空值。我们将深入了解Pandas如何简化这些任务&#xff0c;重点关注需要同时分析多个表格的情况。使用的库是pandas和tabulate。 数据集形状 要检索单个表格的形状&#xff0c;可以使用.sh…

「小摹AI」赋能原型设计 开放内测申请

「小摹AI」智能原型助手发布啦&#xff01; 4大AI应用能力&#xff1a;智能原型|智能文本|智能翻译|智能填充 释放原型设计的无限潜能 摹客AI - 让设计更具创造力http://www.mockplus.cn/ai/?hmsrwencsdnAI 智能原型 催生创新&#xff0c;开启原型设计新境界 根据描述&…

equals与Hashcde的区别

1、equals与hashCode的区别 equals与hashcode这两个方法都是从Object类中继承过来的。 hashCode()&#xff1a;计算出对象实例的哈希地址&#xff0c;并且返回该地址&#xff0c;称哈希函数&#xff0c;又称散列表。 equals()&#xff1a;反映的是对象的内存地址或者对象的内…

uniapp canvas 生成海报 小程序码 二维码

uniapp canvas 生成超简单海报带小程序码 canvas官网链接&#xff0c;可以先看下官方介绍&#xff0c;更好理解 uniapp官网canvas介绍 一、首先自定义一个生成海报的组件 uni-xcxcanvas.vue&#xff0c;创建同名目录 模板文件代码&#xff1a; <template><view&…

AtcoderABC251场

A - Six CharactersA - Six Characters 题目大意 给定一个由小写英文字母组成的字符串S&#xff0c;S的长度在1到3之间。 打印一个长度为6的字符串&#xff0c;该字符串是S的重复。 思路分析 通过将S重复拼接6次&#xff08;如果给定原字符串最小的情况&#xff09;&#xf…

【LLM】利用RoPE不微调大模型以扩展Context长度(更新中)

note 文章目录 note一、扩展LLM的Context长度1. 常见方法2. PCW方法 二、NBCE方法三、RoPE方法四、FlashAttention方法Reference 一、扩展LLM的Context长度 1. 常见方法 扩展LLM的Context长度其实已有不少&#xff0c;但多数是通过结合检索或者摘要的方式来缩短样本的长Conte…

深入学习 Redis - 常用数据类型,结构认识

目录 一、Redis数据类型 Redis 数据类型结构简单认识 每个数据类型具体的编码方式 1.string 2.hash 3.list 4.set 5.zset 典中典&#xff1a;记数字&#xff01;&#xff01;&#xff01; 6.查看 key 对应 value 的实际编码方式 如果本文有帮助到你&#xff0c;不…

古风编曲用什么软件?水果编曲FL Studio21怎么样

古风编曲需要什么乐器&#xff1f;古风编曲多采用笙、箫、二胡、古筝、琵琶、唢呐、二胡、竹笛、马头琴等中国传统乐器。古风编曲用什么软件&#xff1f;市面上几款热门软件有FL Studio、Cubase、Logic Pro等。 古风编曲需要什么乐器 我们常说的古风歌曲&#xff0c;其实是一…

CocoaPods could not find compatible versions for pod ““:

添加新的 SDK 依赖抛出了如下异常,无法找到可兼容的版本依赖库; [!] CocoaPods could not find compatible versions for pod "HDSSup":In Podfile:HDSSupSpecs satisfying the HDSSup dependency were found, but they required a higher minimum deployment targe…

使用这些ai绘画生成器,再也不愁没有好看的壁纸了

你们最近有没有发现网上开始流行ai绘画&#xff1f;我总是能看到ai绘画出来的图片&#xff0c;犹如下面这些图片。 这些图片是不是好看到&#xff0c;可以作为头像使用了&#xff1f;它们确实是ai绘画出来的&#xff0c;不过现在ai绘画软件众多&#xff0c;你们知道哪一款好用吗…

nvm node yarn

nvm node 以及node 对应的版本的npm相关配置的整理 ##如何设置npm 全局node_modules 安装目录 你可以通过 npm 的配置来设置全局安装的 node_modules 目录。默认情况下&#xff0c;npm 全局安装的包会存储在操作系统的特定目录中&#xff0c;如在 Windows 上为 C:\Users\<…

MySQL日常操作记录

1.查看MySQL版本 select version();2.快速复制表结构&#xff0c;不包含相关主键及约束 create table user_test as select * from user where 12;3.uuid select uuid(),uuid_short();4.替换uuid()里的’-‘为’’ select replace(uuid(),-,);5.md5摘要 select md5(uuid()…

SCT52240双路 4A/4A 高速MOSFET/IGBT栅极驱动器, 可并联输出

SCT52240是是一款宽供电电压、双通道、高速、低测栅极驱动器&#xff0c;包括功率MOSFET&#xff0c;IGBT。单个通道能够提供高达4A拉电流和4A灌电流的轨到轨驱动能力&#xff0c;并实现轨到轨输出。高达24V宽电压范围提高功率器件开关瞬间栅极驱动的振铃幅值裕度。13ns输入输出…

Spring源码学习-核心注解,架构以及整体流程

目录 核心注解核心组件接口分析基础接口ResourceResourceLoaderResourceResourceLoader BeanFactory结构解析图示核心的子接口 BeanDefinition加载注册流程(xml形式的) BeanDefinitionReaderBeanDefinitionRegistryApplicationContext类结构图示结构解析 Aware接口类图xxAware是…

后端Long类型传到前端精度丢失的问题

问题出现&#xff1a;后端的Java Bean的id属性是用的Long类型对应数据库主键使用bigint类型&#xff0c;当使用JSON方式传递该数据给前端时&#xff0c;前端接收到的数据末尾会变成0。&#xff08;发生的精度丢失问题&#xff09; 问题原因&#xff1a;Java中的long能表示的范围…

域名下配置,dns TXT记录,防止任意邮件伪造

每日鸡汤&#xff1a;每个你想要学习的瞬间都是未来的你向自己求救 最近在做一个网页的项目&#xff0c;这个项目和之前的项目的区别是部署在海外的服务器上&#xff0c;为了方便国外的用户访问&#xff0c;所以在埋点统计这块我们就选择了谷歌统计。国内的项目一般使用百度统计…