https://blog.csdn.net/harvic880925/article/details/50995268
如何让文字居中显示
如题,在自定义view中,如果要drawText,要求文字在控件的正中心,这之后咋做?
image.png
个人的测试,首先以任意中文字来获取文字的高度
var text="好里"
p.getTextBounds(text,0,text.length,bounds)
完事在获取实际文字的高度,分两种处理
- 比中文字的高度小的,或者等于,那么基线就设置为中心点往下如下数值
比如字母bdtl, gpy ,这两种都比中文高度小,可如果放一起就比中文字高度大了
val offset=bounds.height()/2-bounds.bottom
- 比中文字高度大的
这种一般是英文字母,bg之类的,也就是有字母在基线上的b,d,t,l,也有在基线下g,p,y之类的
offset=bounds.height()/2-this.descent.roundToInt()
paint的属性
常见的几种
p.apply {
strokeWidth=20f 画笔的粗细,宽度
textAlign=Paint.Align.CENTER//对drawText有效,文字从哪里开始画
textSize=33f//文字的字体大小
style=Paint.Style.FILL_AND_STROKE
color=Color.parseColor("#550000ff") //画笔的颜色
}
image.png
以画个圆圈为例
canvas.drawCircle(width/2f,height/2f,100f,p)
如上图style的3种解释如下
public enum Style {
/**
* Geometry and text drawn with this style will be filled, ignoring all
* stroke-related settings in the paint.
*/
FILL (0),//圈内部
/**
* Geometry and text drawn with this style will be stroked, respecting
* the stroke-related fields on the paint.
*/
STROKE (1), 画笔宽度平分,一半在圈内,一半在圈外
/**
* Geometry and text drawn with this style will be both filled and
* stroked at the same time, respecting the stroke-related fields on
* the paint. This mode can give unexpected results if the geometry
* is oriented counter-clockwise. This restriction does not apply to
* either FILL or STROKE.
*/
FILL_AND_STROKE (2); 上边两个加起来的就是,圆圈内部,加上画笔宽度的一半
}
Aligin 下图,x坐标为中心线
image.png
public enum Align {
/**
* The text is drawn to the right of the x,y origin
*/
LEFT (0),//文字在x坐标的右边
/**
* The text is drawn centered horizontally on the x,y origin
*/
CENTER (1),//文字的中心在x坐标
/**
* The text is drawn to the left of the x,y origin
*/
RIGHT (2);//文字的右边在x坐标上
private Align(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
paint.setShadowLayer,效果图上边有
radius:阴影文字的模糊半径,必须大于0,小于等于0的话这个就无效了。不能太大,稍微大点就模糊的没有了。。
dx:阴影文字相对原始文字在x轴的偏移量,和坐标轴方向一致,大于0自然是往右
dy:y轴方向的,同上
shadowColor:阴影文字的颜色
public void setShadowLayer(float radius, float dx, float dy, int shadowColor)
drawline
drawpoint
drawrect
这些简单的就不说了
drawOval 其实就是在一个矩形内,也是需要4个点,或者一个rect
圆弧,椭圆的一部分,rect就是椭圆的范围了,也可以是4个点。
startAngle:起始角度,x轴右侧为0度,顺时针为正
sweepAngle:经过的角度,最终的角度就是起始角度加上这个
useCenter:是否把圆弧和中心点连接起来。
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint)
//代码如下,效果图在下边
val rect=RectF(width/2f-100,height/2f-100,width/2f+100,height/2f+100)
p.color=Color.RED
canvas.drawArc(rect,0f,90f,false,p)
p.color=Color.BLUE
canvas.drawArc(rect,0f,-90f,true,p)//圆弧和中心点连接起来了。
image.png
Cap
处理线条的起点和终点的效果的,默认的BUTT,没效果
/**
* The Cap specifies the treatment for the beginning and ending of
* stroked lines and paths. The default is BUTT.
*/
public enum Cap {
/**
* The stroke ends with the path, and does not project beyond it.
*/
BUTT (0),
/**
* The stroke projects out as a semicircle, with the center at the
* end of the path.
*/
ROUND (1),以圆弧结尾,就是凸出去一个半圆
/**
* The stroke projects out as a square, with the center at the end
* of the path.
*/
SQUARE (2);这个是凸出去一个矩形,凸出去的就是画笔宽度的一半,下图可以看到效果
}
p.strokeWidth=15f
p.style=Paint.Style.FILL
p.strokeCap=Paint.Cap.BUTT
canvas.drawLine(10f,20f,200f,20f,p)
p.strokeCap=Paint.Cap.ROUND
canvas.drawLine(10f,40f,200f,40f,p)
p.strokeCap=Paint.Cap.SQUARE
canvas.drawLine(10f,60f,200f,60f,p)
image.png
JOIN
处理线条相交的地方,有3种,看效果图就知道了
image.png
/**
* The Join specifies the treatment where lines and curve segments
* join on a stroked path. The default is MITER.
*/
public enum Join {
/**
* The outer edges of a join meet at a sharp angle
*/
MITER (0),
/**
* The outer edges of a join meet in a circular arc.
*/
ROUND (1),
/**
* The outer edges of a join meet with a straight line
*/
BEVEL (2);
}
测试代码如下
val path=Path().apply {
moveTo(20f,20f)
lineTo(80f,20f)
lineTo(80f,100f)
close()
}
p.style=Paint.Style.STROKE
p.strokeJoin=Paint.Join.MITER
canvas.drawPath(path,p)
path.offset(100f,0f)
p.strokeJoin=Paint.Join.ROUND
canvas.drawPath(path,p)
path.offset(100f,0f)
p.strokeJoin=Paint.Join.BEVEL
canvas.drawPath(path,p)
setStrokeMiter
个人测试,感觉这个是对它生效的p.strokeJoin=Paint.Join.MITER而且画笔的style不是是FILL.
就是对于尖角的处理,小点的话感觉和BEVEL差不多了,稍微大点就看不出效果了
/**
* Set the paint's stroke miter value. This is used to control the behavior
* of miter joins when the joins angle is sharp. This value must be >= 0.
*
* @param miter set the miter limit on the paint, used whenever the paint's
* style is Stroke or StrokeAndFill.
*/
public void setStrokeMiter(float miter)
p.strokeMiter=2f
p.style=Paint.Style.STROKE
p.strokeJoin=Paint.Join.MITER
其他文字相关
p.textSkewX=0.5f//文字的倾斜角度,我们平时说的斜体字,是 -0.25朝右倾斜的
paint.setUnderlineText(true);//设置下划线
paint.setStrikeThruText(true);//设置带有删除线效果
模糊
image.pngimage.png
image.png
image.png
setLayerType(LAYER_TYPE_SOFTWARE,null)
p.setMaskFilter(BlurMaskFilter(5f,BlurMaskFilter.Blur.SOLID))
public enum Blur {
/**
* Blur inside and outside the original border.
*/
NORMAL(0),
/**
* Draw solid inside the border, blur outside.
*/
SOLID(1),
/**
* Draw nothing inside the border, blur outside.
*/
OUTER(2),
/**
* Blur inside the border, draw nothing outside.
*/
INNER(3);
}
bitmap有个方法如下,可以获取一个新的bitmap,只包含透明度,没有色值,或者说返回的图片就是黑色的,带透明度的,
如下图,左侧是原图,浅蓝色外圈中间是白色的, 右边的是只有透明度的图
image.png
/**
* Returns a new bitmap that captures the alpha values of the original.
* This may be drawn with Canvas.drawBitmap(), where the color(s) will be
* taken from the paint that is passed to the draw call.
*
* @return new bitmap containing the alpha channel of the original bitmap.
*/
@CheckResult
public Bitmap extractAlpha() {
return extractAlpha(null, null);
}
上边说道了blur,可以模糊画笔,那么对这种图片的影响是啥,可以看下效果,染成红色
image.png
val bpExtra = bp!!.extractAlpha()//或者bp!!.extractAlpha(Color.RED,null),后边canvas draw的时候就不需要paint了
p.color = Color.RED
p.setMaskFilter(BlurMaskFilter(5f, BlurMaskFilter.Blur.OUTER))
canvas.drawBitmap(bpExtra, 10f, 10f, p)
Shader
Shader is the based class for objects that return horizontal spans of colors during drawing. A subclass of Shader is installed in a Paint calling paint.setShader(shader). After that any object (other than a bitmap) that is drawn with that paint will get its color(s) from the shader
image.png
image.png
BitmapShader
给paint设置了这个shader以后,那么就相当于画布从0,0开始到bitmap的宽,高范围里,paint的色值已经确定了。
这时候如果你canvas.drawCircle,那么就会看到这个圆圈里就是图片的一部分,好像望远镜一样
public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
LinearGradient
线性的着色器,就是创建一个沿着直线绘制线性渐变的着色器。
x0,y0,起始位置,x1,y1终点位置,然后颜色值,位置数组【取值范围0到1】,长度和颜色值一样,或者为空也可以。最后就是扩展模式了。上边有讲过
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
@Nullable float positions[], @NonNull TileMode tile)
//上边构造方法的简化版,
public LinearGradient(float x0, float y0, float x1, float y1,
@ColorInt int color0, @ColorInt int color1,
@NonNull TileMode tile)
实现文在渐变动画也比较简单
我们在一个线条范围内设置个色值变化,完事设置个TileMode,那么其实整个画布范围内的颜色就已经确定了
比如我们设置repeat模式,完事色值从红到蓝,范围从0到100,那么100到200也是红到蓝,200到300也是
linearGradient=LinearGradient(100f-bounds.width(),0f,100f,0f, intArrayOf(Color.RED,Color.BLUE), floatArrayOf(0f,1f),Shader.TileMode.MIRROR)
val mt=Matrix()
mt.setTranslate(transX.toFloat(),0f)//对x轴方向进行平移,比如移动了10,那么就相当于10到110是红蓝,-90到10是红蓝。
linearGradient?.setLocalMatrix(mt)
p.setShader(linearGradient)
canvas.drawText(text,0,text.length,100f,100f,p)
RadialGradient
从内向外发射的一个着色器,构造方法和线性的差不多,不过这种是从圆心向外部扩散,就和波纹一样
参数,中心原点,半径,颜色,完事根据重复的模式TileMode,也能知道半径以外的色值了,也就是整个画布的颜色都确定拉。
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorInt int colors[], @Nullable float stops[],
@NonNull TileMode tileMode)
public RadialGradient(float centerX, float centerY, float radius,
@ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode)
image.png
代码如下
p.shader=RadialGradient(100f,100f,50f,Color.RED,Color.BLUE,Shader.TileMode.REPEAT)
canvas.drawCircle(100f,100f,80f,p)
canvas.drawCircle(300f,150f,100f,p)
p.shader=null
SweepGradient
类似雷达扫描那种,给个中心点,给几个色值即可
image.png
public SweepGradient(float cx, float cy,
@NonNull @ColorInt int colors[], @Nullable float positions[])
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1)
ComposeShader
这种没太弄懂,就是2种shader根据PorterDuff.Mode进行重组,可是以我的理解,每种shader好像大小都是无限的也就是和画布大小一样,那么这个Mode感觉是无用的啊,最终还是用的shaderA
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
@NonNull PorterDuff.Mode mode)
canvas.drawBitmapMesh
这个方法可以对图片进行一些处理,比较扭曲,拉伸等,就是把图片分成多个网格,然后操作顶点的位置
bitmap:一张图
meshWidth:横向分成多少份
meshHeight:纵向分成多少份
verts:所有顶点的坐标,依次是x,y,也就是偶数索引存储x,奇数索引存储y值,数组最小值为
(meshWidth+1) * (meshHeight+1) * 2 + vertOffset
vertOffset: verts数组跳过多少条数据
colors:最小长度(meshWidth+1) * (meshHeight+1) + colorOffset,可以为空,弄个颜色,大家一起来染色
colorOffset:同vertOffset,相对colors来说的
public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
@NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
@Nullable Paint paint)
关键还是对verts里的坐标进行处理。其他都不是重点。
网友评论