一,自定义 View 根底:
1,画笔 Paint
文本相干:
办法 | 形容 |
---|---|
setColor(@ColorInt int color) | 设置画笔色彩 |
setStrokeWidth(float width) | 设置画笔粗细 |
setTextSkewX(float f) | 设置歪斜,负右斜,正为左 |
setARGB(int a, int r, int g, int b) | 设置色彩,a 为透明度 |
setTextSize(float textSize) | 设置绘制文字大小 |
setFakeBoldText(boolean fakeBoldText) | 是否粗体 |
setTextAlign(Paint.Align align) | 设置文字对齐形式,LEFT,CENTER,RIGHT |
setUnderlineText(boolean underlineText) | 设置下划线 |
setStyle(Style style) | 设置画笔款式,FILL,STROKE,FILL_AND_STROKE setTypeface(Typeface typeface |
位图相干
办法 | 形容 |
---|---|
setDither(boolean dither) | 设置抖动解决 |
setAlpha(int a) | 设置透明度 |
setAntiAlias(boolean aa) | 是否开启抗锯齿 |
setFilterBitmap() | 是否开启优化 Bitmap |
setColorFilter(ColorFilter filter) | 设置色彩过滤 |
setMaskFilter(MaskFilter maskfilter) | 设置滤镜的成果 |
setShader(Shader shader) | 设置图像突变成果 |
setStrokeJoin(Paint.Join join) | 设置图像联合形式 |
setXfermode(Xfermode xfermode) | 设置图像重叠成果 |
setPathEffect(PathEffect effect) | 设置门路成果 reset() 复原默认设置 |
2,门路
moveTo() 绘制起始的点
lineTo() 绘制连贯的点
close() 造成闭环
arcTo() 弧线门路
参数:生成椭圆的矩形、弧线开始的角度、弧线扫描过的角度、是否强制地将弧线的起始点作为绘制起始地位
Region 区域:一块任意形态的关闭图形。应用 RegionIterator,用于区域相交填充操作。
3,画布 Canvas
1)革除画布
/**
* 画布清屏
*/
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
2)绘制画布背景
/**
* 绘制画布背景
*/
// 绘制画布背景,argb
canvas.drawARGB(99, 255, 0, 255);
canvas.drawRGB(255, 0, 255);
canvas.drawColor(Color.LTGRAY);
3)绘制圆形
/**
* 绘制圆形
*/
// 设置画笔的根本属性
// 设置画笔色彩
mPaint.setColor(Color.RED);
// 设置画笔填充款式
// 仅填充外部
mPaint.setStyle(Paint.Style.FILL);
// 填充外部和描边
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
// 仅填充描边
mPaint.setStyle(Paint.Style.STROKE);
// 设置描边宽度,单位 px,当填充款式是 FILL_AND_STROKE 时候无效
mPaint.setStrokeWidth(20);
// 应用画布画圆
canvas.drawCircle(200, 200, 150, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(200, 200, 130, mPaint);
4)绘制点
/**
* 绘制点
*/
// 点的大小
mPaint.setStrokeWidth(20);
// 绘制点,x 坐标、y 坐标
canvas.drawPoint(340, 340, mPaint);
5)绘制直线
/**
* 绘制直线
*/
// 直线粗细
mPaint.setStrokeWidth(20);
// 绘制直线终点 x 坐标、终点 y 坐标、起点 x 坐标、起点 y 坐标
canvas.drawLine(360, 360, 660, 660, mPaint);
6)绘制
/**
* 绘制矩形
*/
// 矩形的边框粗细
mPaint.setStrokeWidth(5);
// 仅填充矩形描边
mPaint.setStyle(Paint.Style.STROKE);
// 绘制矩形,RectF 是保留 float 类型的矩形,Rect 是保留 int 类型的矩形,左上右下
mRectF.set(400F, 400F, 450F, 450F);
mRect.set(500, 500, 550, 550);
canvas.drawRect(mRectF, mPaint);
canvas.drawRect(mRect, mPaint);
7)绘制文字
/**
* 绘制文字
* setColor(@ColorInt int color) 设置画笔色彩
* setStrokeWidth(float width) 设置画笔粗细
* setTextSkewX(float f) 设置歪斜,负右斜,正为左
* setARGB(int a, int r, int g, int b) 设置色彩,a 为透明度
* setTextSize(float textSize) 设置绘制文字大小
* setFakeBoldText(boolean fakeBoldText) 是否粗体
* setTextAlign(Paint.Align align) 设置文字对齐形式,LEFT,CENTER,RIGHT setUnderlineText(boolean underlineText) 设置下划线
* setStyle(Style style) 设置画笔款式,FILL,STROKE,FILL_AND_STROKE setTypeface(Typeface typeface) 设置 Typeface 对象,即字体格调,包含粗体,斜体以及衬线体,非衬线体等
*/
mPaint.setTextSize(90f);
canvas.drawText("Android Stack", 200, 1000, mPaint);
二,绘制文字
筹备根底类 BaseView,继承View 类。
public class BaseView extends View {
static final int LINE_OFFSET = 60;
protected Paint notePaint = new Paint();
protected Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
protected Rect textRect = new Rect();
public BaseView(Context context) {this(context, null);
}
public BaseView(Context context, AttributeSet attrs) {this(context, attrs, 0);
}
public BaseView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
}
}
在 BaseView 里筹备了 2 个画笔。
绘制文字的办法canvas.drawText
,须要指定字符串,绘制起始坐标,画笔 paint。
canvas.drawText(text, x, y, textPaint);
起始坐标的 y 值所在横线在前面也称为 baseline。
获取文字边界
从图中能够看到文字的局部笔画是能够超出边线的。比方结尾的j
。
应用 Paint.getTextBounds
办法获取文字的边界。
textPaint.getTextBounds(onShowText, 0, onShowText.length(), textRect); // 获取 text 边界
绘制上图的局部代码
private void drawBounds(Canvas canvas, float tx, float ty, String onShowText) {canvas.drawText(onShowText, tx, ty, textPaint); // 写字
textPaint.getTextBounds(onShowText, 0, onShowText.length(), textRect); // 获取 text 边界
notePaint.setStrokeWidth(3);
notePaint.setColor(Color.parseColor("#EF6C00"));
// 右边线
canvas.drawLine(tx, ty - textRect.height() - LINE_OFFSET, tx, ty + LINE_OFFSET, notePaint);
// 左边线
canvas.drawLine(tx + textRect.width(), ty - textRect.height() - LINE_OFFSET, tx + textRect.width(), ty + LINE_OFFSET, notePaint);
notePaint.setColor(Color.parseColor("#FF0277BD"));
// 上边线
canvas.drawLine(tx - LINE_OFFSET, ty - textRect.height(), tx + textRect.width() + LINE_OFFSET, ty - textRect.height(), notePaint);
notePaint.setColor(Color.parseColor("#00695C"));
// y - baseline
canvas.drawLine(tx - LINE_OFFSET, ty, tx + textRect.width() + LINE_OFFSET, ty, notePaint);
}
获取 text 尺寸信息
须要应用 Paint.FontMetrics
类。它有 5 个属性
属性 | 介绍 |
---|---|
top | 从 baseline 到最高的文字顶部的最大间隔 |
ascent | 单行距(singled spaced)时,baseline 上方空间的举荐间隔 |
descent | 单行距时,baseline 下方空间的举荐间隔 |
bottom | baseline 到最下方字符的最大间隔 |
leading | 多行文字之间举荐应用的额定空间 |
应用 Paint.getFontMetrics()
办法获取 Paint.FontMetrics
。
Paint.FontMetrics fm = textPaint.getFontMetrics();
留神,top
和 ascent
是负值。
绘制上图的办法
private void drawFontMetrics(Canvas canvas, float x, float y, String text) {canvas.drawText(text, x, y, textPaint);
textPaint.getTextBounds(text, 0, text.length(), textRect);
Paint.FontMetrics fm = textPaint.getFontMetrics();
notePaint.setStrokeWidth(1);
notePaint.setColor(Color.BLACK);
canvas.drawText(String.format(Locale.CHINA, "top:%.2f, bottom:%.2f", fm.top, fm.bottom), x, y + notePaint.getTextSize() * 2.5f, notePaint);
canvas.drawText(String.format(Locale.CHINA, "ascent:%.2f, descent:%.2f, leading:%.2f", fm.ascent, fm.descent, fm.leading), x, y + notePaint.getTextSize() * 4f, notePaint);
notePaint.setColor(Color.parseColor("#FFD84315"));
// fm top 线
canvas.drawLine(x - L_BIAS, y + fm.top, x + textRect.width() + L_BIAS, y + fm.top, notePaint);
notePaint.setColor(Color.parseColor("#FF00695C"));
// fm bottom 线
canvas.drawLine(x - L_BIAS, y + fm.bottom, x + textRect.width() + L_BIAS, y + fm.bottom, notePaint);
notePaint.setColor(Color.parseColor("#4527A0"));
// fm ascent 线
canvas.drawLine(x - L_BIAS, y + fm.ascent, x + textRect.width() + L_BIAS, y + fm.ascent, notePaint);
notePaint.setColor(Color.parseColor("#0E0822"));
// fm descent 线
canvas.drawLine(x - L_BIAS, y + fm.descent, x + textRect.width() + L_BIAS, y + fm.descent, notePaint);
}
对齐文字两头
有了下面的根底,咱们能够很快晓得如何实现文字“居中对齐”。将文字的程度中心线或竖直中心线对齐到某个点。
例如绘制一个立体坐标系
绘制出 2 个坐标轴,而后绘制上刻度,并写上数值。数值文字对应到刻度相应的地位上。咱们次要应用 Paint.getTextBounds
获取到文字的尺寸信息,再计算出适合的地位。
private void drawChart(Canvas canvas) {
final float x0 = 100;
final float y0 = 300; // 原点在画布上的坐标
final float halfLine = 5; // 刻度长度的一半
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(20);
notePaint.setStrokeWidth(2);
canvas.drawLine(x0, y0, x0 + 500, y0, notePaint); // 绘制横轴
canvas.drawLine(x0, y0, x0, y0 - 290, notePaint); // 绘制纵轴
// 绘制横轴刻度和数字
for (int i = 1; i <= 4; i++) {
int step = i * 100;
String n = String.valueOf(step);
float x = x0 + step;
canvas.drawLine(x, y0 - halfLine, x, y0 + halfLine, notePaint); // 刻度
// 文字对齐
textPaint.getTextBounds(n, 0, n.length(), textRect);
canvas.drawText(n, x - textRect.width() / 2f, y0 + textRect.height() + halfLine, textPaint);
}
// 绘制纵轴刻度和数字
for (int i = 1; i <= 2; i++) {
int step = i * 100;
String n = String.valueOf(step);
float y = y0 - step;
canvas.drawLine(x0 - halfLine, y, x0 + halfLine, y, notePaint); // 刻度
// 文字对齐
textPaint.getTextBounds(n, 0, n.length(), textRect);
canvas.drawText(n, x0 - halfLine * 2 - textRect.width(), y + textRect.height() / 2f, textPaint);
}
// 绘制原点 0
String origin = "0";
textPaint.getTextBounds(origin, 0, origin.length(), textRect);
canvas.drawText(origin, x0 - textRect.width(), y0 + textRect.height(), textPaint);
}
三,裁剪画布 canvas clip path
应用 canvas.clipPath(path)
办法。
clipPath
办法相当于按给定的门路裁剪画布。依据这个用处,咱们能够做出圆角图片的成果。
圆角图片示例
圆角图片是一种常见的设计。Android 官网库中并没有间接显示圆角图片的办法。如果是纯色,渐变色的背景,咱们能够用 shape 的 corners 来实现圆角。这里是利用 clipPath
在super.onDraw(canvas)
之前将画布裁出圆角。须要传入一个 Path 来指定裁剪范畴。
新建一个 RoundImageView
类,继承自 AppCompatImageView
。
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;
public class RoundImageView extends AppCompatImageView {
private float vw, vh;
private Path path = new Path();
private float topLeftR;
private float topRightR;
private float botLeftR;
private float botRightR;
public RoundImageView(Context context) {this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);
topLeftR = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_topLeftR, 0);
topRightR = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_topRightR, 0);
botLeftR = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_botLeftR, 0);
botRightR = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_botRightR, 0);
typedArray.recycle();}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);
vw = getWidth();
vh = getHeight();}
@Override
protected void onDraw(Canvas canvas) {path.reset();
if (vw > topLeftR && vh > topRightR) {path.moveTo(topLeftR, 0);
path.lineTo(vw - topRightR, 0);
path.quadTo(vw, 0, vw, topRightR);
path.lineTo(vw, vh - botRightR);
path.quadTo(vw, vh, vw - botRightR, vh);
path.lineTo(botLeftR, vh);
path.quadTo(0, vh, 0, vh - botLeftR);
path.lineTo(0, topLeftR);
path.quadTo(0, 0, topLeftR, 0);
path.close();
canvas.clipPath(path);
}
super.onDraw(canvas);
}
}
外面应用了 TypedArray
,须要新增 declare-styleable
资源。
<resources>
<declare-styleable name="RoundImageView">
<attr name="topLeftR" format="dimension" />
<attr name="topRightR" format="dimension" />
<attr name="botLeftR" format="dimension" />
<attr name="botRightR" format="dimension" />
</declare-styleable>
</resources>
应用这个类。在 layout 中间接应用。别离定义 4 个圆角半径参数。给 src
设置纯色。如果给定图片,图片内容接触到 view 端点时,才会看得出有圆角成果。
<com.rustfisher.tutorial2020.customview.view.RoundImageView
style="@style/RoundIvCube"
app:botLeftR="8dp"
app:botRightR="8dp"
app:topLeftR="8dp"
app:topRightR="8dp" />
<com.rustfisher.tutorial2020.customview.view.RoundImageView
style="@style/RoundIvCube"
app:botLeftR="25dp"
app:botRightR="25dp"
app:topLeftR="25dp"
app:topRightR="25dp" />
<com.rustfisher.tutorial2020.customview.view.RoundImageView
style="@style/RoundIvCube"
app:botLeftR="16dp" />
<com.rustfisher.tutorial2020.customview.view.RoundImageView
style="@style/RoundIvCube"
app:topLeftR="16dp" />
<com.rustfisher.tutorial2020.customview.view.RoundImageView
style="@style/RoundIvCube"
app:botRightR="16dp"
app:topLeftR="16dp" />
style 定义
<style name="RoundIvCube">
<item name="android:layout_width">50dp</item>
<item name="android:layout_height">50dp</item>
<item name="android:src">#303F9F</item>
<item name="android:layout_margin">8dp</item>
</style>
运行后果
能够看到各个图形的圆角。然而咱们也能发现有锯齿。这个裁剪办法没能做到润滑圆润的圆角。
Android 零根底入门教程视频参考