Android 多边形导圆角(Path画折线导圆角)

news2025/1/19 11:14:10

前言:用path画折线,2条线相交处导圆角

简介:为开发者提供工具类,方便对使用path画折线设置圆角。使用path画多边形,如三角形、矩形、平行四边形等,同时设置圆角。另外提供计算直线上某点坐标。

实现原理

三个点,2条直线相交,给中间点连接处导圆角。设圆角半径为radius,  三个点分别为p2, p3,p1,  如下图所示:给p3点处导圆角。先求出导圆角处连接的2点p4、p5坐标,再用path连接p2和p4,然后通过p4、p3、p5绘制贝塞尔曲线连接p4、p5两个点,最后连接p5、p1两个点。
总结:计算圆角的起始和结束点,使用贝塞尔曲线绘制圆角。


API使用:
 

三角形导圆效果如下:

 导圆角之后

 调用api如下

        int r = 22;//圆角尺寸
        Path path = SimplePath.buildle()
                .moveTo(50, 50, r, r)//起始点
                .lineTo(200, 50, r, r)
                .lineTo(200, 250, r, r)
                .close()//结束,完成三角形
                .build();

矩形导圆角

Path path = SimplePath.buildle()
        .addRect(50, 50, 200, 250, r)
        .build();
 

其它API使用:

获取线上点坐标   public static float[] getOnLinePointLocationEnd(float lenght, float x1, float y1, float x2, float y2)
示例:上述实现原理中,计算圆角起始坐标点p4. 
float[] p2f = SimplePath.getOnLinePointLocationEnd(r,x1,y1,x2,y2);
Point p2 = new Point(p2f[0], p2f[1]);
其中r在这里是圆角半径,表示圆角起始点p4与中间点p2之间的距离。


​​​​​​​画线并添加圆角 (绘制的线是起始点到圆角结束点的路径,并不包含到第三点路径)  public static void lineToAndCorner(Path path, float startRadius, float endRadius, float x1, float y1, float x2, float y2, float x3, float y3) 

示例:上述实现原理中,如果给p3点导圆角r.

path先要自行设置起始点p2,  然后

SimplePath.lineToAndCorner(path, r, r, p2.x, p2.y, p3.x, p3.y, p1.x,p1y);

绘制的线是 p2到p4,再到p5的路径, 不包含,p5到p1的路径。目标点是中间点p3.

备注:方法参数详情请查阅源码

源码:

package com.ttkx.deviceinfo.bkchart;


import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;

import java.util.ArrayList;
import java.util.List;

public class SimplePath {

    public static Buildle buildle() {
        return new Buildle();
    }

    public static class Buildle {

        private Path mPath;

        private class P {

            public float x;
            public float y;
            public float startRadius;
            public float endRadius;
            public boolean isStartPoint;//是否是起始点

            public P(float x, float y) {
                this.x = x;
                this.y = y;
            }

            public P(float x, float y, float startRadiu, float endRadiu) {
                this.x = x;
                this.y = y;
                this.startRadius = startRadiu;
                this.endRadius = endRadiu;
            }
        }

        private List<P> mList = new ArrayList<>();

        public void moveTo(float x, float y) {
            moveTo(x, y, 0, 0);
        }

        public Buildle setPath(Path path) {
            mPath = path;
            return this;
        }

        public Buildle moveTo(float x, float y, float startRadius, float endRadius) {
            P p = new P(x, y, startRadius, endRadius);
            p.isStartPoint = true;
            mList.add(0, p);
            return this;
        }

        public Buildle addRect(Rect rect, float r) {
            return addRect(rect.left, rect.top, rect.right, rect.bottom, r);
        }

        public Buildle addRect(RectF rect, float r) {
            return addRect(rect.left, rect.top, rect.right, rect.bottom, r);
        }

        public Buildle addRect(float left, float top, float right, float bottom, float r) {
            moveTo(left, top, r, r);
            lineTo(right, top, r, r);
            lineTo(right, bottom, r, r);
            lineTo(left, bottom, r, r);
            close();
            return this;
        }

        public Buildle lineTo(float x, float y) {
            lineTo(x, y, 0, 0);
            return this;
        }

        public Buildle lineTo(float x, float y, float startRadius, float endRadius) {
            mList.add(new P(x, y, startRadius, endRadius));
            return this;
        }

        public Buildle close() {
            if (mList.size() >= 1) {
                mList.add(mList.get(0));
            }
            return this;
        }

        public Path build() {
            Path path = mPath;
            if (path == null) {
                path = new Path();
            }
            for (int i = 0; i < mList.size(); i++) {
                P p = mList.get(i);
                float x = p.x;
                float y = p.y;
                if (i == 0) {
                    if (p.isStartPoint) {
                        if (hasCorner(p)) {
                            P p1 = mList.get(i + 1);
                            P p2 = p;
                            float[] onePoint = getOnLinePointLocationEnd(p.startRadius, p1.x, p1.y, p2.x, p2.y);
                            path.moveTo(onePoint[0], onePoint[1]);
                        } else {
                            path.moveTo(x, y);
                        }
                    } else {
                        path.lineTo(x, y);
                    }
                } else if (i == mList.size() - 1) {
                    //最后一个点
                    P p0 = mList.get(0);
                    if (p.x == p0.x && p.y == p0.y && hasCorner(p0)) {
                        P p1 = mList.get(i - 1);
                        P p2 = p0;
                        P p3 = mList.get(1);
                        lineToAndCorner(path, p2.startRadius, p2.endRadius, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
                    } else {
                        path.lineTo(p.x, p.y);
                    }
                } else {
                    if (hasCorner(p)) {
                        P p1 = mList.get(i - 1);
                        P p2 = p;
                        P p3 = mList.get(i + 1);
                        lineToAndCorner(path, p2.startRadius, p2.endRadius, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
                    } else {
                        path.lineTo(x, y);
                    }
                }
            }
            return path;
        }


        private boolean hasCorner(P p) {
            return p.endRadius > 0 && p.startRadius > 0;
        }
    }

    /**
     * 画线并添加圆点 (绘制的线是起始点到圆角结束点的路径,并不包含到p3点路径)
     *
     * @param path
     * @param startRadius 起始圆角半径
     * @param endRadius   结束圆角半径
     * @param x1          起始点
     * @param y1
     * @param x2          中间点
     * @param y2
     * @param x3          结束点
     * @param y3
     */
    public static void lineToAndCorner(Path path, float startRadius, float endRadius, float x1, float y1, float x2, float y2, float x3, float y3) {
        float[] onePoint = getOnLinePointLocationEnd(startRadius, x1, y1, x2, y2);
        path.lineTo(onePoint[0], onePoint[1]);
        float[] twoPoint = getOnLinePointLocationStart(endRadius, x2, y2, x3, y3);
        //绘制圆角
        path.cubicTo(onePoint[0], onePoint[1], x2, y2, twoPoint[0], twoPoint[1]);
    }

    /**
     * 获取线上点坐标
     *
     * @param lenght 线上点距离起始点(x1,y1)长度
     * @param x1     起始点x坐标
     * @param y1     起始点y坐标
     * @param x2     结束点x坐标
     * @param y2     结束点y坐标
     * @return
     */
    public static float[] getOnLinePointLocationStart(float lenght, float x1, float y1, float x2, float y2) {
        double degree = getDegree(x1, y1, x2, y2);
        double dx = getRightSideFromDegree(degree, lenght);
        double dy = getLeftSideFromDegree(degree, lenght);
        double v2 = x1 + dx;
        double v3 = y1 + dy;
        return new float[]{(float) v2, (float) v3};
    }

    /**
     * 获取线上点坐标
     *
     * @param lenght 线上点距离结束点(x2,y2)长度
     * @param x1     起始点x坐标
     * @param y1     起始点y坐标
     * @param x2     结束点x坐标
     * @param y2     结束点y坐标
     * @return
     */
    public static float[] getOnLinePointLocationEnd(float lenght, float x1, float y1, float x2, float y2) {
        double degree = MathHelper.getDegree(x1, y1, x2, y2);
        double dx = MathHelper.getRightSideFromDegree(degree, lenght);
        double dy = MathHelper.getLeftSideFromDegree(degree, lenght);
        double v2 = x2 - dx;
        double v3 = y2 - dy;
        return new float[]{(float) v2, (float) v3};
    }


    //两点间的角度
    private static double getDegree(float sx, float sy, float tx, float ty) {
        float nX = tx - sx;
        float nY = ty - sy;
        double angrad = 0d, angel = 0d, tpi = 0d;
        float tan = 0.0f;
        if (Float.compare(nX, 0.0f) != 0) {
            tan = Math.abs(nY / nX);
            angel = Math.atan(tan);
            if (Float.compare(nX, 0.0f) == 1) {
                if (Float.compare(nY, 0.0f) == 1 || Float.compare(nY, 0.0f) == 0) {
                    angrad = angel;
                } else {
                    angrad = 2 * Math.PI - angel;
                }
            } else {
                if (Float.compare(nY, 0.0f) == 1 || Float.compare(nY, 0.0f) == 0) {
                    angrad = Math.PI - angel;
                } else {
                    angrad = Math.PI + angel;
                }
            }
        } else {
            tpi = Math.PI / 2;
            if (Float.compare(nY, 0.0f) == 1) {
                angrad = tpi;
            } else {
                angrad = -1 * tpi;
            }
        }
        return Math.toDegrees(angrad);
    }

    /**
     * 直角三角形 根据角度和斜边求直角边
     *
     * @param degree 角度
     * @param width  斜边
     * @return 直角边长
     */
    private static double getRightSideFromDegree(double degree, double width) {
        double cos = Math.cos(Math.toRadians(degree));
        return width * cos;
    }

    private static double getLeftSideFromDegree(double degree, double width) {
        double sin = Math.sin(Math.toRadians(degree));
        return width * sin;
    }
}

实践:绘制带箭头的提示框背景

第一步:先绘制带圆角的三角形。

三角形只有一个角导圆角

        int r = 10;//圆角尺寸
        int ph = 30;//箭头离左侧距离
        int arrowsHeight = 35;//箭头高度

        Point p1 = new Point(ph, arrowsHeight);
        Point p2 = new Point(ph + arrowsHeight * 2, arrowsHeight);
        Point p3 = new Point(ph + arrowsHeight, 0);

        Path path = SimplePath.buildle()
                .setPath(new Path())//设置path (可以设置也可以不设置, 若不设置工具类自己创建一个path对象)
                .moveTo(p1.x, p1.y)//起始点
                .lineTo(p2.x, p2.y)
                .lineTo(p3.x, p3.y, r, r)
                .close()
                .build();

效果如下:

 第二步:添加圆角矩形

int rectRadius = 30;
path.addRoundRect(new RectF(0,arrowsHeight, getBounds().right, getBounds().bottom), rectRadius, rectRadius ,Path.Direction.CW);

第三步:创建自定义Drawable, 将path绘制出来,再将drawable设置为textView 背景, 最后给textView设置padding即可。

效果如下:

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

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

相关文章

Matplotlib下载和安装

Matplotlib 是 Python 的第三方绘图库&#xff0c;它非常类似于 MATLAB。在使用 Matplotlib 软件包之前&#xff0c;需要对其进行安装。本节以 Windows10 系统为例&#xff0c;介绍 Matplotlib 的几种安装方式。 MATLAB 是一款商业软件&#xff0c;主要用于数据分析、图像处理…

http协议(二)

欢迎来到南方有乔木的博客&#xff01;&#xff01;&#xff01; 博主主页&#xff1a;点击点击&#xff01;戳一戳&#xff01;&#xff01; 博主名:南方有乔木呀 博主简介&#xff1a; 一名在校大学生&#xff0c;正在努力学习Java语言编程。穷且意坚&#xff0c;不坠青云…

Java常规写法与新特性对比详解

Java常规写法与新特性对比详解 ✨1. Lambda表达式对比常规写法&#xff1a;⭐️1.1 迭代集合&#xff1a;⭐️1.2 条件过滤&#xff1a;⭐️1.3 映射转换&#xff1a; ✨2. Stream API对比常规写法&#xff1a;⭐️2.1 过滤和计数&#xff1a;⭐️2.2 排序&#xff1a;⭐️2.3 …

通过 Postman+Newman+Jenkins 进行接口自动化测试和进一步实现 CI

目录 前言&#xff1a; Postman 部分 Collection 或者说测试集的建立 Postman 使用详解 测试工具 mock server Newman 的安装和使用 newman 的使用 对 SSL 的支持 newman 第三版以后&#xff08;目前已经是 4.3.1&#xff09;可以支持通过 CLI 选项来支持 SSL 对 new…

Python 使用 Stable Diffusion API 生成图片示例

代码&#xff1a; import base64 import datetime import json import osimport requestsdef submit_post(url: str, data: dict):"""Submit a POST request to the given URL with the given data.:param url: url:param data: data:return: response"…

用html+javascript打造公文一键排版系统6:三级标题排版

正文中的标题分为四级&#xff0c;文中结构层次序数依次可以用“一、”“&#xff08;一&#xff09;”“1.”“&#xff08;1&#xff09;”标注&#xff1b;一般第一层用黑体字、第二层用楷体字加粗、第三层和第四层用仿宋体字标注。 对于以阿拉伯数字开头、后接英文点号.及…

First image then video A two-stage network for spatiotemporal video denoising

First image then video: A two-stage network for spatiotemporal video denoising http://export.arxiv.org/abs/2001.00346 作者&#xff1a;王策 南开大学 本文针对的是视频中出现大运动。或者前景和背景由于弱光环境较弱时造成去噪性能差的问题而解决&#xff0c;论文时在…

String 类的常用方法

String类的常用方法 说明 String类是保存字符串常量的&#xff0c;每次更新都需要重新开辟空间&#xff0c;效率比较低&#xff0c;因此 java 设计者还提供了StringBuilder和StringBuffer来增强String的功能&#xff0c;并提高效率。 常用方法 public class String01 {publi…

C语言——动态内存管理(malloc, calloc, realloc, free, 柔性数组详解)

C语言——动态内存管理 1. 为什么需要动态内存管理 我们以往定义数组&#xff0c;都是这么定义的&#xff1a; int nums[10] {0};以这种方式开辟空间有两个特点&#xff1a; 空间开辟的大小是固定的数组在声明的时候&#xff0c;必须指定数组的长度&#xff0c;它所需要的内…

数据库应用:Navicat连接MySQL

目录 一、理论 1.Navicat 2.MVCC 二、实验 1.Navicat连接MySQL 2.navicat的基础操作 3.测试提交事务 三、问题 1.解决1130 2.解决2003 四、总结 一、理论 1.Navicat &#xff08;1&#xff09;简介 Navicat Premium 是一套数据库开发工具&#xff0c;实现从单一应用…

基于时域特征和频域特征组合的敏感特征集,再利用CNN进行轴承故障诊断(python编程)

1.文件夹介绍&#xff08;使用的是CWRU数据集&#xff09; 0HP-3HP四个文件夹装载不同工况下的内圈故障、外圈故障、滚动体故障和正常轴承数据。 2.模型 按照1024的长度分割样本&#xff0c;构建内圈故障、外圈故障、滚动体故障和正常轴承样本集 2.1.计算11种时域特征值 # 计…

EfficientNet论文笔记

EfficientNet论文笔记 通过NAS平衡了channel&#xff0c;depth&#xff0c;resolution&#xff0c;发现在相同的FLOPs下&#xff0c;同时增加 depth和 resolution的效果最好。 数据集效果小于resolution怎么办&#xff1f; EfficientNet—b0框架 表格中每个MBConv后会跟一个…

Maven —— 项目管理工具

前言 在这篇文章中&#xff0c;荔枝会介绍如何在项目工程中借助Maven的力量来开发&#xff0c;主要涉及Maven的下载安装、环境变量的配置、IDEA中的Maven的路径配置和信息修改以及通过Maven来快速构建项目。希望能对需要配置的小伙伴们有帮助哈哈哈哈~~~ 文章目录 前言 一、初…

力扣刷题序列 - 字符串篇

这里写目录标题 字符1.520 回文串的定义2. 125 公共前缀3. 14 单词4. 4345. 58 字符串的反转6. 3447. 5418. 5579. 151 字符的统计10. 38711. 38912. 38313. 24214. 4915. 45116. 42317. 65718. 55119. 69620. 467 数字与字符间的转换21.41222.50623.53924.553537592---64038 子…

GAME101 OpenCV环境安装

文章目录 Opencv 库编译Step 1.下载源码Step 2. 使用CMake编译Step3. 解决CMake 过程重的报错错误1&#xff1a; 错误的Python版本:错误1 解决办法 错误2&#xff1a;下载ippicv_2020_win_ia32_20191018_general.zip失败错误2 解决办法 错误3&#xff1a;ffmpeg相关文件下载失败…

力扣 509.斐波那契数

509.斐波那契数 1 题目2 思路3 代码4 结果 1 题目 题目来源&#xff1a;力扣&#xff08;LeetCode &#xff09;https://leetcode.cn/problems/fibonacci-number 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为斐波那契数列 。该数列由 0 和 1 开始&…

git国内下载

https://npm.taobao.org/mirrors/git-for-windows/点进去最新的最后一条 选择.exe文件点击

二、遥感物理基础(2)物体的发射与反射辐射特征

前言 本文内容较为枯燥&#xff0c;是遥感的物理原理&#xff0c;作者已经尽量去帮助读者理解了&#xff0c;无论是精细的阅读还是走马观花&#xff0c;长期下来都能提高读者对专业知识的理解&#xff1b;作者非物理专业&#xff0c;对某些知识点的总结仅是个人理解&#xff0c…

win7 刻录机刻录文件显示 “准备好写入到”光盘中的文件”

一、问题描述 就是这么突然&#xff0c;好好的刻录机&#xff0c;突然就刻录不了了&#xff0c;昨天都可以正常刻录&#xff0c;今天就显示&#xff1a; 准备好写入到”光盘中的文件&#xff0c;然后还显示待刻录的文件…&#xff0c;右键选择刻录就直接弹出光盘了&#xff1f…

吴恩达机器学习2022-Jupyter特征缩放

1可选实验室: 特征缩放和学习率(多变量) 1.1 目标 在这个实验室里: 利用前一实验室开发的多变量线性回归模型程序在具有多种功能的数据集上运行梯度下降法探讨学习速度 alpha 对梯度下降法的影响通过使用 z 分数标准化的特征缩放来提高梯度下降法的性能 1.2 工具 您将使用…