自定义View:Padding与绘制内容

有些时候,扩展Android框架提供的view并不能很好地解决问题。很多情况下,我们需要进行view绘制来实现想要的效果。本文我们将介绍如何使用Canvas绘制折线图,同时也会介绍一些视图的尺寸和padding的一些工作原理。

简单绘制

如果你打算在自定义的view中控制绘制内容,最好是直接继承自View类。它是最基础的UI绘制单元。它相对来说功能齐全,虽然相比其他子类少一些功能,但对于本文还是够用的。

自定义绘制步骤

1.创建一个继承自View的类 2.重写onDraw方法,在该方法内,使用Canvas进行内容绘制。

注意:这里我们不需要调用父类(View)的onDraw方法,因为View.onDraw方法为空实现。

1 2 3 4 5 6 7 8

@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setStyle(Style.STROKE); paint.setColor(0xFF33B5E5); paint.setStrokeWidth(4); canvas.drawLine(0, 0, getWidth(), getHeight(), paint); }

上面代码的意思是

  • 绘制一个蓝色(0xFF33B5E5)的线
  • 起点坐标为(0,0) 终点坐标为(getWidth(), getHeight())
  • 线的宽度为4像素

Paint是用来控制绘制的类,使用它我们可以实现超级多的效果。这里我们仅仅使用了它的简单功能。

注意,当我们绘制内容时,该View的左上角的坐标为(0,0),不管这个view位于屏幕的哪个位置。View有两个方法,getLeft()和getTop(),但是它们返回的是这个相对与父View的位置信息,所以在绘制view内容时,不能使用这两个值。

处理Padding

通常情况下,我们可以在xml布局文件中设置padding等信息,但是对于上面的onDraw方法来说,由于我们并没有处理padding,所以布局文件的padding值是不生效的。

在View中,视图的宽度和高度包含了padding的值,比如一个view的宽度为100像素,两侧的padding值为10像素,那么view的内容只有80像素的绘制宽度。同理高度也是一样。

在View中获取宽度,我们可以使用getWidth(),获取padding,可以使用getPaddingTop(), getPaddingBottom, getPaddingLeft() and getPaddingRight()这些方法。

想要支持padding,通常修改起点和终点即可。这里我们设置起点为(getPaddingLeft(), getPaddingTop()) 终点为(getWidth() – getPaddingRight(), getHeight() – getPaddingBottom())。

支持padding的onDraw代码如下

1 2 3 4 5 6 7 8 9 10 11 12

@Override protected void onDraw(Canvas canvas) { paint.setStyle(Style.STROKE); paint.setColor(0xFF33B5E5); paint.setStrokeWidth(4); int left = getPaddingLeft(); int top = getPaddingTop(); int right = getWidth() - getPaddingRight(); int bottom = getHeight() - getPaddingBottom(); canvas.drawLine(left, top, right, bottom, paint); }

之后后的效果图如下

因情况而已,你可能不许要支持padding,但是我还是建议你加上对padding的处理,以备后用。

绘制折线图

首先,为了便于理解,我们先看一看,最终的折线图的样子。

想要绘制上图,实际上需要很多的点坐标,及x轴的值与y轴的值。为了简单,我们这里只需要提供y轴的值,而x轴的值就是y轴值数组的索引。

以下就是View提供的设置数据的方法。

1 2 3 4 5 6 7 8 9 10 11 12

/** * Sets the y data points of the line chart. The data points * are assumed to be positive and equally spaced on the x-axis. * The line chart will be scaled so that the entire height of * the view is used. * * @param datapoints * y values of the line chart */ public void setChartData(float[] datapoints) { this.datapoints = datapoints.clone(); }

除了提供值外,我们还需要对这些值进行缩放来填充视图,以下是一个对Y轴坐标进行缩放转换的方法。

1 2 3 4 5 6 7

private float getYPosition(float value, float maxValue) { float height = getHeight() - getPaddingTop() - getPaddingBottom(); value = (value/maxValue) * height; float offset = height - value;//确保数值低的点位于底部 offset = offset + getPaddingTop(); return offset; }

getYPosition这个方法

  • 它接受一个y轴坐标和一个最大的y轴坐标,进行缩放处理后,返回适用于该View的值
  • value = (value/maxValue) * height 这一步用来获取缩放的初始值
  • float offset = height - value;由于折线图需要y轴低的点位于底部,所以需要做转换
  • 除此之外,我们还要考虑到paddingTop的值,这就是为什么要使用offset = offset + getPaddingTop();的原因

我们现在就可以绘制折线图了,关于实现方案,我们根据数据点绘制很多线,但是我们这里采用Path来实现,相比之下,使用Path经过处理可以让绘制效果更好一些,如下为onDraw方法。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

@Override protected void onDraw(Canvas canvas) { mPaint.setShadowLayer(4, 20, 2, 0x80000000); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(4); Path path = new Path(); float maxValue = getMax(); path.moveTo(getXPosition(0), getYPosition(mData[0], maxValue)); for (int i = 1; i < mData.length; i++) { path.lineTo(getXPosition(i), getYPosition(mData[i], maxValue)); } canvas.drawPath(path, mPaint); }

上述方法用到的getXPosition实现如下

1 2 3

private float getXPosition(float value) { return value * (getWidth() / mData.length); }

细节处理

首先,我们需要的处理就是开启抗锯齿,开启后会减少线的锯齿感,让线看起来更加平滑。开启方法如下

1

paint.setAntiAlias(true);

其次,我们需要增加一些阴影来达到更好的展示效果。

1

paint.setShadowLayer(4, 2, 2, 0x80000000);

应用上面的代码,我们使用paint绘制出来的每条线都会有阴影效果。该方法的参数解释如下

  • 第一个参数意思是阴影的半径,其值越大,阴影也越大。如果该值为0,则表示移除阴影效果。
  • 第二个和第三个参数表示阴影的偏移量。我们设置2,2表示阴影相对实线向右偏移2个像素和向下偏移2个像素。
  • 第三个参数为阴影的颜色

同时我还增加了水平线作为背景这样看起来更符合折线图的效果,实现代码很简单,如下

1 2 3 4 5 6 7 8 9 10 11

private void drawBackground(Canvas canvas) { float maxValue = getMax(datapoints); int range = getLineDistance(maxValue); paint.setStyle(Style.STROKE); paint.setColor(Color.GRAY); for (int y = 0; y < maxValue; y += range) { final float yPos = getYPos(y); canvas.drawLine(0, yPos, getWidth(), yPos, paint); } }

除此之外,我们还可以增加更多的效果,利用Canvas,我们可以绘制线,路径,矩形,椭圆,位图等内容。使用Paint,我们可以更改填充方式,颜色,画笔宽度等很多效果。建议了解以下这两个类的API,然后自己写点小例子熟悉一下。

英文原文

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LeoXu的博客

借助 iText 用代码在 PDF 中创建空白签名域

15120
来自专栏androidBlog

自定义ProgressBar(包括自定义图片,带进度的圆形进度条、长方形进度条)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

1.9K10
来自专栏向治洪

android 自定义控件那些事

概述 在android应用开发过程中,固定的一些控件和属性可能满足不了开发的需求,所以在一些特殊情况下,我们需要自定义控件与属性。而自定义控件通常有两种:自定义...

23180
来自专栏前端儿

图片轮播(淡入淡出)--JS原生和jQuery实现

图片轮播(淡入淡出)--js原生和jquery实现 图片轮播有很多种方式,这里采用其中的 淡入淡出形式 js原生和jQuery都可以实现,jquery因为...

49110
来自专栏软件开发

CSS3与页面布局学习总结(一)——概要、选择器、特殊性与刻度单位

web前端开发者最最注的内容是三个:HTML、CSS与JavaScript,他们分别在不同方面发挥自己的作用,HTML实现页面结构,CSS完成页面的表现与风格,...

25380
来自专栏Android Note

Android – Path画搜索动画

39340
来自专栏梦魇小栈

JQuery分析及实现part6之动画模块功能及实现

10120
来自专栏程序员叨叨叨

听说你想玩RecyclerView嵌套GridView

有很多小伙伴们可能会遇到这样的问题: 为什么不论我传入多大size的List,我的GridView只能显示一行?

1.2K20
来自专栏移动开发的那些事儿

自定义实现垂直滚动的TextView

通过控制y参数可实现文字不同的垂直距离,这里的x,y并不代表默认横向坐标为0,纵向坐标为0的坐标,具体详解我觉得这篇博客解释的比较清楚,我们主要关注的是参数y的...

17520
来自专栏林德熙的博客

win10 uwp 拖动控件 Margin 移动Canvas 拖动控件Manipulation 拖动控件

我们会使用控件拖动,可以让我们做出好看的动画,那么我们如何移动控件,我将会告诉大家多个方法。其中第一个是最差的,最后的才是我希望大神你去用。

17300

扫码关注云+社区

领取腾讯云代金券