一、原理说明

主要是通过计算轨迹的坐标点加入到集合中,然后对集合进行相应截取,传入canvas中。

二、具体代码实现

/**

* 原理是先通过尺寸把各个轨迹的坐标计算出来,然后再截取相应坐标,进行重绘。

*

* @author lz

* @Time 2019-3-27

*/

public class RotateTrackView extends View {

/**

* 起始点横坐标

*/

private float startXPoint;

/**

* 起始点纵坐标

*/

private float startYPoint;

/**

* 宽度

*/

private float width;

/**

* 高度

*/

private float heigth;

/**

* 圆角半径

*/

private float radius;

/**

* 图形点集合

*/

float[] mPoints;

/**

* 画笔

*/

private Paint mPaint;

/**

* 标志位

*/

private final int LeftUp = 0;

private final int LeftDown = 1;

private final int RightDown = 2;

private final int RightUp = 3;

/**

* 停止动画标志位

*/

private boolean isPause = false;

/**

* 动画开始位置起点

*/

private int indexBegin = 0;

/**

* 动画结束位置,也就是整个动画的长度

*/

private int indexEnd = 200;

private int LENGTH = 200;

private int TIME = 1;

/**

* 截取的轨迹点集合

*/

private float[] snackPoints;

public RotateTrackView(Context context) {

super(context);

}

public RotateTrackView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public RotateTrackView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

public RotateTrackView(Context context, float startXPoint, float startYPoint, float width, float heigth, float radius) {

this(context);

this.startXPoint = startXPoint;

this.startYPoint = startYPoint;

this.heigth = heigth;

this.width = width;

this.radius = radius;

// 初始化画笔

mPaint = new Paint();

mPaint.setColor(Color.RED);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(8);

// 轨迹计算

ArrayList mPointLists = new ArrayList<>();

addXPoints(width, radius, startXPoint, startYPoint, mPointLists);

addCircleArgle(RightUp, radius, mPointLists);

addYPoints(heigth, radius, mPointLists.get(mPointLists.size() - 1), mPointLists.get(mPointLists.size() - 2), mPointLists);

addCircleArgle(RightDown, radius, mPointLists);

reduceXPoints(width, radius, mPointLists.get(mPointLists.size() - 2), mPointLists.get(mPointLists.size() - 1), mPointLists);

addCircleArgle(LeftDown, radius, mPointLists);

reduceYPoints(heigth, radius, mPointLists.get(mPointLists.size() - 1), mPointLists.get(mPointLists.size() - 2), mPointLists);

addCircleArgle(LeftUp, radius, mPointLists);

// 将list集合转换成canvas接受的float数组

mPoints = new float[mPointLists.size()];

for (int i = 0; i < mPointLists.size(); i++) {

mPoints[i] = mPointLists.get(i);

}

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (snackPoints.length == LENGTH) {

canvas.drawPoints(snackPoints, mPaint);

}

}

/**

* x轴向右计算

*

* @param length

* @param radius

* @param addNumber

* @param constantNumber

* @param list

*/

private void addXPoints(float length, float radius, float addNumber, float constantNumber, List list) {

for (int i = 1; i < (length - 2 * radius); i++) {

float pointX = addNumber + i;

float pointY = constantNumber;

list.add(pointX);

list.add(pointY);

}

}

private void reduceXPoints(float length, float radius, float addNumber, float constantNumber, List list) {

for (int i = 1; i < (length - 2 * radius); i++) {

float pointX = addNumber - i;

float pointY = constantNumber;

list.add(pointX);

list.add(pointY);

}

}

private void addYPoints(float length, float radius, float addNumber, float constantNumber, List list) {

for (int i = 1; i < (length - 2 * radius); i++) {

float pointX = addNumber + i;

float pointY = constantNumber;

list.add(pointY);

list.add(pointX);

}

}

private void reduceYPoints(float length, float radius, float addNumber, float constantNumber, List list) {

for (int i = 1; i < (length - 2 * radius); i++) {

float pointX = addNumber - i;

float pointY = constantNumber;

list.add(pointY);

list.add(pointX);

}

}

/**

* 由于上下左右四个圆的轨迹坐标计算方式是不一样的,需要单独处理

*

* @param position

* @param radius

* @param list

*/

private void addCircleArgle(int position, float radius, List list) {

int argle = 0;

float x = list.get(list.size() - 2);

float y = list.get(list.size() - 1);

for (int i = 0; i < 90; i += 10) {

argle = i + 10;

switch (position) {

case RightUp:

float x1 = (float) (x + Math.sin((Math.PI / 180) * argle) * radius);

float y1 = (float) (y + (radius - Math.cos((Math.PI / 180) * argle) * radius));

list.add(x1);

list.add(y1);

break;

case RightDown:

float x2 = (float) (x - (radius - Math.cos((Math.PI / 180) * argle) * radius));

float y2 = (float) (y + (Math.sin((Math.PI / 180) * argle) * radius));

list.add(x2);

list.add(y2);

break;

case LeftDown:

float x3 = (float) (x - (Math.sin((Math.PI / 180) * argle) * radius));

float y3 = (float) (y - (radius - Math.cos((Math.PI / 180) * argle) * radius));

list.add(x3);

list.add(y3);

break;

case LeftUp:

float x4 = (float) (x + (radius - Math.cos((Math.PI / 180) * argle) * radius));

float y4 = (float) (y - (Math.sin((Math.PI / 180) * argle) * radius));

list.add((float) (x + (radius - Math.cos((Math.PI / 180) * argle) * radius)));

list.add((float) (y - (Math.sin((Math.PI / 180) * argle) * radius)));

break;

default:

break;

}

}

}

/**

* 开始动画

*/

public void startAnim() {

Thread thread = new Thread() {

@Override

public void run() {

super.run();

while (!isPause) {

RotateTrackView.this.post(new Runnable() {

@Override

public void run() {

invalidate();

}

});

indexBegin += 2;

indexEnd += 2;

if (indexEnd < mPoints.length) {

snackPoints = (Arrays.copyOfRange(mPoints, indexBegin, indexEnd));

} else if (indexBegin == mPoints.length) {

indexBegin = 0;

indexEnd = LENGTH;

} else {

float[] floats2 = Arrays.copyOfRange(mPoints, indexBegin, mPoints.length);

float[] floats3 = Arrays.copyOf(floats2, LENGTH);

int length = floats2.length;

for (int i = 0; i < (LENGTH - floats2.length); i++) {

floats3[length] = mPoints[i];

length++;

}

snackPoints = floats3;

}

try {

Thread.sleep(TIME);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

};

thread.start();

}

/**

* 停止动画

*/

public void stopAnim() {

isPause = true;

}

/**

* 设置轨迹粗细

*/

public void setLinesWidth(int width) {

mPaint.setStrokeWidth(width);

}

}

三、使用方法

public class MainActivity extends AppCompatActivity {

private RotateTrackView rotateTrackView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// 创建RotateTrackView, 设置起始坐标、宽、高、圆角半径

rotateTrackView = new RotateTrackView(this, 500, 600, 250, 180, 30);

setContentView(rotateTrackView);

// 设置画笔粗细

rotateTrackView.setLinesWidth(20);

// 开始动画

rotateTrackView.startAnim();

}

}

四、效果展示

RotateTrackView.gif

五、下载地址

android布局中画圆角矩形,Android 自定义View之圆角矩形轨迹图相关推荐

  1. android 轨迹生成图,Android自定义View实现公交成轨迹图

    本文实例为大家分享了Android自定义View实现公交成轨迹图的具体代码,供大家参考,具体内容如下 总体分析下:水平方向recyclewview,item包含定位点,站台位置和站台名称. 下面看实现 ...

  2. 自定义View将圆角矩形绘制在Canvas上

    前几天,公司一个项目中,头像图片需要添加圆角,这样UI效果会更好看,于是写了一个小的demo进行圆角的定义,该处主要是使用BitmapShader进行了渲染(如果要将一张图片裁剪成椭圆或圆形显示在屏幕 ...

  3. android 图片颜色渐变动画,自定义View之颜色渐变折线图

    今日科技快讯 近日,汽车之家宣布与湖南卫视达成独家战略合作.据悉,双方开辟了"台网互动"营销模式,将共同开展汽车广告业务,并联合推出品效合一的新型汽车类广告产品"芒果汽车 ...

  4. android类中定义颜色,自定义实现简单的Android颜色选择器(附带源码)

    在写Android App过程中需要一个简单的颜色选择器,Android自带的ColorPicker和网上的一些ColorPicker都太高端了,都实现了颜色渐变功能,我要的不需要那么复杂,只想提供几 ...

  5. Android 雪花飘落动画效果 自定义View

    在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天.每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不 ...

  6. android 选择银行类型,『自定义View实战』—— 银行种类选择器

    在工作中难免遇到自定义 View 的相关需求,本身这方面比较薄弱,因此做个记录,也是自己学习和成长的积累.自定义View实战 前言 年前的最后一个开发需求,将之前H5开卡界面转变成native.意思就 ...

  7. Carson带你学Android:源码解析自定义View Draw过程

    前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...

  8. Android 自定义View 圆形圆角图片

    [Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...

  9. android自定义progressbar样式,Android开发中如何实现自定义ProgressBar的样式

    Android开发中如何实现自定义ProgressBar的样式 发布时间:2020-11-20 16:08:10 来源:亿速云 阅读:294 作者:Leah Android开发中如何实现自定义Prog ...

最新文章

  1. 日记 [2008年03月23日]LINUX网关后面的pptp ***客户机连接***
  2. 跳水比赛背后的隐形教练现身了!百度智能云还完成了历史性的大升级
  3. 在Cisco交换机上实现隔离访问
  4. Auto packing the repository in background for optimum performance.
  5. STM32F7xx —— LAN8720(FreeRTOS+LWIP)
  6. android版 点击下载,自动点击器最新版
  7. SQLSERVER中的BCP命令使用
  8. HTTP、websocket、XMPP、COAP、MQTT和DDS协议对比
  9. html 编辑器插件安装,最新版CKEditor的配置方法及插件(Plugin)编写示例
  10. CMSIS 记录与下载
  11. 通过 电脑快捷键 高效利用时间
  12. 计算机工作的本质是什么?
  13. 电热毯UL964标准上架亚马逊所需资料流程
  14. linux yum安装svn版本号,CentOS7 yum安装svn服务
  15. 半年以来的图像去雾总结-图像去雾(一)暗通道去雾
  16. 用C++实现强化学习,速度不亚于Python,这里有个框架可用
  17. bootstarp怎么使盒子到最右边_8+ | 从恐龙特急克塞号到小猪佩奇,怎么都有它
  18. 图书信息管理系统(二)
  19. 第一章 略说中医的学习与研究(4)
  20. Android 之路18---Java基础12

热门文章

  1. BotSharp v0.2 发布, 支持微信智能回复
  2. 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版
  3. AspNetCoreApi 跨域处理(CORS )
  4. spring cloud+dotnet core搭建微服务架构:Api网关(三)
  5. Windows Server Containers 支持 Windows 开发者使用 Docker
  6. 使用webpack搭建个性化项目
  7. Git 撤销操作 / 回滚历史
  8. mysql中的操作指令,MySQL中常用指令操作的介绍(代码示例)
  9. frameset在html5下用什么代替_速速围观!冬至吃货地图来啦~蚌埠的吃货们,你们今天吃什么?...
  10. Excel实用函数大全(名称、功能、说明、用法、举例)