一.拆行

监听到文本变化后需要对文本进行拆行,得到每行的文字。我们可以通过Layout实现,代码如下:

String text = layout.getText().toString();
int lineCount = layout.getLineCount();
for (int i = 0; i < lineCount; i++) {
int start = layout.getLineStart(i);
int end = layout.getLineEnd(i);
String rowStr = text.substring(start,end);
}

但是,但是,但是,这里有一个点需要特别注意,不能通过EditText自带的Layout来计算每行文本,不然拿到的每行文本是错误的。

为什么呢?

举个例子:

如下图所示,当你在第二行将要输入“好”时,因为你输入"好"后该行文本宽度已经大于此时EditText的宽度了,所以“好”字会被认为是重启一行,这样你得到的每行文本就是错的了,因为“好”应该显示在第二行才对。

这就涉及到我在思路探究中提到的第一个问题,无论我字体怎么缩小放大,如何保证每行最多可显示的文本都是一样的?

那么如何保证呢?

其实很简单,因为影响到文字自动换行的因素主要就是字体大小和最大文本宽度,那么只要保证这两个因素不变,无论你输入什么文本,都能准确一致的拆分出每一行的文字。

因为EditText的每行字体在变,而且宽度也在变,所以通过EditText自带的Layout算出的每行文本肯定是错误的。

所以,思路应该是这样的,你需要构建一个用于计算的Layout,这个Layout的字体大小和宽度必须是固定不变的,这样它就能够保证每行最多可容纳的文本始终是一样的,这样我就能够准确拆分出每行文本。

前面已经说过EditText的计算工作都是交给DynamicLayout,所以我们需要创建的是DynamicLayout。代码如下

protected Layout buildCalculateLayout(CharSequence text,TextView host){
TextPaint paint = new TextPaint(host.getPaint());
paint.setTextSize(mDefFontSize);
return new DynamicLayout(text,paint, mDefMaxTextWidth,host.getLayout().getAlignment(),host.getLayout().getSpacingMultiplier(),host.getLayout().getSpacingAdd(),host.getIncludeFontPadding());
}

需要注意的是,这里除了字体大小和宽度,其他的参数都需要跟EditText的参数一样。

其中mDefFontSize是一开始定义的一个默认字体大小,mDefMaxTextWidth是EditText在没动态调整宽度前的宽度(需要减去padding)。

这样子每次都是通过自构建的Layout去计算每行的文本,就不需要考虑EditText的字体和宽度的动态变化。

二.按匹配最大宽度计算每行字体大小

搞定了第一步拆行后,其实已经离成功不远了,接下来就是如何确定每行字体大小了。

确定字体大小说简单简单,说难也难,关键是看你有没有想到那个点。比如一开始我一直纠结于每行文字是怎么随着输入文字个数和行数变化动态改变的,陷入了局部细节,搞得自己晕头转向,如果按照这个方向思考我感觉估计是怎么做都搞不定的。

后来,想了两天后还是没搞明白,我就试着换个思维方式,从整体来考虑,接下来就有种恍然大悟的感觉,原来其实没那么难。

首先,有一个规律是很显然的:

每行文字越多,它的字体就越小,文字越少,字体就越大。

那么我就想一开始时你把每行文字的宽度放大到最大文本宽度,算出匹配这个宽度的字体应该多大,这样文字越少的行,字体就越大,文字越多的行,字体就越小,这个不就是符合那个规律吗。

计算文本宽度的代码如下:

float width = paint.measureText(text);

因为需要通过不断更改字体大小,去算出匹配最大宽度的字体,所以为了减少计算量,一开始可以做一个初始字体大小的换算。

当字体大小是mDefFontSize时对应的文本宽度是mDefMaxTextWidth,那么当文本宽度是x时,对应的字体大小是y,因为字体大小和宽度成反比(宽度越小,字体越大),所以y的计算公式就是:

y = mDefMaxTextWidth \ x * mDefFontSize
x = paint.measureText(text);

这样我们就可以得到一个比较接近目标值的字体大小,这时候再去判断此时文本宽度是否匹配最大文本宽度,不等于的话再去改变字体大小,直到文本宽度匹配最大文本宽度为止。

代码如下:

public float calculateMatchWidthSize(Paint paint,String text,int maxWidth){
float textSize = paint.getTextSize();
float width = paint.measureText(text);

if(maxWidth >= width && maxWidth - width <= text.length()){
return textSize;
}

if(width > maxWidth){
textSize = getNarrowFitTextSize(paint,text,maxWidth,1);
}else{
textSize = getZoomFitTextSize(paint,text,maxWidth,1);
}

return textSize;
}

private float getNarrowFitTextSize(Paint paint,String text,int maxWidth,float rate){
float textSize = paint.getTextSize();
textSize -= 1 * rate;
paint.setTextSize(textSize);
float width = paint.measureText(text);
if(maxWidth >= width && maxWidth - width <= text.length()){
return textSize;
}
//结束条件
if(width < maxWidth){
return getZoomFitTextSize(paint,text,maxWidth,rate);
}else{
return getNarrowFitTextSize(paint,text,maxWidth,rate);
}
}

private float getZoomFitTextSize(Paint paint,String text,int maxWidth,float rate){
float textSize = paint.getTextSize();
textSize += 1 * rate;
paint.setTextSize(textSize);
float width = paint.measureText(text);
if(maxWidth >= width && maxWidth - width <= text.length()){
return textSize;
}
//结束条件
if(width < maxWidth){
return getZoomFitTextSize(paint,text,maxWidth,rate);
}else{
return getNarrowFitTextSize(paint,text,maxWidth,rate);
}
}

三.按匹配最大高度计算每行字体大小

按照匹配最大宽度计算出来的字体会很大,导致文本高度很高,这时候就需要再动态调整每行字体大小,直到文本高度匹配最大高度为止。

动态调整字体大小时,每行文字的字体大小需要按比例调整,比如每行字体都调整为原来的0.9倍大小。

计算每行文本高度的代码:

int height = paint.getFontMetricsInt(null);

为了让每行文本高度的累加值等于文本实际总高度,需要设置EditText的边距为0并且去掉文字上下的空白部分。代码如下:

//去掉文本上下空白区域
mHost.setIncludeFontPadding(false);
//不设置行间距
mHost.setLineSpacing(0,1);

为了提高计算速度,采用二分法来动态调整字体大小,代码如下:

/**

  • 二分法查找合适的字体大小,字体大小按比例调整
  • @return
    */
    private void calculateMatchHeightSizeByRate(float lowRate,float highRate,int minHeight,int maxHeight){
    if(highRate - lowRate <= RATE_SCALE_ERROR_VALUE){
    return;
    }

最后

考虑到文章的篇幅问题,我把这些问题和答案以及我多年面试所遇到的问题和一些面试资料做成了PDF文档,如果有需要的朋友可以私信我【面试】免费领取

点击这里领取Android面试资料汇总

F%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

[外链图片转存中…(img-ScOKgYUY-1643783724586)]

[外链图片转存中…(img-VedF3b3k-1643783724587)]

喜欢的朋友可以关注、转发、点赞 感谢!

Android知识笔记:特效系列,仿instagram文字排版特效,android应用开发基础学堂在线答案相关推荐

  1. 乌鸦飞过flash素材下载_会声会影仿AE文字动画特效教程-会声会影中文官网

    现在人多少有点娱乐至死的精神,没事就爱点找乐子,许是窥出了百姓这点心思,各大卫视都争相推出各自的综艺王牌节目,但是僧多粥少,要想争得收视王,自然要贴合观众逗逼搞笑的心性,本集小编同大家分享一个快速获取 ...

  2. 【Android知识笔记】ARouter / Navigation / EventBus

    注:本文主要基于过去 Android View 体系的路由学习笔记整理,不包括最新的 Jetpack Compose 路由体系,如您需了解关于 Jetpack Compose 中的导航路由,请参考 J ...

  3. 【Android知识笔记】系统进程(二)

    AMS ActivityManagerService(以下简称AMS) 主要负责四大组件的启动.切换.调度以及应用进程的管理和调度工作.所有的APP应用都需要与AMS打交道,ActivityManag ...

  4. Android学习笔记(一) - 如果我们来设计Android

    大家应该都背过公式,对于背公式,大体上有这么几种做法: 1,死背. 2,看公式的证明,并看懂. 3,尝试自己证明公式,而后对照"标准"证明. 一般来说,对公式的记忆和使用:3 &g ...

  5. android dev guide 中文版,《Android Dev Guide》系列教程1:什么是Android?

    <>系列1:什么是Android? 懒骨头(http://blog.csdn.com/iamlazybone) Android是一个移动设备的软件集,他包括一个操作系统.中间件,和一些重要 ...

  6. android学习笔记---57_采用方向传感器实现指南针,android设备传感器介绍,以及使用方法

    Java技术qq交流群:JavaDream:251572072 ------------------------------------------------------ 传感器类型:方向.加速度( ...

  7. 【Android Studio安装部署系列】二十二、Android studio自动生成set、get方法

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 操作步骤 将光标放置我们所要生成get,set方法的实体类空白处--然后右键--选择Generate. 选择"Getter an ...

  8. Android知识补充(Android学习笔记)

    Android知识补充 ●国际化 所谓的国际化,就是指软件在开发时就应该具备支持多种语言和地区的功能,也就是说开发的软件能同时应对不同国家和地区的用户访问,并针对不同国家和地区的用户,提供相应的.符合 ...

  9. 《繁凡的深度学习笔记》前言、目录大纲 一文让你完全弄懂深度学习所有基础(DL笔记整理系列)

    <繁凡的深度学习笔记>前言.目录大纲 (DL笔记整理系列) 一文弄懂深度学习所有基础 ! 3043331995@qq.com https://fanfansann.blog.csdn.ne ...

  10. 【Android Studio安装部署系列】目录

    概述 从刚开始使用Android Studio到现在,下面所有目录下的操作,当时习惯性的把每一个整理成一个文档(其实就是简单文字描述+截图):有些地方当时是一知半解,现在会稍微明白一些.正好赶上现在有 ...

最新文章

  1. 参考答案:03 向量空间
  2. 三国志、英雄无敌玩腻了?没关系,我教你开发个战旗游戏玩玩
  3. java单例注册表_Java单例模式(Singleton)
  4. 游戏服务器架构-设计模式之观察者模式和发布订阅模式真的一样吗?
  5. .NET方法演化史 从Delegate到Lambda再到LINQ
  6. IntelliJ IDEA lombok插件的安装和使用
  7. Oracle大型数据库系统在AIX/UNIX上的实战详解
  8. NXP与STM32单片机程序架构
  9. 大数据测试理论与方法
  10. 2017实习生在线编程——奇虎360(分金子)
  11. 一个优秀活跃的视频转码项目分享
  12. delaunay三角剖分与delaunay四面体
  13. 基于 SpringBoot 的个人博客系统设计与实现(含论文与程序代码).rar
  14. 【python】获取当前时间(年月日时分秒)
  15. TalkingData首席金融行业专家鲍忠铁:18亿数据解读移动互联网
  16. 变革的腾讯:一个游戏之外的帝国
  17. Oracle - 同义词
  18. LITESTAR 4D应用:室内植物照明模拟
  19. 大鱼号开通收益的基础条件是什么,大鱼号可以通过哪几种方式开通收益
  20. 卫星地图多长时间更新一次啊?

热门文章

  1. cadence IC
  2. Mac 下通过Homebrew安装ADB工具
  3. mysql数据库p_PbootCMS Sqlite数据库转Mysql数据库教程
  4. Springboot+CAS下Session过期无效,页面请求302问题解决
  5. 安卓毕业设计- Android计算器
  6. Microsoft Store 微软商店中 APP 独立安装包下载方法
  7. MySQL是怎样运行的(实体书扫描+掘金小册)免费下载
  8. 如何用c语言批量替换字符串,[C/C++]急速批量替换字符串
  9. GitKraken 比较好用的 Git 可视化工具
  10. MAC系统镜像几个版本的下载链接