关于android:Android入门教程-自定义View绘制文字裁剪画布

41次阅读

共计 9819 个字符,预计需要花费 25 分钟才能阅读完成。

一,自定义 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();

留神,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 零根底入门教程视频参考

正文完
 0