一,自定义 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)绘制画布背景

/** * 绘制画布背景 *///绘制画布背景,argbcanvas.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下方空间的举荐间隔
bottombaseline到最下方字符的最大间隔
leading多行文字之间举荐应用的额定空间

应用 Paint.getFontMetrics() 办法获取 Paint.FontMetrics

Paint.FontMetrics fm = textPaint.getFontMetrics();

留神,topascent 是负值。

绘制上图的办法

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 来实现圆角。 这里是利用 clipPathsuper.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零根底入门教程视频参考