Canvas:The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
根据doc的介绍我们可以看到canvas是画集合图形的一个重要类,它持有draw动作(就是画图).使用draw的时候我们需要4个基本条件:
1. 一个用来保存像素的Bitmap
2. Canvas
用来调用draw方法
3. 我们要画的材料(Rect,Path,Text,Bitmap,Color…)
4. Paint,用来设置内容的色值,样式,透明度…
这是根据文档中的介绍而翻译的,但是我们可以仔细瞅瞅,平时我们的自定义view也就是这个条件,例子我就列举了,随便找个自定义view就可以发现都是这么做的.
MATRIX_SAVE_FLAG,CLIP_SAVE_FLAG在使用save(flag)时被调用
ALL_SAVE_FLAG,CLIP_TO_LAYER_SAVE_FLAG,FULL_COLOR_LAYER_SAVE_FLAG,HAS_ALPHA_LAYER_SAVE_FLAG在使用saveLayer()或者saveLayerAlpha时调用.
我们可以使用如下code来进行测试:
...
canvas?.save(Canvas.flag);
...
canvas?.draw()...
canvas?.restore();
...
canvas?.draw()
Canvas(Bitmap bitmap) == Canvas() + setBitmap(Bitmap bitmap)
裁剪:clipPath();clipRect()
在介绍裁剪前,我们先介绍一下region这个类.
用于指定的几何区域剪裁区域图。
组合两个区域时可以执行的逻辑操作,既在多个裁剪区域发生重叠时,可以使用该类来实现我们需要的功能,如果是单个图形裁剪时,各个值对应显示的裁剪形状相同,同时,clip()都有默认值
我们可以举例:有A和B两个几何图形,对他们进行裁剪,同时他们相交,B使用Region.Op指定
注:该部分的含义我们可以从SkRegion.h
中的注释了解,如果想要理解具体内容的话,可以自己研究SkRegion.h,SkRegion.cpp
/**
* The logical operations that can be performed when combining two regions.
*/
enum Op {
kDifference_Op, //!< subtract the op region from the first region
kIntersect_Op, //!< intersect the two regions
kUnion_Op, //!< union (inclusive-or) the two regions
kXOR_Op, //!< exclusive-or the two regions
/** subtract the first region from the op region */
kReverseDifference_Op,
kReplace_Op, //!< replace the dst region with the op region
kLastOp = kReplace_Op
};
注意:op指的是我们裁剪时,裁剪完后显示的图形区域.如果clip()中没有使用Region.Op时,我们可以去Canvas中看看,一般都有默认Region.Op被调用
示例:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.save()
canvas?.translate(5f, 5f)
paint.setColor(Color.parseColor("#00ff00"))
canvas?.drawRect(RectF(0f, 0f, 300f, 300f), paint)
canvas?.drawCircle(300f, 300f, 150f, paint)
paint.setColor(Color.parseColor("#ff0000"))
// 对一个矩形进行裁剪
canvas?.clipRect(RectF(0f, 0f, 300f, 300f))
val mPath = Path()
mPath.addCircle(300f, 300f, 150f, Path.Direction.CCW)
// 对指定的path进行裁剪
canvas?.clipPath(mPath, Region.Op.INTERSECT)
// 显示裁剪内容
canvas?.drawRect(RectF(0f, 0f, Integer.MAX_VALUE.toFloat(), Integer.MAX_VALUE.toFloat()), paint)
canvas?.restore()
}
我们可以看到Rect裁剪和Path裁剪完后显示出来的内容
注意:使用完clip()后,必须使用canvas.draw(),才可以把我们要裁剪的内容画出来
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.save()
canvas?.translate(5f, 5f)
var width: Int = bitmap.width
var hegith: Int = bitmap.height
var radius: Int
when {
width < hegith -> radius = width / 2
width > hegith -> radius = hegith / 2
else -> radius = width / 2
}
var path = Path()
// path.addCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), Path.Direction.CW)
path.addRoundRect(RectF(0f, 0f, width.toFloat(), hegith.toFloat()), 30f, 30f, Path.Direction.CW)
canvas?.clipPath(path, Region.Op.INTERSECT)
canvas?.drawBitmap(bitmap, 0f, 0f, paint)
canvas?.restore()
}
我记得前边写过这种图形,其中一个是BitmapShader,如果有兴趣的话可以去看看这个类,这个类可以实现图片的各种形状.
canvas.clipPath()也可以根据Path绘制各种形状的图片,例如五角星,三角形,正方形…….
paint.setShader(BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
canvas?.drawRect(0f, 0f, 600f, 300f, paint)
paint.setShader(null)
paint.color = Color.parseColor("#4400ff00")
canvas?.clipRect(50, 20, 550, 280)
canvas?.drawRect(0f, 0f, Float.MAX_VALUE, Float.MAX_VALUE, paint)
canvas?.restore()
paint.setShader(BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
paint.color = Color.RED
canvas?.clipRect(50, 20, 400, 200)
canvas?.drawRect(0f, 0f, 600f, 300f, paint)
paint.setShader(null)
这个就不举例子了,最上面的那个就是,如果有兴趣的话,可以自己找些例子看看,或者自己写写
这个也不举例了,把上面的代码复制一份,将clip()修改为clipOut()就可以了,注意这个clipOut()需要运行在API26版本上.
动画:移动(translate),旋转(rotate),缩放(scale),错切(skew);矩阵(Matrix)变换(translate,rotate,scale,skew)
canvas的几何变换实际上还是对画布的矩阵进行改变来实现目地的
例:
/**
* Preconcat the current matrix with the specified translation
*
* @param dx The distance to translate in X
* @param dy The distance to translate in Y
*/
public void translate(float dx, float dy) {
if (dx == 0.0f && dy == 0.0f) return;
nTranslate(mNativeCanvasWrapper, dx, dy);
}
这是Canvas源码中的translate()函数,通过注释(将指定矩阵前乘当前矩阵
)可以发现实际上是通过Matrix来实现变换的.矩阵的变换我们下边再介绍
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.translate(5f, 5f)
canvas?.concat(mMatrix);
// canvas?.matrix=mMatrix
canvas?.drawBitmap(bitmap, 0f, 0f, paint)
}
public fun click() {
mMatrix.setTranslate(10f, 20f)
// mMatrix.postTranslate(10f, 20f)
// mMatrix.preTranslate(10f, 20f)
invalidate()
}
我们先看一下canvas.matrix和canvas.concat()他们的效果相同,但是有什么区别呢?来我们看看源码:
/**
* Preconcat the current matrix with the specified matrix. If the specified
* matrix is null, this method does nothing.
*
* @param matrix The matrix to preconcatenate with the current matrix
*/
public void concat(@Nullable Matrix matrix) {
if (matrix != null) nConcat(mNativeCanvasWrapper, matrix.native_instance);
}
/**
* Completely replace the current matrix with the specified matrix. If the
* matrix parameter is null, then the current matrix is reset to identity.
*
* Note: it is recommended to use {@link #concat(Matrix)},
* {@link #scale(float, float)}, {@link #translate(float, float)} and
* {@link #rotate(float)} instead of this method.
*
* @param matrix The matrix to replace the current matrix with. If it is
* null, set the current matrix to identity.
*
* @see #concat(Matrix)
*/
public void setMatrix(@Nullable Matrix matrix) {
nSetMatrix(mNativeCanvasWrapper,
matrix == null ? 0 : matrix.native_instance);
}
根据注释我们可以看到
(1)concat(matrix)的意思是将制定矩阵和canvas当前矩阵连接起来,也就是将它前乘制定矩阵;
(2)setMatrix(matrix),使用制定矩阵替代canvas当前矩阵
(3)在canvas变换的时候一般使用concat(),rotate(float),scale(float, float),translate(float, float)来替代setMatrix()
setTranslate()和preXXX或者postXXX效果不相同,这是怎么回事呢???
通过源码我们发现:
(1) 使用mMatrix.setXXX()的时候,Matrix的值变为单位值,也就是Matrix先变成成单位矩阵,让后在执行变换,也就是说上文中的setTranslate(10f, 20f)实际上是,先变成单位矩阵(回到最初效果),然后只执行我们的移动,这样就出现了上图中的效果,移动一次后再也不移动了.
(2)而使用preTranslate(),postTranslate()时,M’ = M * T,既每次调用它们时,我们要变换的效果矩阵乘以当前矩阵,把相乘后的矩阵赋给Matrix,这样不断循环,一直相乘我们的变换矩阵,不断移动,就会出现上图效果了.
注:我们在自定义view中实现图片拖动,旋转等效果就是根据这个原理来实现的.
简单介绍,具体的内容后边介绍
preXXX和postXXX一个是前乘一个是后乘。我们知道Matrix是一个矩阵,而我们设置的参数也是一个矩阵,最终的结果肯定是这两个矩阵相互运算后得出的。
对于矩阵可以这样说,图形的变换实质上就是图形上点的变换,而我们的Matrix计算也是基于此,比如点P(x0,y0)经过矩阵变换后会去到P(x,y)的位置。学过矩阵的就知道了,矩阵乘法是不能交换左右位置的
,矩阵相乘时前乘和后乘就很重要了。
preXXX和postXXX一个是前乘一个是后乘.setxxx是设置当前矩阵,不进行运算。
举例:
matrix.preScale(0.5f, 1);
matrix.setScale(1, 0.6f);
matrix.postScale(0.7f, 1);
matrix.preTranslate(15, 0);
把上面的代码,变为用两个数字组成的 [x,x] 运算对象。
pre、post表示运算顺序。
遇到post的矩阵,就把这个矩阵放在已有矩阵后面进行乘法;
遇到pre,那么就把这个矩阵放到已有矩阵之前进行乘法。
那么,上面代码运算过程如下:
1. [0.5f,1] * 原始矩阵 = 矩阵A (因为是pre,所以设置的值放在原始矩阵之前相乘)
2. [1,0.6f] -> 矩阵A = [1,0.6f] = 矩阵B (因为是set,所以不会进行运算,直接重置所有值)
3. 矩阵B * [0.7f,1] = 矩阵C (因为是post,所以把设置的值放在后面相乘)
4. [15,0] * 矩阵C = 最终结果 (因为是pre,所以把设置值放在矩阵之前相乘)
所以,我们如果想要实现多种变换按照一定顺序实现的话,可以使用preXXX()和postXXX()来实现我们想要的效果,例如图片拖动和缩放控件
给画布填充色值
draw(几何图形)
画弧形和弧线:
paint.strokeWidth = 3f
var rectF1 = RectF(0f, 0f, 100f, 100f)
paint.style = Paint.Style.FILL_AND_STROKE
paint.color = Color.RED
canvas?.drawRect(rectF1, paint)
paint.style = Paint.Style.STROKE
paint.color = Color.WHITE
canvas?.drawArc(rectF1, 0f, 120f, true, paint)
paint.style = Paint.Style.FILL_AND_STROKE
var rectF2 = RectF(0f, 150f, 100f, 250f)
paint.color = Color.RED
canvas?.drawRect(rectF2, paint)
paint.color = Color.WHITE
paint.style = Paint.Style.STROKE
canvas?.drawArc(rectF2, 0f, 120f, false, paint)
var rectF3 = RectF(150f, 0f, 250f, 100f)
paint.style = Paint.Style.FILL_AND_STROKE
paint.color = Color.RED
canvas?.drawRect(rectF3, paint)
paint.color = Color.WHITE
canvas?.drawArc(rectF3, 0f, 120f, true, paint)
var rectF4 = RectF(150f, 150f, 250f, 250f)
paint.color = Color.RED
canvas?.drawRect(rectF4, paint)
paint.color = Color.WHITE
canvas?.drawArc(rectF4, 0f, 120f, false, paint)
使用这个函数,我们可以画自定义圆形进度条、钟表、汽车油表…如果有兴趣,可以自己去写,当然还的有其他函数如:drawText(),drawLine(),有这三个基本就可以实现了.
这三个函数画矩形,非常简单,示例就不写了
rect
:圆弧矩形边界
rx
:x方向上的圆角半径。
ry
:y方向上的圆角半径
这两个函数我们应该了解,圆形矩形,例如我们在使用BitmapShader画带弧度的的矩形图片
例:
paint.setShader(BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
var rectF1 = RectF(0f, 0f, 240f, 160f)
canvas?.drawRoundRect(rectF1, 40f, 40f, paint)
paint.shader = null
paint.style = Paint.Style.FILL_AND_STROKE
paint.strokeWidth = 1f
paint.color = Color.BLACK
canvas?.drawPoint(250f,40f,paint)
canvas?.drawPoint(250f,80f,paint)
根据基线(baseline)坐标将文字画到画布上
paint.textSize = 20f
// 设置色值渐变
paint.shader = LinearGradient(0f, 0f, 80f, 30f, Color.RED, Color.GREEN, Shader.TileMode.CLAMP)
paint.setShadowLayer(5f, 2f, 3f, Color.parseColor("#77000000"))
canvas?.drawText("android开发", 0, 3, 5f, 30f, paint)
从中我们可以看出,给字体设置样式,色值,阴影等效果都是使用paint来设置
canvas?.translate(500f, 500f)
canvas?.rotate(180f)
paint.textSize = 38f
paint.color = Color.RED
paint.style = Paint.Style.STROKE
var text = "I'm an android developer and I'm testing the canvas feature"
var path = Path()
path.addCircle(0f, 0f, 200f, Path.Direction.CW)
path.close()
paint.pathEffect = CornerPathEffect(60f)
canvas?.drawPath(path, paint)
paint.color = Color.BLACK
canvas?.drawTextOnPath(text, path, 0f, 0f, paint)
上文中提到的自定义汽车油表中的文字就是使用该函数来实现的,只是路径不同而已.
如果有兴趣的话,可以修改hOffset, vOffset的值来看看效果,
这个函数中的context我看了半天也不是太明白,网上搜了半天,找到一个解释详细的,如果想仔细理解的话点击查看
canvas?.drawTextRun(text, 0, text.length, 0, text.length, 0f, 100f, false, paint)
canvas?.drawTextRun(text, 0, text.length, 0, text.length, 100f, 100f, true, paint)
var floatArray = floatArrayOf(0f, 0f, 10f, 400f, 10f, 10f, 20f, 410f, 20f, 20f, 30f, 420f, 30f, 30f, 40f, 430f)
canvas?.drawLines(floatArray, 4, 12, paint)
var floatArray1 = floatArrayOf(60f, 0f, 70f, 400f, 70f, 10f, 80f, 410f, 80f, 20f, 90f, 420f, 90f, 30f, 100f, 430f)
canvas?.drawLines(floatArray1, paint)
绘制点,同上面的绘制线,只是线由两点四个数组成,而点由两个数组成,其他相同
paint.textSize = 20f;
canvas?.translate(200f, 200f)
paint.strokeWidth = 5f
paint.shader = SweepGradient(0f, 0f, Color.RED, Color.YELLOW)
paint.style = Paint.Style.STROKE
canvas?.drawCircle(0f, 0f, 100f, paint)
paint.shader = null
for (i in 0 until 12) {
canvas?.drawLine(0f, 80f, 0f, 95f, paint)
canvas?.drawText((i + 1).toString(), -10f, 75f, paint)
canvas?.rotate(30f)
}
绘制椭圆,这个函数就不举例,自己写吧
别人写的非常值得一看的一篇path文章
该函数和Picture类在其他文中有单独介绍有兴趣可以点击查看
给画布中的位图填充色值
将bitmap位图绘制到画布中,matrix作用到bitmap
canvas?.translate(500f, 500f)
mMatrix.postRotate(45f)
mMatrix.postSkew(0.2f, -0.5f)
canvas?.drawBitmap(bitmap, mMatrix, paint)
canvas?.translate(100f, 100f)
var mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
var mCanvas: Canvas = Canvas(mBitmap)
paint.color = Color.RED
canvas?.drawLine(0f, 0f, 200f, 0f, paint)
var intarray = intArrayOf(
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY,
Color.RED, Color.GREEN, Color.DKGRAY, Color.RED, Color.GREEN, Color.DKGRAY)
// 颜色数组的长度>=width*height,否则会出现数组越界异常
mCanvas.drawBitmap(intarray, 0, 10, 0, 5, 10, 10, false, paint)
canvas?.drawBitmap(mBitmap, 0f, 0f, paint)
这个函数类似
Bitmap.createBitmap(@NonNull @ColorInt int[] colors, int offset, int stride,int width, int height, @NonNull Config config)
canvas?.drawBitmap(bitmap, 0f, 0f, paint)
canvas?.translate(0f, 10f + bitmap.height.toFloat())
var imgRect = Rect(0, 0, 300, 250)
var rect = Rect(0, 0, 300, 250)
canvas?.drawBitmap(bitmap, imgRect, rect, paint)
canvas?.translate(0f, 260f)
var rect2 = Rect(0, 0, 380, 300)
canvas?.drawBitmap(bitmap, imgRect, rect2, paint)
canvas?.translate(0f, 310f)
var rect3 = Rect(0, 0, 100, 80)
canvas?.drawBitmap(bitmap, imgRect, rect3, paint)
canvas?.translate(0f, 90f)
var imgRect1 = Rect(0, 0, 600, 400)
var rect4 = Rect(0, 0, 525, 328)
canvas?.drawBitmap(bitmap, imgRect1, rect4, paint)
根据上图,我们可以看到:
src:指的是图片的剪切位置
dst:指的是绘制图片的位置
如果src小于原图,则剪切,如果dst小于或者大于src,图片进行缩放,然后存放到指定位置中.
根据左边和顶部左边将图片绘制到位图中.
该函数可以对 Bitmap进行各种扭曲
bitmap - 需要进行扭曲的位图
meshWidth - 横向网格数量
meshHeight - 纵向网格数量
verts - 网格顶点坐标数组,记录扭曲后图片各顶点的坐标.大小最小为 :(meshWidth+1) * (meshHeight+1) * 2 + vertOffset
vertOffset - 从第几个顶点开始对位图进行扭曲,通常传 0
colors - 设置网格顶点的颜色,该颜色会和位图对应像素的颜色叠加,数组大小为 (meshWidth+1) * (meshHeight+1) + colorOffset,可以传 null
colorOffset - 从第几个顶点开始转换颜色,通常传 0
这是google APIDemo中的示例
// 原理:将bitmap的高宽分为HEIGHT*WIDTH数组.根据实际高宽将相对应的值赋值到数组中.
// 通过触摸bitmap,将触摸的x,y点获取到,修改与之对应的数组内容,让后调用drawBitmapMesh改变图片的某个地方.
var paint: TextPaint
var bitmap: Bitmap
private val WIDTH = 60
private val HEIGHT = 60
private val COUNT = (WIDTH + 1) * (HEIGHT + 1)
private val mVerts = FloatArray(COUNT * 2)
private val mOrig = FloatArray(COUNT * 2)
private val mMatrix = Matrix()
private val mInverse = Matrix()
init {
paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
bitmap = BitmapFactory.decodeResource(resources, R.mipmap.mn)
isFocusable = true
val w = bitmap.getWidth().toFloat()
val h = bitmap.getHeight().toFloat()
// 根据图片高宽和网格数量,填充mVerts内容
var index = 0
for (y in 0..HEIGHT) {
val fy = h * y / HEIGHT
for (x in 0..WIDTH) {
val fx = w * x / WIDTH
setXY(mVerts, index, fx, fy)
setXY(mOrig, index, fx, fy)
index += 1
}
}
mMatrix.setTranslate(10f, 10f)
mMatrix.invert(mInverse)
}
private fun setXY(array: FloatArray, index: Int, x: Float, y: Float) {
array[index * 2 + 0] = x
array[index * 2 + 1] = y
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawColor(-0x333334)
canvas?.concat(mMatrix)
canvas?.drawBitmapMesh(bitmap, WIDTH, HEIGHT, mVerts, 0, null, 0, null)
}
private fun warp(cx: Float, cy: Float) {
val K = 10000f
val src = mOrig
val dst = mVerts
var i = 0
while (i < COUNT * 2) {
val x = src[i + 0]
val y = src[i + 1]
// 原数组中的数据
val dx = cx - x
val dy = cy - y
// 求原数组和给定数据的差值
val dd = dx * dx + dy * dy
val d = Math.sqrt(dd.toDouble()).toFloat()
var pull = K / (dd + 0.000001f)
pull /= d + 0.000001f
if (pull >= 1) {
dst[i + 0] = cx
dst[i + 1] = cy
} else {
dst[i + 0] = x + dx * pull
dst[i + 1] = y + dy * pull
}
i += 2
}
}
private var mLastWarpX = -9999
private var mLastWarpY: Int = 0
override fun onTouchEvent(event: MotionEvent): Boolean {
val pt = floatArrayOf(event.x, event.y)
mInverse.mapPoints(pt)
val x = pt[0].toInt()
val y = pt[1].toInt()
Log.e("ontouchevent", x.toString() + "--" + y.toString());
if (mLastWarpX != x || mLastWarpY != y) {
mLastWarpX = x
mLastWarpY = y
warp(pt[0], pt[1])
invalidate()
}
return true
}
效果图就不贴了,自己复制代码看效果吧
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawColor(-0x333334)
canvas?.clipRect(0f, 0f, 200f, 100f)
canvas?.drawBitmap(bitmap, 0f, 0f, paint)
var rect = Rect();
// rect = getClipBounds()
canvas?.getClipBounds(rect)
Log.e("Rect", rect.bottom.toString() + "--" + rect.right)
}
setDensity(int density)
指定此画布的背景位图的密度。既修改画布本身的目标密度,以及通过bitmap.setdensity (int)修改其背景位图的密度
getDrawFilter()
PaintFlagsDrawFilter(int clearBits, int setBits)
clearBits:要清除的属性:例如抗锯齿…
setBits:要设置的属性
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var bitmaps = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
var canvass = Canvas(bitmaps)
Log.e("Recanvassct", canvas?.width.toString() + "--" + canvass.width)
}
从中可以看出,一个canvas我们没有设置大小,它默认大小是该控件的高宽,而指定bitmap大小的canvas的大小是bitmap的大小
// 该函数是获取canvas中允许画Bitmap的最大宽度和高度.但是有一些不是特别理解,
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.style = Paint.Style.FILL_AND_STROKE
// 在原始图层上画图
canvas?.drawCircle(200f, 200f, 80f, paint)
// 创建一个新的透明图层(图层的边界是:0,0,300,300)(如果在该图层的paint没有透明色值时,则使用0x77该透明度值,如果paint有透明色值,则使用该paint的透明值)
canvas?.saveLayerAlpha(0f, 0f, 300f, 300f, 0x77)
// 在透明图层上画图
canvas?.drawColor(Color.parseColor("#44ff0000"))
// paint.color = Color.parseColor("#55ff0000")
canvas?.drawCircle(150f, 150f, 80f, paint)
// 恢复到原始图层
canvas?.restore()
paint.color = Color.GREEN
// 在原始图层上继续画图
canvas?.translate(-50f, -50f)
canvas?.drawCircle(250f, 250f, 50f, paint)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.style = Paint.Style.FILL_AND_STROKE
// 在原始图层上画图
canvas?.drawCircle(200f, 200f, 80f, paint)
// 创建一个新的透明图层(图层的边界是:0,0,300,300)(如果在该图层的paint没有透明色值时,则使用0x77该透明度值,如果paint有透明色值,则使用该paint的透明值)
val layerAlpha: Int? = canvas?.saveLayerAlpha(0f, 0f, 300f, 300f, 0x77)
// 在透明图层上画图
canvas?.drawColor(Color.parseColor("#44ff0000"))
// paint.color = Color.parseColor("#55ff0000")
canvas?.drawCircle(150f, 150f, 80f, paint)
// 创建一个新的图层layerAlpha1高宽400x400
val layerAlpha1 = canvas?.saveLayerAlpha(0f, 0f, 400f, 400f, 0x255)
paint.color = Color.parseColor("#ff0000")
canvas?.drawRect(0f, 0f, 100f, 100f, paint)
//该图层上画矩形
// 还原layerAlpha1图层
layerAlpha1?.let { canvas?.restoreToCount(it) }
paint.color = Color.GREEN
// 在layerAlpha图层上继续画图
canvas?.drawCircle(250f, 250f, 80f, paint)
// 还原layerAlpha图层到原始图层上
layerAlpha?.let { canvas?.restoreToCount(it) }
// 在最初的图层上画图
canvas?.drawCircle(350f, 350f, 80f, paint)
}
这是一个简单的示例:如果有不明白的可以修改属性和方法来测试.在学习Xfermode混合模式,编写示例的时候会用到这个几个函数.
在使用该类函数时,实际上有6个常量来表示保存不同的状态,但是,我们最好是使用系统推荐的ALL_SAVE_FLAG这个属性,一些其他属性和含有设置属性的方法已经过时了,不建议我们使用.
好了,canvas暂时就写到这了…,没有解决的问题后期会继续解决的