更新时间:2022-01-23
1. 先来一个简单的演示效果
1)MyDrawTextView.java
自定义的View,代码如下
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
public class MyDrawTextView extends AppCompatTextView {
public MyDrawTextView(@NonNull Context context) {
super(context);
}
public MyDrawTextView(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyDrawTextView(@NonNull Context context,
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
}
}
2)MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
3)activity_main.xml
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.mydrawtexttest.MyDrawTextView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
2. 展开具体来说说
1)Canvas.drawText 的方法参数
Android Developers 中描述的 Canvas.drawText方法,参考文档:地址
public void drawText(@NonNull String text, float x, float y,
@NonNull Paint paint) {
super.drawText(text, x, y, paint);
}
Canvas.drawText参数 | 描述 |
---|---|
text | 需要绘制的文本内容 |
x | 正在绘制的文本原点的 x 坐标 |
y | 正在绘制的文本基线baseLine的 y 坐标 |
paint | Paint |
2)Paint的方法
Android Developers 中描述的 Paint的方法,参考文档:地址
Paint的方法 | 描述 |
---|---|
paint.setStyle | 填充方式,默认FILL填充,还有STROKE描边,FILL_AND_STROKE填充并描边 |
paint.setTextSize | 文本大小,以像素为单位 |
paint.setTextAlign | 设置对齐方式 |
paint.getFontSpacing | 获取行间距 |
3)演示1 左对齐
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
}
4)演示2 左对齐并向右偏移
关于中间的分割线,可以参考这个博文:DrawLine
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing();
canvas.drawText(text, x, baseLine, paint);
}
5)演示3 居中对齐
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing();
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.CENTER); // 居中对齐,默认值LEFT
paint.setColor(Color.GREEN);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing() * 2;
canvas.drawText(text, x, baseLine, paint);
}
6)演示4 右对齐
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing();
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.CENTER); // 居中对齐,默认值LEFT
paint.setColor(Color.GREEN);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing() * 2;
canvas.drawText(text, x, baseLine, paint);
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.RIGHT); // 右对齐,默认值LEFT
paint.setColor(Color.GRAY);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2;
baseLine = 100 + paint.getFontSpacing() * 3;
canvas.drawText(text, x, baseLine, paint);
}
7)演示5 上下左右居中对齐
a)错误演示效果:
这里并没有上下左右居中对齐,有微差的。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.CENTER); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = getWidth() / 2;
float baseLine = 50 + getHeight() / 2;
canvas.drawText(text, x, baseLine, paint);
}
b)Paint的方法
Paint的方法 | 描述 |
---|---|
paint.measureText | 测量文本的宽度 |
Paint.FontMetrics | 描述 |
---|---|
ascent | 单行距文本基线上方的推荐距离。注意:是 负 的值 |
descent | 单行距文本基线下方的推荐距离。注意:是正的值 |
leading | 建议在文本行之间添加额外空间。 |
bottom | 给定文本大小下字体中最低字形基线下方的最大距离。 |
top | 给定文本大小的字体中最高字形基线上方的最大距离。 |
c)正确的演示效果:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
// 当默认左对齐的情况下
// 仅仅getWidth() / 2 ,文字会出现在中间的右方,如演示2的效果
// 当 减去文本宽度的一半时,即向左平移了文本的一半,正好左右居中了
float x = getWidth() / 2 - paint.measureText(text) / 2;
// 再来解决上下居中
// getHeight() / 2 这样设置的话,baseLine就是正好中间上方的位置,需要再向下平移一部分
// getFontMetrics() 可以获取到 accent、descent,如截图中的标识部分
// descent为正的,ascent为负的,他们相加 再 除以2,正好是需要向下偏移的距离
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float baseLine = getHeight() / 2 - (fontMetrics.descent + fontMetrics.ascent)/2;
canvas.drawText(text, x, baseLine, paint);
}
3. 图层 保存和恢复
canvas.save() 可以进行保存当前canvas的状态, 保存后再进行平移等操作。
canvas.restore() 恢复到上次保存时的状态,相当于回滚了,save和restore之间的操作给扔弃了。
canvas.scale() 在X轴/Y轴上进行缩放
public void scale(float sx, float sy) {
if (sx == 1.0f && sy == 1.0f) return;
nScale(mNativeCanvasWrapper, sx, sy);
}
canvas.scale参数 | 描述 |
---|---|
x | 在 X 轴方向上进行缩放 |
y | 在 Y 轴方向上进行缩放 |
1)演示:不进行缩放的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.save();
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
// canvas.scale(0.5f, 0.5f);
// canvas.restore();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2 - paint.measureText(text) / 2;
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
baseLine = getHeight() / 2 - (fontMetrics.descent + fontMetrics.ascent)/2;
canvas.drawText(text, x, baseLine, paint);
}
2)演示:进行缩放的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.save();
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
canvas.scale(0.5f, 0.5f);
// canvas.restore();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2 - paint.measureText(text) / 2;
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
baseLine = getHeight() / 2 - (fontMetrics.descent + fontMetrics.ascent)/2;
canvas.drawText(text, x, baseLine, paint);
}
3)演示:进行缩放 并 save及restore 的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 先进行保存
canvas.save();
String text = "绘制文本";
Paint paint = new Paint();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.BLUE);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
float x = 0;
float baseLine = 100;
canvas.drawText(text, x, baseLine, paint);
// 缩小
canvas.scale(0.5f, 0.5f);
// 恢复
canvas.restore();
paint.setTextSize(100); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(Color.RED);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
x = getWidth() / 2 - paint.measureText(text) / 2;
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
baseLine = getHeight() / 2 - (fontMetrics.descent + fontMetrics.ascent)/2;
canvas.drawText(text, x, baseLine, paint);
}
4. 文字变色效果
1) MyDrawTextView2.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
public class MyDrawTextView2 extends AppCompatTextView {
private float progess = 0.01f;
public MyDrawTextView2(@NonNull Context context) {
super(context);
}
public MyDrawTextView2(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyDrawTextView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
myDraw(canvas, 0, (int) (getWidth() * progess), Color.BLUE);
myDraw(canvas, (int) (getWidth() * progess), getWidth(), Color.RED);
}
private void myDraw(Canvas canvas, int left, int right, int colorValue) {
canvas.save();
Rect rect = new Rect(left, 0, right, getHeight());
canvas.clipRect(rect);
Paint paint = new Paint();
paint.setTextSize(getTextSize()); // 文字大小
paint.setTextAlign(Paint.Align.LEFT); // 默认值LEFT
paint.setColor(colorValue);
paint.setAntiAlias(true); // 防锯齿
paint.setDither(true); // 防抖动
String text = getText().toString();
if (TextUtils.isEmpty(text)) return;
float dx = 0;
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
float baseLine = getHeight() / 2 + dy;
canvas.drawText(text, dx, baseLine, paint);
canvas.restore();
}
public void setProgress(float progress) {
this.progess = progress;
// 重新绘制
invalidate();
}
}
2) activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.mydrawtexttest.MyDrawTextView2
android:layout_alignParentBottom="true"
android:id="@+id/my_draw_text_view2_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
android:textSize="30sp"
/>
<Button
android:id="@+id/change_id"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="change"
/>
</RelativeLayout>
3) MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
public class MainActivity extends AppCompatActivity {
private MyDrawTextView2 myDrawTextView2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
myDrawTextView2 = findViewById(R.id.my_draw_text_view2_id);
findViewById(R.id.change_id).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setAnimation();
}
});
}
public void setAnimation() {
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 1);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float animatedValue = (Float) animation.getAnimatedValue();
myDrawTextView2.setProgress(animatedValue);
}
});
valueAnimator.start();
}
}
4) 效果
示例源码地址:
参考文档:
DrawLine博客文档:地址
Canvas.clipRect裁剪矩形 博客文档:地址
Android Developers 中描述的 Canvas.drawText方法,参考文档:地址
Android Developers 中描述的 Paint的方法,参考文档:地址
Android Developers 中描述的 Canvas.scale方法,参考文档:地址
日进一步!