Android自定义View实现多边形统计图示例代码

前言

  最近利用空闲时间学习了自定义View的一些知识,为了巩固,写了一个小东西,顺便分享出来,下面话不多说了,来一起看看详细的介绍吧。

简介

  一个多边形统计图。边数,每个方向的值,每个点的文字等等都是可以设置的。

下面就来分析一下这个自定义View

这个view由以下几个部分组成

  • M层N边形
  • 中心到各顶点的连线
  • 填充区域
  • 文字
 @Override
 protected void onDraw(Canvas canvas) {
 if (!canDraw()) {
 return;
 }
 canvas.translate(width / 2, height / 2);
 computeMaxPoint();
 drawPolygon(canvas);
 drawLine(canvas);
 drawArea(canvas);
 drawText(canvas);
 }

我们一步一步来说明

绘制多边形

  绘制多边形主要用到的是Path这个东西。具体的思路就是先计算好每个点的位置,同Path的lineTo方法连接起来,然后绘制。

  我的做法是先算出最大的半径(再之后还会用到,建议单独存起来),然后根据所在层数来计算每一层的半径,利用cos函数各sin函数计算出每一层各顶点的位置。

计算最大半径并且保存顶点

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 width = w;
 height = h;
 maxRadius = (float) ((width / 2) * 0.8);
 postInvalidate();
 }
 /*
 计算最大半径,之后的位置都是基于最大半径的比例
 */
 public void computeMaxPoint() {
 maxPointXList = new ArrayList<>();
 maxPointYList = new ArrayList<>();
 for (int i = 0; i < eageCount; i++) {
 float currentAngle = i * angle - 90;
 float currentX = (float) (maxRadius * Math.cos((currentAngle / 180) * Math.PI));
 float currentY = (float) (maxRadius * Math.sin((currentAngle / 180) * Math.PI));
 maxPointXList.add(currentX);
 maxPointYList.add(currentY);
 }
 }

  注意:cos和sin都是按照弧度制计算的,要换算。

  这里解释一下为currentAngle什么要减去90度

  按照android的坐标系,如果不减去90度直接乘上cos的话,第一个顶点会默认在中心的右侧,而一般的认知是第一个点在正上方,所以减去90度

按照比例和层数边数绘制多边形

 /*
 绘制多边形和每一层
 */
 private void drawPolygon(Canvas canvas) {
 Path path = new Path();
 for (int i = 0; i < loopCount; i++) {
 path.reset();
 //依据最大半径和角度来判断每一层点的位置
 float rate = computeRate(i + 1, loopCount);
 for (int j = 0; j < eageCount; j++) {
 float currentX = maxPointXList.get(j) * rate;
 float currentY = maxPointYList.get(j) * rate;
 if (j == 0) {
  path.moveTo(currentX, currentY);
 } else {
  path.lineTo(currentX, currentY);
 }
 }
 path.close();
 canvas.drawPath(path, eagePaint);
 }
 }

  代码还是很容易的吧,要是看不懂的话自己动手算算就知道了,很容易计算各个点的位置。

绘制连线

  由于之前保存了顶点的坐标,这个就很容易了

 /*
 画出从中心向各顶点的连线
 */
 private void drawLine(Canvas canvas) {
 Path path = new Path();
 for (int i = 0; i < eageCount; i++) {
  path.reset();
  path.lineTo(maxPointXList.get(i), maxPointYList.get(i));
  canvas.drawPath(path, eagePaint);
 }
 }

绘制覆盖区域

  这个原理其实和绘制多边形是一样的,就是对顶点坐标乘的比例发生了变化。每个方向的数值是由用户传递进来的。

 /*
 绘制个方向值覆盖的区域
 */
 private void drawArea(Canvas canvas) {
 Path path = new Path();
 //原理就是用path根据各方向值创建一个封闭的区域,然后填充
 for (int i = 0; i < eageCount; i++) {
  float rate = pointValue.get(i);
  float currentX = maxPointXList.get(i) * rate;
  float currentY = maxPointYList.get(i) * rate;
  if (i == 0) {
  path.moveTo(currentX, currentY);
  } else {
  path.lineTo(currentX, currentY);
  }
 }
 path.close();
 canvas.drawPath(path, areaPaint);
 }

绘制文字

  说实话,前面的没有什么难点,但是唯独绘制文字有许多麻烦事。主要是文字是默认自左向右的,最上面和最先面的文字倒是没啥,左侧和右侧的文字就会出现问题了,文字会绘制到多边形上,看起来特别难受。这里我的解决办法就是前面图中看到的,让字跟着多边形的顶点位置一起旋转。

 /*
 绘制文字
 */
 private void drawText(Canvas canvas) {
 if (pointName == null) {
  return;
 }
 //绘制文字的难点在于无法最好的适配屏幕的位置,会发生难以控制的偏倚
 for (int i = 0; i < pointName.size(); i++) {
  //解决办法就是让文字在不同的角度也发生旋转,并且在x轴上减去一定的数值来保证正确的位置
  float currentAngle = i * angle;
  //180度需要也别的处理,让它正着显示,不然就是倒着的
  if (currentAngle == 180) {
  float currentX = maxPointXList.get(i) * 1.1f;
  float currentY = maxPointYList.get(i) * 1.1f;
  canvas.drawText(pointName.get(i), currentX - (textPaint.getTextSize() / 4)
   * (pointName.get(i).length()), currentY, textPaint);
  } else {
  canvas.save();
  float currentX = maxPointXList.get(0) * 1.1f;
  float currentY = maxPointYList.get(0) * 1.1f;
  //旋转画布,达到旋转文字的效果
  canvas.rotate(currentAngle);
  canvas.drawText(pointName.get(i), currentX - (textPaint.getTextSize() / 4)
   * (pointName.get(i).length()), currentY, textPaint);
  canvas.restore();
  }
 }
 }

到这里,整个组件就绘制完成了

额外的属性

如果单纯只是想画出这个组件来,其实没啥难度。我们可以在加一些别的东西让他更加实用。

动画效果

  利用属性动画的知识,我们可以做到让中间的填充区域慢慢的扩散出来。原理也简单,就是把0到1用属性计算展开,当做一个演化的比例,让各个方向的值乘上这个数值,绘制一个比原先覆盖区域小的区域就可以了。

 /*
 用属性动画绘制组件
 */
 public void draw() {
 if (canDraw()) {
  final Float[] trueValues = pointValue.toArray(new Float[pointValue.size()]);
  ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
  valueAnimator.setDuration(1000);
  valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   float rate = animation.getAnimatedFraction();
   for (int i = 0; i < pointValue.size(); i++) {
   pointValue.set(i, trueValues[i] * rate);
   }
   invalidate();
  }
  });
  valueAnimator.start();
 }
 }

定义xml属性

  我们正常使用系统组件的时候都会写一大堆的xml来控制我们组件的属性,自定义View也可以尝试这些

  首先在value下创建atts文件

  然后指定你想要的属性名称和类型

  再然后就是让atts和我们的view联系上。这个也简单,仔细观察View的构造方法中的参数,有这么一个玩意 AttributeSet attrs

public PolygonView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);
 init(context, attrs);
 }
 public PolygonView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init(context, attrs);
 }

  它就是联系xml和view的纽带

xmlns:app="http://schemas.android.com/apk/res-auto"
<com.totoro.xkf.polygonview.PolygonView
 android:id="@+id/pv_polygon_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:areaColor="@android:color/holo_blue_light"
 app:eageColor="@android:color/black"
 app:eageCount="6"
 app:loopCount="4"
 app:textColor="@android:color/black" />
public void init(Context context, AttributeSet attrs) {
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Polygon);
 initPaint();
 setTextColor(typedArray.getColor(R.styleable.Polygon_textColor, Color.BLACK));
 setLoopCount(typedArray.getInteger(R.styleable.Polygon_loopCount, 0));
 setEageCount(typedArray.getInteger(R.styleable.Polygon_eageCount, 0));
 setAreaColor(typedArray.getColor(R.styleable.Polygon_areaColor, Color.BLUE));
 setEageColor(typedArray.getColor(R.styleable.Polygon_eageColor, Color.GRAY));
 typedArray.recycle();
 }
xmlns:app=http://schemas.android.com/apk/res-auto

这个东西不能忘了

快速使用

  感谢你看到这里,如果你想使用这个组件但是不想自己写的话欢迎访问

项目Github里面有讲如何添加依赖,导入组件

如果能帮到你的话,不胜荣幸!!!

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • Android 实现会旋转的饼状统计图实例代码
  • Android编程实现canvas绘制柱状统计图功能【自动计算宽高及分度值、可左右滑动】
  • Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】
  • Android自定义View——扇形统计图的实现代码
(0)

相关推荐

  • Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】

    本文实例讲述了Android编程实现canvas绘制饼状统计图功能.分享给大家供大家参考,具体如下: 本例的目的是实现一个简单的饼状统计图,效果如下:    特点: 1.使用非常方便,可放在xml布局文件中,然后在代码中设置内容,即: PieChartView pieChartView = (PieChartView) findViewById(R.id.pie_chart); PieChartView.PieItemBean[] items = new PieChartView.PieItem

  • Android自定义View——扇形统计图的实现代码

    Android 扇形统计图 先看看效果: 看上去如果觉得还行就继续往下看吧! 自定义View 定义成员变量 private int mHeight, mWidth;//宽高 private Paint mPaint;//扇形的画笔 private Paint mTextPaint;//画文字的画笔 private int centerX, centerY;//中心坐标 //"其他"的value //扇形图分成太多快 所以要合并一部分为其他 即图中灰色部分 private double

  • Android 实现会旋转的饼状统计图实例代码

    Android 实现会旋转的饼状统计图实例代码 最近在做一个项目,由于有需要统计的需要,于是就做成了下面饼状统计图. 下图是效果图: 大致思路是: 关于的介绍这里不做详细介绍,如果想深入请点击开源项目MPAndroidChart 下面是其实现: 首先是添加MPAndroidChart依赖: maven { url "https://jitpack.io" } compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' Mainactivity

  • Android编程实现canvas绘制柱状统计图功能【自动计算宽高及分度值、可左右滑动】

    本文实例讲述了Android编程实现canvas绘制柱状统计图功能.分享给大家供大家参考,具体如下: 这里实现了一个简单的柱状统计图,如下:   特点: 1.根据数据源自动计算每个条目的高度.宽度.间距,自动计算分度值. 2.当条目数较多时,可左右滑动查看全部内容,图形.文字同步滑动,并且松手后会渐渐的停下来(而不是立刻停下来). 代码: (1)核心代码:BarChartView.Java package com.sina.appbarchart; import android.app.Acti

  • Android自定义View实现多边形统计图示例代码

    前言   最近利用空闲时间学习了自定义View的一些知识,为了巩固,写了一个小东西,顺便分享出来,下面话不多说了,来一起看看详细的介绍吧. 简介   一个多边形统计图.边数,每个方向的值,每个点的文字等等都是可以设置的. 下面就来分析一下这个自定义View 这个view由以下几个部分组成 M层N边形 中心到各顶点的连线 填充区域 文字 @Override protected void onDraw(Canvas canvas) { if (!canDraw()) { return; } canv

  • Android自定义滑动验证条的示例代码

    本文介绍了Android自定义滑动验证条的示例代码,分享给大家,具体如下: *注:不知道为什么,h5的标签在这里没用了,所以我也只能用Markdown的语法来写了 项目地址:https://github.com/994866755/handsomeYe.seekbar.github.io 需求: 在我们的某些应用中需要滑动验证.比如说这个样子的: 刚开始我也很懵逼要怎么去弄,结果我去看了一些人的代码,有人是用自定义viewgroup去做,就是viewgroup包含滑动块和滑动条.但我觉得太麻烦,

  • Android自定义view实现太极效果实例代码

    Android自定义view实现太极效果实例代码 之前一直想要个加载的loading.却不知道用什么好,然后就想到了太极图标,最后效果是有了,不过感觉用来做loading简直丑到爆!!! 实现效果很简单,我们不要用什么贝塞尔曲线啥的,因为太极无非就是圆圆圆,只要画圆就ok了.来上代码: 因为有黑有白,所以定义2个画笔分别为黑和白. private void inital() { whitePaint = new Paint(); whitePaint.setAntiAlias(true); wh

  • Android自定义view实现圆环效果实例代码

    先上效果图,如果大家感觉不错,请参考实现代码.           重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePercentBar extends View{ private Context mContext; private int mArcColor; private int mArcWidth; private int mCenterTextColor; private int mCent

  • Android自定义view实现雪花特效实例代码

    目录 一.前言 二.创意名 三.效果展示 四.实现步骤 五.编码实现 总结 一.前言 这个冬天,老家一直没有下雨, 正好圣诞节,就想着制作一个下雪的特效. 圣诞祝福:平安夜,舞翩阡.雪花飘,飞满天.心与心,永相伴. 圣诞节是传统的宗教节日,对于基 督徒,那是庆祝耶稣的诞生,纪念耶稣和发扬基督精神.现在整个西方社会都在过圣诞节,像许多宗教节日一样,它已经越来越民俗化了. 尽管如此,圣诞节依然倍受尊重.人们在圣诞快乐中怀有对耶稣的敬仰,欢乐的节庆里含有庄严肃穆的神念.欢度圣诞佳节的人都不拒绝耶稣的教

  • Android自定义View圆形图片控件代码详解

    前言 在日常开发中,圆形的图片效果还是很常见的.可以通过给Paint设置Xfermode来实现,这里简单记录如下. 实现 实现圆形效果的核心是PorterDuffXfermode,对于PorterDuffXfermode,这里不展开,可以查询相关资料. 核心代码 //绘制背景 canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2, mPaint); //设置模式为:显示背景层和上层的交集,且显示上层图像 mPaint.setXfermode(new

  • android自定义View圆圈拖动

    本文实例为大家分享了android自定义View圆圈拖动的具体代码,供大家参考,具体内容如下 问题: 1 . 累加问题:"点击坐标"坐标在移动时必须改变位置,不然将导致累加过载 2. 圆形改变问题,每次刷新时圆必将改变位置 3. 图片平移:圆在移动时只要 public class MovingBlockView extends View { //画笔 Paint paint = new Paint(); Region circleRegion; Path circlePath; pri

  • Android自定义View画圆功能

    本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下 引入布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools&q

  • Android 自定义view实现TopBar效果

    本文实例为大家分享了Android自定义view实现TopBar的具体代码,供大家参考,具体内容如下 布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/t

  • Android自定义View倒计时圆

    本文实例为大家分享了Android自定义View倒计时圆的具体代码,供大家参考,具体内容如下 创建attr <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CountDownView"> <!--颜色--> <attr name="ringColor" format=&q

随机推荐