前言
本章要实现的效果
自定义TextView文字绘制
我们在进行文字的自定义绘制的时候,通过不会通过 setText 的方式,而是重写 onDraw 方法,通过 Canvas 的 drawText 方法来实现,整体实现方式如下:
less
public class CustomTextView extends AppCompatTextView {
public CustomTextView(@NonNull Context context) {
super(context);
}
public CustomTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
canvas.drawText("我是老A", 0, 0, paint);
}
}
我们运行看下效果:
运行,并没有在屏幕上看到『我是老A』这几个字,这是为什么呢?我们来一探究竟
我们来看一下 drawText 的参数注释:
float y,是 text 的 baseline,这个 baseline 是什么鬼呢?
baseline
上图中的的那条红线,就是 baseline;大部分文字都以这条线为基准线,保证文字的对齐;而文字绘制的起始点,就是第一个文字的左下角,也就是下图剪头指的位置
y值 如果设置成0,文字其实就跑到屏幕外面了,所以我们的 y 值不能为0,我们修改下 y 值,然后看下效果:
可以看到 『我是老A』文字绘制了出来;因为没有设置文字的大小,所以看着会比较小;
也就是说:无论什么文字,Android 系统都会让其在某一条线上对齐,而这条对齐的线,就是 baseline,也叫做基准线;
有人可能会说了,为什么不把『我是老A』绘制到屏幕X轴中间位置,这样方便看,好的,那我们把这几个字绘制到屏幕的中间位置,为了便于看到中间位置,我们现在屏幕中心画一条竖线
scss
private void drawCenterLineX(Canvas canvas) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
canvas.drawLine(getWidth() / 2,0, getWidth()/2, getHeight(), paint);
}
我们在屏幕 X 轴上画了一条竖线;然后我们把 文字绘制到 X 轴中心位置
less
canvas.drawText("我是老A", getWidth() / 2, 200, paint);
运行看下效果:
咦咦咦,好像和我们期望的不一样,整体文字偏右了。。。那么怎么解决呢?我们需要设置文字的对齐方式
less
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("我是老A", getWidth() / 2, 200, paint);
我们在编译运行看下效果:
这次文字不是从中心线的左侧开始绘制了;也就是说,如果我们不设置 TextAlign,那么默认的 TextAlign 是从左边开始;
我们现在是 X 轴的中心位置,那么如何绘制到 Y 轴的中心位置呢?我们先来画一条 Y 轴中心线
scss
private void drawCenterLineY(Canvas canvas) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
canvas.drawLine(0,getHeight() / 2, getWidth(), getHeight() / 2, paint);
}
那么,我们如何把文字绘制到中心位置呢?可能大家会说 Y 值直接除以 2,好,我们试一下
less
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("我是老A", getWidth() / 2, getHeight() / 2, paint);
运行看下效果:
看起来,还是没有达到我们期望效果,希望文字在Y轴方向上平分,那么我们需要知道文字的高度才能进行向下绘制;那么文字的高度如何计算呢?
文字高度计算
我们需要使用 FontMetrics 来进行文字的高度测量,我们点进去看下这个类的实现:
这几个值,正好对应的就是下图的这几个值
那么文字的高度具体怎么计算呢?本质就是 ascent + descent 的值,但是根据 Android 坐标系来看 X 轴是右正左负,Y 轴是下正上负,也就是说根据基准线,我们的 ascent 值是 负值,descent 是正值,所以文字的高度最终的计算方式应是 descent - ascent 的值
所以 Y 值应该是 getHeight()/2 + ((fontMetircs.descent - fontMetircs.descent) / 2)
less
canvas.drawText("我是老A", getWidth() / 2,
getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent)/2),
paint);
我们运行看下效果:
咦,还是不对呀,文字又偏下了,这是为什么呢?
文字绘制的(0, 0)点,是基于基准线来绘制的,也就是文字的左下角,如果我们用文字高度的一半的话,Y 轴的值就是偏大的,因为基准线的位置没有在文字高度的一半,所以需要减去一部分高度,这个减去的高度就是 descent;
less
canvas.drawText("我是老A", getWidth() / 2,
getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent)/2) - fontMetrics.descent,
paint);
运行看下效果:
可以看到,这次我们的文字 绘制到了中心位置!
接下来,可能有人会问了,不用 TextAlign 可以将文字绘制到 X 轴中心位置吗?答案是可以的,我们只需要计算出文字的宽度,然后用屏幕宽度的一半减去文字宽度的一半,就可以算出起始位置;那么文字的宽度应该如何计算呢?
文字宽度的计算
使用 Paint 的 measureText 方法来获取文字的宽度,我们将 TextAlign 注释掉
scss
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
// paint.setTextAlign(Paint.Align.CENTER);
float width = paint.measureText("我是老A");
canvas.drawText("我是老A", getWidth() / 2 - width / 2 ,
getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent)/2) - fontMetrics.descent,
paint);
运行看下效果:
依然是中心位置;
自定义TextView颜色渐变
我们的文字绘制的时候,是不可能只绘制一半的,那么我们就需要对画布进行裁剪,而 canvas 也给我们提供了一系列的 API 来进行画布的裁切;
也就是说,我们对画布进行裁剪,裁剪之后,能绘制多少就会绘制多少,看个例子:
scss
private void drawText(Canvas canvas) {
canvas.save();
paint.setTextSize(40);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float width = paint.measureText("我是老A");
float baseLine = getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent) / 2) - fontMetrics.descent;
float x = getWidth() / 2 - width / 2;
Rect rect = new Rect((int) x, 0, 330, getHeight());
canvas.clipRect(rect);
canvas.drawText("我是老A", x,
baseLine,
paint);
canvas.restore();
}
运行看下效果:
裁剪的画布只能够把『我』字绘制出来;
如果我们想实现文字的渐变,那么我们就可以用两层画布,一层是黑色,一层是红色,然后我们计算每一个字在整个文字占的百分比,从而设置 Rect 的右边界的值;
scss
private void drawTextBlack(Canvas canvas) {
paint.setTextSize(40);
paint.setColor(Color.BLACK);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float width = paint.measureText("我是老A");
float baseLine = getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent) / 2) - fontMetrics.descent;
float x = getWidth() / 2 - width / 2;
canvas.drawText("我是老A", x,
baseLine,
paint);
}
private void drawTextRed(Canvas canvas) {
canvas.save();
paint.setTextSize(40);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float width = paint.measureText("我是老A");
float baseLine = getHeight() / 2 + ((fontMetrics.descent - fontMetrics.ascent) / 2) - fontMetrics.descent;
float x = getWidth() / 2 - width / 2;
float right = x + width*mPercent;
Rect rect = new Rect((int) x, 0, (int) right, getHeight());
canvas.clipRect(rect);
canvas.drawText("我是老A", x,
baseLine,
paint);
canvas.restore();
}
我们需要动态的改变 mPercent 的值,可以通过属性动画,也可以通过 ViewPager 的手势滑动,我们先用属性动画来看下效果;
scss
ObjectAnimator.ofFloat(view, "percent", 0f, 1f).setDuration(5000).start()
我们运行看下效果:
我们实现了文字的渐变效果;
ViewPager 的滑动监听,可以通过 onPageScrolled 回调设置
java
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (positionOffset > 0) {
CustomTextView left = mTabs.get(position);
CustomTextView right = mTabs.get(position + 1); left.setDirection(ColorChangeTextView1.DIRECTION_RIGHT);
right.setDirection(ColorChangeTextView1.DIRECTION_LEFT);
Log.v(TAG, positionOffset + "");
left.setProgress(1 - positionOffset);
right.setProgress(positionOffset);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
运行,实现我们开头的效果;
简历润色
简历上可写:深度理解文字绘制原理,可实现炫酷的文字绘制效果;
下一章预告
玩转RV,使用RecyclerView实现吸顶效果;
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~