最后
上面这些公司都是时下最受欢迎的互联网大厂,他们的职级、薪资、福利也都讲的差不多了,相信大家都是有梦想和野心的人,心里多少应该都有些想法。
也相信很多人也都在为即将到来的金九银十做准备,也有不少人的目标都是这些公司。
我这边有不少朋友都在这些厂工作,其中也有很多人担任过面试官,上面的资料也差不多都是从朋友那边打探来的。除了上面的信息,我这边还有这些大厂近年来的面试真题及解析,以及一些朋友出于兴趣和热爱一起整理的Android时下热门知识点的学习资料。
部分文件:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
因为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;
}
float middleRate= (lowRate+highRate)/2;
scaleFontSizeByRate(middleRate);
int height = getTextHeight();
if(height > maxHeight){
//缩小字体后文字高度大于最大值,需要继续缩小字体
highRate = middleRate;
calculateMatchHeightSizeByRate(lowRate,highRate,minHeight,maxHeight);
} else if(height < minHeight){
//缩小字体后文字高度小于最小值,需要放大字体
lowRate = middleRate;
calculateMatchHeightSizeByRate(lowRate,highRate,minHeight,maxHeight);
}
}
private int getTextHeight(){
int totalHeight = 0;
for(CustomSpanData customSpanData: mCustomTextSpanDataList){
int lineHeight = getSingleLineHeight(customSpanData.getTextSize());
totalHeight += lineHeight;
}
return totalHeight;
}
private int getSingleLineHeight(float fontSize){
Paint paint = new Paint(mHost.getPaint());
paint.setTextSize(fontSize);
return paint.getFontMetricsInt(null);
}
private void scaleFontSizeByRate(float rate){
for(int i=0;i<mOriFontSizePxList.size();i++){
float fontSize = mOriFontSizePxList.get(i) * rate;
mCustomTextSpanDataList.get(i).setTextSize(UNIT_PX,fontSize);
}
}
四.重新调整EditText的宽度
前面有提到一个问题那就是输入框的宽度是怎么动态改变的?
阅读了前三个步骤后是不是已经有了答案,首先通过自构建的Layout确定每行需要显示什么文本,然后动态调整每行字体大小以适应输入框的宽高,这时候可能每行的字体已经很小了,如果不调整EditText的宽度必然会导致不同行的文字顶到同一行显示。
所以,最后一步需要把输入框的宽度调整为所有行中宽度最大的那一行的宽度。
总结
这里说下我在这个过程中踩的坑。
一开始我用的span是自己写的一个继承ReplacementSpan的自定义span,然后就一直存在删除文本时都是整行删除的问题,不能删除单个字符,后来看了源码发现EditText对ReplacementSpan的处理是直接当成一个整体,所以删除也是整个ReplacementSpan都删除掉。
监听文字的变化一开始我是放在onPreDraw方法实现的,后来发现会出现文字换行时跳动的问题,正确的方法应该是放在TextWatcher的onTextChanged方法实现。
一开始陷入了局部思维,一直在思考每行字体大小是怎么变化的,后来才发现应该要整体考虑,不需要考虑局部,这个浪费了我很多时间。
最后
最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
总结
我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题
等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~
Android 基础知识点
Java 基础知识点
Android 源码相关分析
常见的一些原理性问题
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1715661670673)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!