/   今日科技快讯   /

近日,携程发布《秋游及中秋预测数据报告》。报告显示,中秋前夕跨省游有序复苏,旅游报名人次平稳上升,9月6日预订跨省旅游订单量比较8月20日增长356%。北京、北京环球度假区位列中秋最热旅游目的地。

/   作者简介   /

本篇文章来自android超级兵的投稿,文章主要分享了他对Android中字体渐变效果的实现过程,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

android超级兵的博客地址:

https://blog.csdn.net/weixin_44819566

/   正文   /

先来看看完成的效果:

简单解释,在滑动的过程中,渐变文字会随着ViewPager的滑动而变化!!

绘制文字与BaseLine思考

先来看看最初版代码:

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);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);/** 绘制文字* 参数一: 绘制文字* 参数二: x轴开始位置* 参数三: y 轴开始位置* 参数四: 画笔*/canvas.drawText(text, 0, 0, mPaint);}
}

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

疑问

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

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

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

出现问题

文字并没有显示。

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

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

再来看看文字的坐标系。

(图片来自于网络)

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

虚线为BaseLine

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

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

文字居中

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

代码

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

@Overrideprotected 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可以参考上面文字绘制图。

相关代码

 @Overrideprotected 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);}

效果图

裁剪

 @Overrideprotected 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;@Overrideprotected 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")@Overridepublic 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")@Overridepublic 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 是控件idval 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. 蓝色 – 1次过度绘制– 这部分的像素点只在屏幕上绘制了两次。

  3. 绿色 – 2次过度绘制 – 这部分的像素点只在屏幕上绘制了三次。

  4. 粉色 – 3次过度绘制 – 这部分的像素点只在屏幕上绘制了四次。

  5. 红色 – 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();}

效果图

完整代码地址如下所示:

https://gitee.com/lanyangyangzzz/android_ui

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

巧用Compose来实现手势拖拽效果

PermissionX 1.5发布,支持申请Android特殊权限啦

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

Android字体渐变效果实战,就是一个字,炫!相关推荐

  1. Android字体渐变效果

    先来看看最初版代码: public class GradualChangeTv extends AppCompatTextView { public Paint mPaint = new Paint( ...

  2. 《Android Studio开发实战》学习(二)- 聊天室

    <Android Studio开发实战>学习(二)- 聊天室 背景 聊天室布局文件的编写 聊天室代码文件的编写 运行结果 背景 在前一篇文章 1中实现了使用Android Studio开发 ...

  3. R语言ggplot2可视化柱状图添加数值说明并控制文本、数值字体大小、轴标签字体大小实战

    R语言ggplot2可视化柱状图添加数值说明并控制文本.数值字体大小.轴标签字体大小实战 目录

  4. Android主线程耗时动画卡顿,Android性能优化实战之界面卡顿

    原标题:Android性能优化实战之界面卡顿 作者:红橙Darren https://www.jianshu.com/p/18bb507d6e62 今天是个奇怪的日子,有三位同学找我,都是关于界面卡顿 ...

  5. android 变化字体,android 字体修改

    android字体的设置有以下方法: 1)直接在代码设置Typeface customFont = Typeface.createFromAsset(this.getAssets(), "1 ...

  6. Android踩坑日记:Android字体属性及测量(FontMetrics)

    Android字体属性及测量(FontMetrics) 字体的几个参数,以Android API文档定义为尊,见下图 要点如下: 基准点是baseline Ascent是baseline之上至字符最高 ...

  7. Android NFC开发实战详解

    Android NFC开发实战详解 Android开发实战详解NFC国内第一本AndroidNFC开发书籍带你开启AndroidNFC开发的神秘之旅大综合案例帮助读者快速进入实战角色:WiFi快速连接 ...

  8. android roboto字体下载,Android字体设置及Roboto字体使用方法

    本文实例讲述了Android字体设置及Roboto字体使用方法.分享给大家供大家参考.具体分析如下: 一.自定义字体 1.android Typeface使用TTF字体文件设置字体 我们可以在程序中放 ...

  9. Android字体简介

    Android字体简介 Android系统默认支持三种字体,分别为:"sans","serif","monospace". android. ...

最新文章

  1. bzoj1854: [Scoi2010]游戏
  2. IntelliJ IDEA 2020.2 EAP 5 发布:完美支持Java 15
  3. centos mysql 5.1_CentOS下的MySQL 5.1安装
  4. NIO 之 Channel
  5. uitextfield 键盘类型_iOS输入类型-文本字段(Text Fields) | 菜鸟教程
  6. fastdfs 吗 支持windows_主流开源文件存储系统-fastdfs是否支持windows?你可以选择minio...
  7. 五款救急的linux文件恢复软件,五款救急的Linux文件恢复软件
  8. 这家大厂手机业务也凉了 改做手表?官方回应让人放心了...
  9. Alipay Direct Bankpay 支付宝网银支付 (For OpenCart 2.x)
  10. docker修改镜像的存储位置_win10家庭版Docker环境搭建步骤
  11. react17.x+MDUI实现todo小案例,react动态添加与删除元素属性
  12. 多渔:赚钱,就是专注和刻意练习!
  13. QChart入门教程-绘制正弦曲线
  14. linux afs3服务,AFS配置3
  15. 搞懂质数,质因子,互质,最大公约数,最小公倍数.
  16. 绝对值编码器工作原理是什么?单圈/多圈绝对值编码器有何区别?
  17. springboot+vue+elementUI用户头像获取
  18. 使用selenium抓取网页内容
  19. inno setup怎么检测系统是否安装了vcredist_x86 exe
  20. how to know a new word's meaning

热门文章

  1. uniapp+Vue3+Vite+ts+pinia
  2. 生成ID模板:年月日时分秒+6位自增码
  3. 为什么成熟男总会栽在幼稚女身上?
  4. C语言趣味小游戏——猜数字(1到100之间)
  5. python中divmod(10、3)_Python3 divmod() 函数
  6. css五角心收藏点亮,CSS3 发光的五角星
  7. Typescript笔记之基础知识(1):强类型语言和弱类型语言、静态语言和动态语言
  8. E-梅莉的市场经济学
  9. 直线拟合问题(Python实现)
  10. 责怪用户、NSA、盗版、朝鲜?Wannacry勒索病毒这锅还得微软背