Android 中美腻的下划线

本文讲的是Android 中美腻的下划线,


在过去两年里,我经常发现一些尝试去如何提高有关在网页中渲染下划线文本修饰的文章。此类问题也同样发生在Android(平台):下划线的文本修饰与降部相交。比较下Android当前如何绘制下划线文本(上图)以及它的替代方案(下图):

你更喜欢哪一种?

尽管我完全认可这些努力,但是我从未喜欢过任何公开的解决方法。目前最新的技术(追求艺术般的状态)—毫无疑问地会强迫开发者们受限于CSS—似乎是通过绘制线性渐变以及多重阴影(我见过多达12层的!)来实现的。这些解决方案都具有无法否认的成效,但这种绘制如此多阴影的做法,即使没有增加模糊效果,也会使得图形开发者们足够头疼了。还有一点,这种方法仅仅在实色的背景下有效。

我今天下午一时兴起,开始着手发掘满足以下需求的其他解决方案:

  • 兼容旧版本的Android系统
  • 仅使用标准的View和Canvas APIs
  • 不需要过度重绘或者大量的阴影开销
  • 在任何背景下都有效,而不是只支持实色背景
  • 不依赖绘制流水线的操作顺序(文本先于/晚于下划线的绘制是无关紧要的)

我在这里提供了两种解决方案,你可以在GitHub获取。其中一种方法适用于API level 19及以上,另外一种适用于API level 1及以上,或者说它 应该 至少支持API level 1以上,我没有完全地测试,但我相信API文档。

你可以在下面的截图中观察比较下这两种被称作 Path 和 Region 的方法:

在Android中更好展示下划线文本的两种可能的实现方式

如何实现的?

这些实现背后的思想与之前提到的CSS方法出奇地类似。我们使用一整条直线段来表示下划线,剩下所需要做的就是为降部挪出空间...

使用Path类

API level 19 (叫KitKat更耳熟) 中引入了一个操作路径的新API叫做path ops。这个API允许你为实例建立两个路径的交叉点,或是从一条路径中减去其它的路径。

使用这个API,制作我们想要的下划线就非常简单了。第一步就是为我们的文本获取轮廓

mPaint.getTextPath(mText, 0, mText.length(), 0.0f, 0.0f, mOutline);

注意返回的path可以通过一种填充的样式来渲染原始文本,我们在这里要使用它来进行后续操作。

文本轮廓

下一步就是剪切表示下划线的矩形轮廓。这一步不完全是必要的,但是这样可以避免在下一步可能出现的近似值偏差。我们只需使用intersection path操作就能方便的实现这一功能:

mOutline.op(mUnderline, Path.Op.INTERSECT);

现在轮廓路径仅仅包含几位降部与下划线的交叉部分。

只有黑色区域表示是路径的一部分,其余的部分只是为了可视目的。

剩下要做的就是从下划线中减去那些降部位置的部分。在做这个之前,我们必须扩大原始文本的尺寸来为降部与下划线间创造出间隙。这个功能可以通过划除我们剪切的轮廓然后建立一个新的填充路径实现:

mStroke.setStyle(Paint.Style.FILL_AND_STROKE);        mStroke.setStrokeWidth(UNDERLINE_CLEAR_GAP);
mStroke.getFillPath(mOutline, strokedOutline);

划掉的宽带代表着你想为降部和下划线之间留下多大的空间。

划除剪切掉的轮廓

最后一步就是使用另外一个path操作从下划线矩形轮廓中减去划除部分和剪切掉的部分:

mUnderline.op(strokedOutline, Path.Op.DIFFERENCE);

最后的下划线可以使用一个填充画笔绘制:

canvas.drawPath(mUnderline, mPaint);

使用Region类

Region是一种在屏幕上高效展示非矩形形状的方法。你可以想象一块区域是由若干对齐到渲染缓冲区的矩形集合组成的。Regions可以被看作是_栅格化_的Path。这意味着如果我们将Path转换成Region后,我们获得的是一系列像素坐标点的集合,一旦Path被绘制,它将影响到这些获得的坐标集合。

Region有趣的地方在于它提供了与Path相同的操作。两块Regions能够互相交错、扣除重叠的部分等等。更重要的是,Region从最早的Android API中就已经存在了。

用Region实现下划线的方法几乎与用Path完全相同,主要的区别存在于轮廓何时怎样被剪切的:

Region underlineRegion = new Region(underlineRect);

// 为文本建立一个Region并且剪切掉下划线部分
Region outlineRegion = new Region();
outlineRegion.setPath(mOutline, underlineRegion);

// 提取返回的Region的Path,从而获得一份剪切后的文本轮廓的拷贝
mOutline.rewind();
outlineRegion.getBoundaryPath(mOutline);

// 划掉剪切掉的文本,将其结果转为一个填充样式的Path
mStroke.getFillPath(mOutline, strokedOutline);

// 使用划掉文本的轮廓建立一个Region对象
outlineRegion = new Region();
outlineRegion.setPath(strokedOutline, new Region(mBounds));

// 在下划线轮廓中扣除剪切掉的,划掉的文本轮廓
underlineRegion.op(outlineRegion, Region.Op.DIFFERENCE);

// 使用下划线Region建立一个Path
underlineRegion.getBoundaryPath(mUnderline);

两种方法的区别

由于Path类和Region类的本质不同,两种实现间有着不易察觉的区别。因为Path类仅仅在曲线上操作,因此在我们从下划线轮廓中扣除降部时,就保留了降部轮廓的斜度,这就造成下划线空隙的边缘与降部的曲线斜度平行。这种效果或许是又或许不是所期望的。

另一方面,Region类操作的是整个像素点,它会清除下划线竖向的切割(你的下划线足够细的话)。下图是两种实现的比较:

上图: Path类. 下图: Region类. 注意到上面的斜度没?如果没有,你需要仔细看。

应当在产品中使用吗?

在你尝试将这些技术运用到你的应用之前,需要了解到我这次没有做任何的性能测试。请记住这些尝试很大程度上只是一种编程乐趣的挑战。所提供的代码没有根据文本的大小来适配下划线的位置,也没有适配间隙的宽度。可能在字体的适配上也有问题,我只尝试了几种Android默认的字型。就让我们将这些问题留给读者当做练习来解决吧。

如果你将尝试着在你的应用里使用这些代码,那么我必须承认我将很乐于看到关于spans的实现,我会鼓励你至少缓存一下最后的填充Path。由于它仅仅依赖于字型,字体和字符串,缓存还是比较容易实现的。

另外,文章中描述的这两种实现方法完全严格遵循开放的SDK API。如果在Android framework层直接实现的话,我有一些想法能使得这个功能变得更有效率。

比如 Region 的转换能够通过渲染自身来获得优化,而不用转换回 Path 了(这会造成软件的碎片化以及GPU结构化更新)。Region类本身就是一系列矩形的集合,对于渲染流水线来说,与绘制碎片化的Path相比,绘制一系列的直线或矩形变得容易多了。






原文发布时间为:2016年07月10日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-08-30 12:40:32

Android 中美腻的下划线的相关文章

如何清除WPS 2005文档中超链接的下划线

我们用WPS文字编辑文档时,插入超链接之后,系统会自动在超链接的下面显示一个下划线,这是为了让用户更好的区分超链接与普通文本而设置的.可当我们要打印该文档时,这个额外的下划线就有些讨厌了,会影响文档的打印效果.我们如何在保持其超链接特征的前提下轻松清除这些下划线呢,且看我的方法. 我们可以在编辑完文档后,打印前一次性清除所有超链接的下划线. 点击"格式"工具栏上的"样式和格式"按钮 () , 或通过菜单栏上的"格式→样式和格式"来打开"

PPT2013中隐藏超链接下划线的方法

  PPT2013中隐藏超链接下划线的方法 1.打开PowerPoint演示文稿,选中带有超链接的字体,然后鼠标右键,单击"字体"选项,如下图. 2.弹出"字体"对话框,在"字体"选项卡中,"下划线线型"设置成为"单线","下划线颜色"设置成"白色",然后单击"确定",如下图. 3.看下方效果图,会发现下划线已经没有了. 说明:此方法只是让下划线颜

怎样隐藏PowerPoint2013中超链接的下划线

  1.点击"开始"→"程序"PowerPoint2013 2."插入"→"文本框"→"横排文本框",在空白处写字,调整字的大小等 3.插入"→"超链接",出现"插入超链接"对话框,选择需要链接到的位置,点击确定 4.这时出现的超链接字体就会有下划线,如图 5.选中超链接字体→右击→选择"字体" 6.在弹出的对话框中,选择"下划

android中读取根目录下的xml文件

问题描述 android中读取根目录下的xml文件 显示无法读取文件 package com.example.testreadxml; import java.io.IOException; import java.io.InputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; import android

android中不同包下的Camera有什么不一样 如图所示

问题描述 android中不同包下的Camera有什么不一样 如图所示 android中不同包下的Camera有什么不一样 如图所示 解决方案 一个可以绘制类似三维动画的,一个是相机拍照用的 解决方案二:

大家如何处理Android中的libs下的x86文件

问题描述 大家如何处理Android中的libs下的x86文件 现在Android手机X86的多吗,网上搜了几种在用户统计中都没有搜到,而libs下的x86文件夹如果不参与打包,则可以是apk包减少6M.我是应该把它们留下还是去掉呢?

text decoration-在css中特定某个下划线去掉,不是在html中直接改

问题描述 在css中特定某个下划线去掉,不是在html中直接改 在html中有N个超链接,我想要其中的一个超链接没有下划线,我赋予这个超链接一个类选择器class=""s1"" 在css中要怎么弄才可以去掉这个超链接的下划线呢 解决方案 .s1{text-decoration: none;} 解决方案二: s1{text-decoration: none;}

Android中Volley框架下保持会话方法_Android

公司经理把我拉出来,死马当活马医,做一个安卓app,作为刚毕业几个月且只是培训了几个月的小白来说,这无疑是一个非常大的挑战,当然最大的挑战不是这个,最大的挑战时两个周做出来.这是最蛋疼的,说实话,对于有两三年的开发经验的人来说,两个周开发一个项目很简单,说不定还有很多时间用来干别的. 于是一上来就把自己给难住了,登陆还是很好做的,只要验证返回的信息就可以跳转,但是在接下来后面的数据接口连接的时候各种报错,整了两天,查了很多信息,还接受了公司老人的嘲讽和谩骂终于做出来了. 这个是基于session

怎么在word文档中添加空白下划线

1.如下图所示我们先做好几个要添加下划线文字说明,然后把光标放在文字后面. 文档中添加空白下划线-word文档空白下划线">2.现在我们按住键盘上的 "Shift"+"-" 键,一般是左边的的"Shift"键 与 减号键. 3.这样就会自动输入下划线了,我们一直按就会一直有哦 4.大功告成 是不是很简单,希望对你有帮助!!! 好了,在最后一聚小编告诉你自己的办法吧,就是先把文字打好,然后选中文字给文字加下划线这样就实现我要的效果了