Android字体渐变效果

news/2024/3/29 22:57:24/文章来源:https://blog.csdn.net/qq_25749749/article/details/124537646

先来看看最初版代码:

public class GradualChangeTv extends AppCompatTextView {
public Paint mPaint = new Paint();

public final String text = "android 超级兵";public GradualChangeTv(Context context) {this(context, null);
}public GradualChangeTv(Context context, AttributeSet attrs) {this(context, attrs, 0);
}public GradualChangeTv(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mPaint.setColor(Color.RED);//抗锯齿mPaint.setAntiAlias(true);
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, 0, 0, mPaint);
}

}

就是简单的绘制了一行字。

疑问

为什么这里要继承自AppCompatTextView而不是View?

答:偷个懒而已,因为不用在我来测量View,直接用父类的就行

来看看效果顺便也看看布局:

图片

出现问题

文字并没有显示。

答:因为文字坐标系和屏幕坐标系不一样,文字坐标系是从BaseLine线开始计算的。

先来回顾一下屏幕的坐标系:

图片

再来看看文字的坐标系。

图片
(图片来自于网络)

再来思考一下文字是为什么不显示的:

图片
虚线为BaseLine

如果此时我把字体放大到100,看一看我能不能看到文字。

图片

再一次证明了文字是从BaseLine线开始绘制。

文字居中

可以用两条辅助线,水平线与垂直线。然后在来看文字是否居中。

代码

⚠️ 底部会给出完整代码。这里看思路即可,不用复制代码。

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//获取当前控件的宽高
int viewWidth = getWidth() / 2;
int viewHeight = getHeight() / 2;
/*
* 绘制文字
* 参数一: 绘制文字
* 参数二: x轴开始位置
* 参数三: y 轴开始位置
* 参数四: 画笔
*/
canvas.drawText(text, viewWidth, viewHeight, mPaint);

    //绘制居中线drawCenterLine(canvas, viewWidth, viewHeight);
}private void drawCenterLine(Canvas canvas, int viewWidth, int viewHeight) {//垂直线canvas.drawLine(viewWidth, 0, viewWidth, getHeight(), mPaint);//水平线canvas.drawLine(0,viewHeight,getWidth(),viewHeight,mPaint);
}

效果图

图片

可以看出,还是上面说的那个问题,文字绘制是基于baseLine线来绘制的。

文字居中思路:

通过mPaint.measureText(text) 获取文字宽
通过mPaint.descent() + mPaint.ascent(); 获取文字高
然后控件各取一半,让控件减去即可

这里的descent和ascent可以参考上面文字绘制图。

相关代码

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//文字宽度
float textWidth = mPaint.measureText(text);
//文字高度
float textHeight = mPaint.descent() + mPaint.ascent();

    //获取当前控件的宽高int viewWidth = getWidth() / 2;int viewHeight = getHeight() / 2;canvas.drawText(text, viewWidth - textWidth / 2, viewHeight - textHeight / 2, mPaint);//绘制居中线drawCenterLine(canvas, viewWidth, viewHeight);
}

效果图

图片

裁剪

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//文字宽度
float textWidth = mPaint.measureText(text);
//文字高度
float textHeight = mPaint.descent() + mPaint.ascent();

    //获取当前控件的宽高的一半int viewWidth = getWidth() / 2;int viewHeight = getHeight() / 2;//裁剪drawClip(canvas, viewWidth, viewHeight, textWidth, textHeight);//绘制居中线drawCenterLine(canvas, viewWidth, viewHeight);
}private void drawClip(Canvas canvas, int viewWidth, int viewHeight, float textWidth, float textHeight) {mPaint.setColor(Color.BLACK);canvas.save();//绘制文字X轴的位置float left = viewWidth - textWidth / 2;//绘制文字Y轴的位置float right = viewHeight - textHeight / 2;//裁剪canvas.clipRect((int) left, 0, (int) left + 300, getHeight());/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, left, right, mPaint);canvas.restore();
}

裁剪(clipRect)参数分析:

参数一: 从文字开始位置绘制
参数二: 顶部裁剪为0
参数三: 裁剪宽度
参数四: 绘制高度

canvas的save()和restore()方法可以理解为将当前绘制的东西当作一个新的图层!

来看看效果图:

图片

代码注释很清晰,就不过多解释了。

从左到右渐变文字

众所周知,在android中是不能够将文字绘制一般的。

思路分析:

绘制两层(两层颜色不同),两层叠加起来
然后通过裁剪将上面一层给裁剪掉

图片

在来看看现在代码是什么样子的:

//用来记录当前进度 【0-1】
float progress = 0.3f;

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//文字宽度
float textWidth = mPaint.measureText(text);
//文字高度
float textHeight = mPaint.descent() + mPaint.ascent();

    //获取当前控件的宽高的一半int viewWidth = getWidth() / 2;int viewHeight = getHeight() / 2;//绘制底层drawBottom(canvas, viewWidth, viewHeight, textWidth, textHeight);//绘制上层【颜色渐变的】drawUp(canvas, viewWidth, viewHeight, textWidth, textHeight);//绘制居中线drawCenterLine(canvas, viewWidth, viewHeight);
}//绘制上层【渐变的】
private void drawUp(Canvas canvas, int viewWidth, int viewHeight, float textWidth, float textHeight) {mPaint.setColor(Color.BLACK);canvas.save();//绘制文字X轴的位置float left = viewWidth - textWidth / 2;//绘制文字Y轴的位置float right = viewHeight - textHeight / 2;//裁剪canvas.clipRect((int) left, 0, (int) left + textWidth * progress, getHeight());/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, left, right, mPaint);canvas.restore();
}//绘制下层 不动的
private void drawBottom(Canvas canvas, int viewWidth, int viewHeight, float textWidth, float textHeight) {mPaint.setColor(Color.RED);  //文字颜色canvas.save();//文字开始位置float left = viewWidth - textWidth / 2;/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, left, viewHeight - textHeight / 2, mPaint);canvas.restore();
}

这里重点解释一下上层[需要裁剪的]参数:

//裁剪
canvas.clipRect((int) left, 0, (int) left + textWidth * progress, getHeight());

textWidth需要绘制文字的宽度
viewWidth控件宽度的一半
文字开始的位置:left = viewWidth - textWidth / 2
文字需要裁剪的位置:文字的宽度 * progress

图片

通过手势滑动来控制。这段代码并没有实质性作用,只是来看看效果。

@SuppressLint(“ClickableViewAccessibility”)
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
progress = event.getX() / getWidth();
invalidate();
}
return true;
}

效果图

图片

从右到左渐变文字

思路和从左到右绘制是一样的直接看关键代码:

private void drawRightToLeft(Canvas canvas, int viewWidth, int viewHeight, float textWidth, float textHeight) {
mPaint.setColor(Color.GREEN);
/*
* 这里 left和right能够在此抽取出来,不过这样写很易懂,有需求自己弄吧!!!
*/
canvas.save();
//绘制文字X轴的位置 【文字开始的位置】
float textX = viewWidth - textWidth / 2;

    //绘制文字Y轴的位置float textY = viewHeight - textHeight / 2;//文字结束的位置float end = viewWidth + mPaint.measureText(text) / 2;canvas.clipRect(end, 0, textX + textWidth * (1 - progress), getHeight());canvas.drawText(text, textX, textY, mPaint);canvas.restore();
}

@SuppressLint(“ClickableViewAccessibility”)
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (type == GradualChangeTextView.GRADUAL_CHANGE_RIGHT) {
//从右到左滑动
progress = 1 - event.getX() / getWidth();
} else if (type == GradualChangeTextView.GRADUAL_CHANGE_LEFT) {
//从左到右滑动
progress = event.getX() / getWidth();
}
invalidate();
}
return true;
}

效果图

图片

最后在添加两个按钮来完全测试一下代码有没有问题。

图片

完完全全没有问题!

最终实现效果(渐变滑动)

先来看看布局:

图片

布局简单的很,就是文字和ViewPager。大致看看ViewPager代码:

//text1 … text4 是控件id
val textList = listOf(text1, text2, text3, text4)

    val list = listOf(HomeFragment(), MyFragment(), TestFragment(), SettingFragment())val viewPagerAdapter = ViewPagerAdapter(supportFragmentManager, list)viewPager.adapter = viewPagerAdapter//默认选择第一页viewPager.currentItem = 1//默认选中textList[viewPager.currentItem].percent = 1f

这段代码只要学过就懂,不细说了。重中之重来了:

viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int,
) {
if (positionOffset > 0) {
val left = textList[position]
val right = textList[position + 1]
//从右到左滑动
left.setSlidingPosition(GradualChangeTextView.GRADUAL_CHANGE_RIGHT)
//从左到右滑动
right.setSlidingPosition(GradualChangeTextView.GRADUAL_CHANGE_LEFT)

                //当前页面取反[从右到左]left.percent = 1 - positionOffset//下一个页面正常[从左到右]right.percent = positionOffset}}override fun onPageSelected(position: Int) { }override fun onPageScrollStateChanged(state: Int) {//当 ViewPage结束的时候,重新设置一下状态 [不设置的话滑动太快,会导致'残影']textList.forEach {if (it.tag == textList[viewPager.currentItem].tag) {it.percent = 1f} else {it.percent = 0f}}}})

来看看效果

图片

过度绘制极限优化

什么是过度绘制,参考文档:
https://www.jianshu.com/p/2cc6d5842986

重点总结

原色 – 没有被过度绘制 – 这部分的像素点只在屏幕上绘制了一次。
蓝色 – 1次过度绘制– 这部分的像素点只在屏幕上绘制了两次。
绿色 – 2次过度绘制 – 这部分的像素点只在屏幕上绘制了三次。
粉色 – 3次过度绘制 – 这部分的像素点只在屏幕上绘制了四次。
红色 – 4次过度绘制 – 这部分的像素点只在屏幕上绘制了五次。

先来看看没有优化的效果:

图片

可以看到,在绘制的过程中,因为是两层,那么就绘制了2次。

优化思路

当黑色[上层]从左到右滑动的时候,红色[下层]跟随着从左到右裁剪。来看看下层绘制的代码:

//绘制下层 不动的
private void drawBottom(Canvas canvas, int viewWidth, int viewHeight, float textWidth, float textHeight) {
mPaint.setColor(Color.RED);
canvas.save();
//绘制文字X轴的位置 [文字开始的位置]
float textX = viewWidth - textWidth / 2;

    //绘制文字Y轴的位置float textY = viewHeight - textHeight / 2;//跟随者上层裁剪canvas.clipRect((int) textX + textWidth * progress, 0, textWidth + viewWidth, getHeight());/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, textX, textY, mPaint);canvas.restore();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_236353.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32F4 | 窗口门狗(WWDG)实验

文章目录一、STM32F4 窗口看门狗简介二、硬件设计三、软件设计四、实验现象五、STM32CubeMX 配置 WWDG在本章中,我们将使用窗口看门狗的 中断功能来喂狗,通过 DS0 和 DS1 提示程序的运行状态。一、STM32F4 窗口看门狗简介 窗口看门狗(WWDG&…

如何将PDF转换为PPT?2个免费好用的pdf转ppt工具

这个教程教你如何将PDF文件变成PowerPoint演示文稿文件。有几个不同的免费在线网站,您可以使用它们将PDF转换为PowerPoint演示文稿。请记住,扫描的 PDF(如扫描到计算机中的文档)无法转换为可编辑的 PowerPoint 演示文稿。 方法1、…

数据处理指令(二)—— 数据运算指令(加减乘)+ 逻辑运算指令(与或)+ 位清零(BIC)

无论是数据运算还是逻辑运算&#xff0c;需要满足如下格式&#xff1a; <操作码> <目标寄存器> <第一操作寄存器> <第二操作数> 操作码&#xff1a; 表示执行哪种操作&#xff08;加减乘&#xff09;目标寄存器&#xff1a; 用…

DVWA靶场安装

DVWA靶场安装1.环境准备2.安装步骤2.1.phpstudy安装2.2.DVWA安装2.2.1.下载DVWA2.2.2.解压DVWA2.2.3.放入服务器2.2.4.删除配置文件后缀2.2.5.修改配置文件2.2.6.访问DVWA2.2.7.报错修改2.2.8.修改php.ini2.2.9.重新访问DVWA2.2.10.登录DVWA3.修改过关等级1.环境准备 服务器环境…

SpringMVC:SpringMVC之JSON数据传输参数(5)

JSON数据传输参数1 JSON数据传输参数2 JSON普通数组3 JSON对象数据4 JSON对象数组5 小结1 JSON数据传输参数 现在比较流行的开发方式为异步调用。前后台以异步方式进行交换&#xff0c;传输的数据使用的是JSON,所以前端如果发送的是JSON数据&#xff0c;后端该如何接收? 对于…

【LeetCode每日一题:1785. 构成特定和需要添加的最少元素~~~数组公式推导+防止整型溢出+向上取整+贪心】

题目描述 给你一个整数数组 nums &#xff0c;和两个整数 limit 与 goal 。数组 nums 有一条重要属性&#xff1a;abs(nums[i]) < limit 。 返回使数组元素总和等于 goal 所需要向数组中添加的 最少元素数量 &#xff0c;添加元素 不应改变 数组中 abs(nums[i]) < limi…

小程序框架与生命周期

目录 框架 响应的数据绑定 页面管理 基础组件 丰富的 API 逻辑层 App Service 小程序的生命周期 注册页面 使用 Page 构造器注册页面 在页面中使用 behaviors 使用 Component 构造器构造页面 页面的生命周期 页面路由 页面栈 路由方式 注意事项 模块化 模块化…

用 Footprint Analytics 的数据 API 快速建立你的项目

Footprint 有一个独特的、非常灵活的API&#xff0c;允许你为数据分析建立成熟的数据管道&#xff0c;以及机器学习应用。这是通过提供两种类型的接口来实现的&#xff1a;第一种是用于将数据上传到平台&#xff08;Upload API&#xff09;&#xff1b;第二种是用于从平台获取数…

BI@report钻取操作

1.创建的表为浮动表 注意设置成浮动维单元格 把东西都选在红色框框里面 2.在对需要进行钻取的表元设置 设置参数 注意 参数是传递用的&#xff0c;值取的是当前表的值&#xff0c;这样参数可以带着这个表的值往下传 使用的是SQL数据源 在最后加上这行代码 having rso1.par…

3. 实例化Bean的三种方式

实例化Bean的三种方式 一、构造方法方式 1.1 BookDaoImpl package com.lin.dao.daoimpl;import com.lin.dao.BookDao;public class BookDaoImpl implements BookDao {public BookDaoImpl() {System.out.println("BookDao的无参构造器");}/*** 数据层实现*/public …

【大数据技术Hadoop+Spark】MapReduce之单词计数和倒排索引实战(附源码和数据集 超详细)

源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、统计单词出现次数 单词计数是最简单也是最能体现MapReduce思想的程序之一&#xff0c;可以称为MapReduce版“Hello World。其主要功能是统计一系列文本文件中每个单词出现的次数 程序解析 首先MapReduce将文件拆分成spli…

CRM客户管理系统源码带手机端+Uniapp小程序源码+调试部署视频

一套Java大型CRM客户关系管理源码带手机端和小程序源码&#xff08;带调试部署视频&#xff09; 了解CRM源码更多信息可私信我。 相关技术&#xff1a; 1. 前端&#xff1a;Vue 2. 后端&#xff1a;Spring boot 3. 数据库&#xff1a;MySQL 4.小程序端&#xff1a;UNIAPP …

【JAVA程序设计】基于JAVA Swing的飞机票订票系统-有报告

基于JAVA Swing的飞机票订票系统零、项目获取一、项目简介二、开发环境三、项目结构四、运行截图零、项目获取 获取方式&#xff08;点击下载&#xff09;&#xff1a;是云猿实战 项目经过多人测试运行&#xff0c;可以确保100%成功运行。 一、项目简介 本项目是基于JAVA Sw…

MindFusion.Diagramming for JavaScript V4.2.4

MindFusion.Diagramming for JavaScript V4.2.4 现在支持使用套索工具进行缩放的多种方式。2022 年 12 月 15 日 - 15:44新版本特征 套索缩放工具- 该控件现在支持使用套索工具进行缩放的多种方式。新的“PanAndModify”行为允许您在鼠标指针悬停在某个项目上或平移视图时进行选…

Ac-GA-K(Ac)-AMC,577969-56-3

Ac- gak (Ac)-AMC&#xff0c;在蛋白酶偶联试验中测量组蛋白去乙酰化酶I类(HDAC 1、2、3和8)和II类(HDAC 6和10)活性的荧光底物。hdac催化Lys脱乙酰生成Ac-GAK-AM。 Ac-GAK(Ac)-AMC, fluorogenic substrate for measuring histone deacetylase class I (HDAC 1, 2, 3, and 8) a…

32-Vue之ECharts-雷达图

ECharts-雷达图前言雷达图特点雷达图的基本实现雷达图的常见效果显示数值区域面积绘制类型完整代码前言 本篇来学习写雷达图 雷达图特点 可以用来分析多个维度的数据与标准数据的对比情况 雷达图的基本实现 ECharts 最基本的代码结构定义各个维度的最大值准备具体产品的数…

深入Java线程池:从设计思想到源码解读

1. 前言 线程池深入详解 2. 初识线程池 我们知道&#xff0c;线程的创建和销毁都需要映射到操作系统&#xff0c;因此其代价是比较高昂的。出于避免频繁创建、销毁线程以及方便线程管理的需要&#xff0c;线程池应运而生。 2.1. 线程池优势 降低资源消耗&#xff1a;线程池…

前端基础_传统Web页面

传统Web页面 传统Web页面就是打开浏览器&#xff0c;整个页面都会打开的应用。例如&#xff0c;笔者的个人网站http://siwei.me就是一个典型的“传统Web应用”&#xff0c;每次单击其中任意一个链接&#xff0c;都会引起页面的整个刷新 传统的页面每次打开&#xff0c;都要把…

谷歌浏览器是最容易受到攻击的

©网络研究院 不幸的是&#xff0c;那些看起来越多&#xff0c;他们发现的越多&#xff0c;这个规则总是适用的&#xff0c;除了来自体育场的无节制的欢呼声。 Windows 比 Linux 更容易受到攻击&#xff0c;因为 Chrome 比 Edge 更容易受到攻击。这也是因为它们是最受欢迎…

详解即时通讯音视频开发实时语音通讯丢包补偿技术

即时通讯应用中的实时音视频技术&#xff0c;几乎是IM开发中的最后一道高墙。原因在于&#xff1a;实时音视频技术 音视频处理技术 网络传输技术 的横向技术应用集合体&#xff0c;而公共互联网不是为了实时通信设计的。 现如今&#xff0c;随着移动互联网越来越普及&#xf…