没错,我给androidx修了一个bug!

不容易啊,必须先截图留恋😁

这个bug是发生在xml中给AppcompatTextView设置textFontWeight,但是却无法生效。修复bug的代码也很简单,总共就几行代码,但是在找引起这个bug的原因和后面给androidx提pr却花了很久。

//AppcompatTextHelper  
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P&& mFontWeight != TEXT_FONT_WEIGHT_UNSPECIFIED && mFontTypeface != null) {mFontTypeface = Api28Impl.create(mFontTypeface, mFontWeight,(mStyle & Typeface.ITALIC) != 0);

事情的起因是这样的,在Android开发中,我们经常会遇到设计需要我们让TextView支持mediumbold等不同字重样式,原生TextView给了一个textStyle的属性,但是这个属性只支持bolditalicnormal三种样式,这肯定是满足不了设计小姐姐的需求的,而且很多时候bold的样式都比设计出的样式要粗一些。

以前的老办法就是导入不同字重样式的字体文件到app里面,但是很多字体包要支持的语言比较多的话,字体包文件又会比较大,会造成Apk包体积增大。裁剪字体包也可以,但是样式字体包文件多了,也是一件麻烦事,这些杂事很多又不会算到开发时间里面,我就想还有没有其他的解决办法。

网上搜索了下,大多数都是让使用textFontWeight属性的,这个属性倒是可以支持0-1000的字重设置,但是就是兼容性不好,只支持大于api28的手机使用。

Attribute textFontWeight is only used in API level 28 and higher (current min is 21)

但是从我实际使用来看,好像这个属性在TextView上也并没起作用。

<TextViewstyle="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font100"android:textFontWeight="100" />
...
<TextViewstyle="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font100"android:textFontWeight="900" />

我用一个LinearLayout装了9个TextView,textFontWeight设置从100-900,然后运行在api34的模拟器上,可以看到每个TextView都没应用上textFontWeight属性。
在这里插入图片描述
然后我就去Read The Fucking Source Code,看看TextViewtextFontWeiget的相关源码。TextView设置textFontWeiget的相关源码就是从setTypefaceFromAttrsresolveStyleAndSetTypeface,最后就是setTypeface

private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,@XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,@IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {if (typeface == null && familyName != null) {// Lookup normal Typeface from system font map.final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);resolveStyleAndSetTypeface(normalTypeface, style, weight);} else if (typeface != null) {resolveStyleAndSetTypeface(typeface, style, weight);} else {  // both typeface and familyName is null.switch (typefaceIndex) {case SANS:resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);break;case SERIF:resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);break;case MONOSPACE:resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);break;case DEFAULT_TYPEFACE:default:resolveStyleAndSetTypeface(null, style, weight);break;}}
}
private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,@IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {if (weight >= 0) {weight = Math.min(FontStyle.FONT_WEIGHT_MAX, weight);final boolean italic = (style & Typeface.ITALIC) != 0;setTypeface(Typeface.create(typeface, weight, italic));} else {setTypeface(typeface, style);}
}

从代码可以看出来,设置textFontWeight就是靠Typeface.create(typeface, weight, italic)
这样来说,我们的代码应该是没有问题的呀,可是为啥没有起效呢?我想了很久,期间我想用hook的办法,把每个TextViewtextFontWeight都打印出来看看,然后我就写了一个WeightPrinterTextView工具类。

class WeightPrinterTextView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null
) : TextView(context, attrs) {
}

然后我把xml中的TextView都替换成这个WeightPrinterTextView,意想不到的是,textFontWeight这个属性竟然又起效果了😂。

<LinearLayoutstyle="@style/WeightLL"android:layout_height="match_parent"android:orientation="vertical"><demo.simple.fontweighttextview.WeightPrinterTextViewandroid:id="@+id/textView1"style="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font100"android:textFontWeight="100" /><demo.simple.fontweighttextview.WeightPrinterTextViewstyle="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font200"android:textFontWeight="200" />...<demo.simple.fontweighttextview.WeightPrinterTextViewstyle="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font800"android:textFontWeight="800" /><demo.simple.fontweighttextview.WeightPrinterTextViewstyle="@style/TextStyle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Font900"android:textFontWeight="900" />
</LinearLayout>

在这里插入图片描述
啊,这?又把我秀到了,不愧是Android啊!不信邪的我,还重写clean了项目,然后xml又换回了TextView,再运行,结论就是TextView就是不行,但是自定义继承TextViewWeightPrinterTextView就是可以,这就奇怪了。

一次偶然的断点调试让我发现了端倪,在xml中声明的TextView竟然变成了AppcompatTextView,我写了几个方法验证了下,发现了如下规律:在xml中定义的TextView会转换成AppcompatTextView,但是自定义继承的TextView是不会转换类型的。
在这里插入图片描述
其实到这里,我大概就明白了应该是AppcompatTextView的问题,因为我之前也看过相关源码,知道如果Activity是继承自AppcompatActivity的,在createView的时候,会自动把相关的基础控件转成Appcompat的相关关联控件。
在这里插入图片描述
接下来又是喜闻乐见的Read The Fucking Source Code时间,首先肯定从View构造函数看起,因为View属性一般都是在构造函数中读取的。

public AppCompatTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(TintContextWrapper.wrap(context), attrs, defStyleAttr);ThemeUtils.checkAppCompatTheme(this, getContext());mBackgroundTintHelper = new AppCompatBackgroundHelper(this);mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);mTextHelper = new AppCompatTextHelper(this);mTextHelper.loadFromAttributes(attrs, defStyleAttr);mTextHelper.applyCompoundDrawablesTints();mTextClassifierHelper = new AppCompatTextClassifierHelper(this);AppCompatEmojiTextHelper emojiTextViewHelper = getEmojiTextViewHelper();emojiTextViewHelper.loadFromAttributes(attrs, defStyleAttr);
}

从代码来看好像也只有loadFromAttributes方法关联性比较大,然后再进入这个方法里面,但是这里嵌套的方法就更多了,就不详细阐述了。如果光是看代码去找问题就可大海捞针了,这里推荐我最喜欢的断点单步调试方法,在你觉得有关系的相关代码上打上断点,然后等着代码执行,一步一步的看调试结果就行了。

但是有一个注意的点就是:你要选择和运行代码匹配的库版本才行,比如你导入了两个appcompat的库,断点打在了1.0.0版本的源码上,但是运行的却是2.0.0的库,或者就是断点打在了Android14的源码上,但是运行的手机或模拟器却是Android13的系统。

反正经过我的各种调试,各种跳转,最终定位到了AppCompatTextHelperupdateTypefaceAndStyle方法。大致解释下造成这个bug的原因:就是updateTypefaceAndStyle被调用了两次,第一次的mFontWeight值是对的,但是mFontTypeface却是fontWeiget不对的,所以第二次就设置成了错误的FontTypeface,所以我们只需要像下面的代码,重新创建一个新的mFontTypeface就好了。

//AppcompatTextHelper  
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P&& mFontWeight != TEXT_FONT_WEIGHT_UNSPECIFIED && mFontTypeface != null) {mFontTypeface = Api28Impl.create(mFontTypeface, mFontWeight,(mStyle & Typeface.ITALIC) != 0);

本来到这里就应该结束了,我只需要用hook或者反射等相关方法解决掉自己的需求就好了,但是突然想到我好像从来没给Google的仓库提交过代码,之前也就给AndroidUtilCodeijkplayer这种国内开发者的仓库提过pr,不管了,试试就逝世😂!

我去仔细读了androidxreadme文档,大致流程就是先在Android Issue Tracker创建一个issue,这一点很坑,我从它这链接点进去,说我没有create issue的权限,我还以为不用完成这一步,这也导致了最开始这个pr搁置了很久也没人来code review。
在这里插入图片描述其实是需要在下拉框,重新选择子模块的。
在这里插入图片描述

然后再是添加自己的Test方法,覆盖测试用例,完善文档说明,最后push代码,new一个新的pr,选择一个code reviewer。

 @Testpublic void testTextFontWeight() {final AppCompatTextView textView = mActivity.findViewById(R.id.text_view_text_font_weight);if (textView.getTypeface() == null) return;final int textFontWeight = textView.getTypeface().getWeight();assertEquals(textFontWeight, 900);}

还有就是在选择code reviewer可能需要注意一下,能多选几个就多选几个吧,我选择了一个对appcompat贡献最多的一个老哥,结果到现在都还在pending review状态,期间都打算发邮件去沟通一下了,我看其他的pr都是同时选择了好几个code reviewer,最后还是在issue tracker创建了一个issue,才有好心的Google老哥帮忙review了一下,thank you bro😁。

提交pr后,google-cla-bot就会让你同意一个Contributor License,同意之后就等着code review就行了。

code review期间liutikas (Aurimas)大佬帮我完善了测试用例,nona-google (Seigo Nonaka)大佬approved了我的code changes,谢谢他们,Thanks(・ω・)ノ。
在这里插入图片描述
在这里插入图片描述
总的来说,这次给androidx提交代码还是很开心,因为AOSP里面也有我写的代码啦,哈哈哈~ 从readme得知,github上这个androidx仓库只是AOSP里面的androidx一个镜像,在github上合并的pr都会同步到AOSP里面去。

好了这篇文章就到这里结束了~假的,我骗你的。因为我在看androidx里面源码的时候,发现了一个神奇的类,那就是TypefaceCompat。它里面有个create的方法,可以适配不同的系统版本,创建带有weightTypeface

public static Typeface create(@NonNull Context context, @Nullable Typeface family,@IntRange(from = 1, to = 1000) int weight, boolean italic) {if (context == null) {throw new IllegalArgumentException("Context cannot be null");}Preconditions.checkArgumentInRange(weight, 1, 1000, "weight");if (family == null) {family = Typeface.DEFAULT;}return sTypefaceCompatImpl.createWeightStyle(context, family, weight, italic);
}

在这个类初始化的时候,static代码块里面会创建自适应的sTypefaceCompatImpl

static {if (Build.VERSION.SDK_INT >= 29) {sTypefaceCompatImpl = new TypefaceCompatApi29Impl();} else if (Build.VERSION.SDK_INT >= 28) {sTypefaceCompatImpl = new TypefaceCompatApi28Impl();} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {sTypefaceCompatImpl = new TypefaceCompatApi26Impl();} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N&& TypefaceCompatApi24Impl.isUsable()) {sTypefaceCompatImpl = new TypefaceCompatApi24Impl();} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {sTypefaceCompatImpl = new TypefaceCompatApi21Impl();} else {sTypefaceCompatImpl = new TypefaceCompatBaseImpl();}
}

本来我是打算再提交个pr,让AppcompatTextView能不区分版本的支持textFontWeight,但是在自己写代码的时候好像发现为啥Google的大佬不这样做的原因了,可能是因为Typeface.getWeight这个方法是在Api28添加的,这样就不好写单元测试了。其实要做感觉也能做,就是给AppcompatTextView增加getWeight的方法,但是这样又感觉有点抽象😂。

所以就此,我又搞了一个新的仓库😄,用来适配全版本的textFontWeight
在这里插入图片描述
simplepeng/FontWeightTextView: 让textFontWeight属性支持Api29(Android9-p)以下

代码确实很简单,就读取了下textFontWeight的属性,然后调用了下TypefaceCompat.create,以至于群里老哥都吐槽:就这么几行代码还要搞一个库😂?

就此,本篇文章确实结束啦,完结撒花✿✿ヽ(°▽°)ノ✿。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

云手机的海外原生IP有什么用?

在全球数字化进程不断加快的背景下&#xff0c;企业对网络的依赖程度日益加深。云手机作为一项创新的工具&#xff0c;正逐步成为企业优化网络结构和全球业务拓展的必备。尤其是云手机所具备的海外原生IP功能&#xff0c;为企业进入国际市场提供了独特的竞争优势。 什么是海外原…

DNF Decouple and Feedback Network for Seeing in the Dark

DNF: Decouple and Feedback Network for Seeing in the Dark 在深度学习领域&#xff0c;尤其是在低光照图像增强的应用中&#xff0c;RAW数据的独特属性展现出了巨大的潜力。然而&#xff0c;现有架构在单阶段和多阶段方法中都存在性能瓶颈。单阶段方法由于域歧义&#xff0c…

如何使用 3 种简单的方法将手写内容转换为文本

手写比文本更具艺术性&#xff0c;这就是许多人追求手写字体的原因。有时&#xff0c;我们必须将手写内容转换为文本&#xff0c;以便于存储和阅读。本文将指导您如何轻松转换它。 此外&#xff0c;通常以扫描的手写内容编辑文本很困难&#xff0c;但使用奇客免费OCR&#xff…

视觉距离与轴距离的转换方法

1.找一个明显的参照物&#xff0c;用上方固定的相机拍一下。保存好图片 2.轴用定长距离如1mm移动一下。 3.再用上相机再取一张图。 4.最后用halcon 将两图叠加 显示 效果如下 从图上可以明显的看出有两个图&#xff0c;红色标识的地方。 这时可以用halcon的工具画一个长方形…

Cesium 绘制可编辑点

Cesium Point点 实现可编辑的pointEntity 实体 文章目录 Cesium Point点前言一、使用步骤二、使用方法二、具体实现1. 开始绘制2.绘制事件监听三、 完整代码前言 支持 鼠标按下 拖动修改点,释放修改完成。 一、使用步骤 1、点击 按钮 开始 绘制,单击地图 绘制完成 2、编辑…

误差评估,均方误差、均方根误差、标准差、方差

均方根误差 RMSE/RMS 定义 RMSE是观察值与真实值偏差的平方&#xff0c;对于一组观测值 y i y_i yi​ 和对应的真值 t i t_i ti​ R M S E 1 n ∑ i 1 n ( y i − t i ) &#xff0c;其中n是观测次数 RMSE\sqrt{\frac1n \sum_{i1}^n (y_i-t_i)} \text{&#xff0c;其中n是…

2.个人电脑部署MySQL,傻瓜式教程带你拥有个人金融数据库!

2.个人电脑部署MySQL&#xff0c;傻瓜式教程带你拥有个人金融数据库&#xff01; ‍ 前边我们提到&#xff0c;比较适合做量化投研的数据库是MySQL&#xff0c;开源免费。所以今天我就写一篇教程来教大家如何在自己的环境中部署MySQL。 在不同的设备或系统中安装MySQL的步骤…

局部凸空间及其在算子空间中的应用之四——归纳极限空间2

局部凸空间及其在算子空间中的应用之四——归纳极限空间2 前言一、归纳极限拓扑中极限的含义总结 数学的真理是绝对的&#xff0c;它超越了时间和空间。——约翰冯诺伊曼 前言 在上一篇文章中&#xff0c;我们讨论了归纳极限拓扑的概念和与连续线性算子有关的一个重要结论。认…

为什么编程很难?

之前有一个很紧急的项目&#xff0c;项目中有一个bug始终没有被解决&#xff0c;托了十几天之后&#xff0c;就让我过去协助解决这个bug。这个项目是使用C语言生成硬件code&#xff0c;是更底层的verilog&#xff0c;也叫做HLS开发。 项目中的这段代码并不复杂&#xff0c;代码…

postman控制变量和常用方法

1、添加环境&#xff1a; 2、环境添加变量&#xff1a; 3、配置不同的环境&#xff1a;local、dev、sit、uat、pro 4、 接口调用 5、清除cookie方法&#xff1a; 6、下载文件方法&#xff1a;

calibre-web报错:File type isn‘t allowed to be uploaded to this server

calibre-web报错&#xff1a;File type isnt allowed to be uploaded to this server 最新版的calibre-web在Upload时候会报错&#xff1a; File type isnt allowed to be uploaded to this server 解决方案&#xff1a; Admin - Basic Configuration - Security Settings 把…

2024PDF内容修改秘籍:工具推荐与技巧分享

现在我们使用PDF文档的频率越来越高了&#xff0c;很多时候收到的表格之类的资料也都是PDF格式的&#xff0c;如果进行转换之后编辑再转换为PDF格式还是有点麻烦的&#xff0c;那么pdf怎么编辑修改内容呢&#xff1f;这篇文章我将介绍几款可以直接编辑PDF文件的工具来提高我们的…

鸿蒙next 带你玩转鸿蒙拍照和相册获取图片

前言导读 各位网友和同学&#xff0c;相信大家在开发app的过程中都有遇到上传图片到服务器的需求&#xff0c;我们一般是有两种方式&#xff0c;拍照获取照片或者调用相册获取照片&#xff0c;今天我们就分享一个小案例讲一下这两种情况的实现。废话不多说我们正式开始 效果图…

安全带检测系统源码分享

安全带检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

Linux,uboot,kernel启动流程,S5PV210芯片的启动流程,DRAM控制器初始化流程

一、S5PV210芯片的DRAM控制器介绍、初始化DDR的流程分析 1、DRAM的地址空间 1)从地址映射图可以知道&#xff0c;S5PV210有两个DRAM端口。 DRAM0的内存地址范围&#xff1a;0x20000000&#xff5e;0x3FFFFFFF&#xff08;512MB&#xff09;&#xff1b;DRAM1:的内存地址范围…

HarmonyOS---权限和http/Axios网络请求

网络请求(http,axios) 目录 一、应用权限管理1.1权限的等级1.2授权方式1.3声明权限的配置1.4如何向用户进行申请 二、内置http请求使用三、Axios请求使用&#xff08;建议&#xff09;3.1 使用方式一3.2 使用方式二&#xff08;建议&#xff09; 一、应用权限管理 应用权限保护…

SpringCloud Alibaba之Seata处理分布式事务

&#xff08;学习笔记&#xff0c;必用必考&#xff09; 问题&#xff1a;Transactional 的9种失效场景&#xff1f; 1、介绍 1.1、简介 官网地址&#xff1a;Apache Seata 源码地址&#xff1a;Releases apache/incubator-seata GitHub Seata是一款开源的分布式事务解决…

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【文件系统】上

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 子系统开发内核 轻量系统内核&#xff08;LiteOS-M&#xff09; 轻量系统内核&#…

linux StarRocks 安装

一、检查服务器是否支持avx2&#xff0c;如果执行命令显示空&#xff0c;则不支持&#xff0c;那么安装后无法启动BE cat /proc/cpuinfo |grep avx2我的支持显示如下&#xff1a; 二、安装 docker run -p 9030:9030 -p 8030:8030 -p 8040:8040 -p 9001:9000 --privilegedtrue…

ps证件照蓝底换白底

ps证件照蓝底换白底 1、打开 Photoshop&#xff0c;导入需要处理的照片。 2、左侧工具栏中选择“魔棒工具”&#xff0c;点击证件照的背景区域进行选择。 3、使用快捷键 Shift F5 或者从顶部菜单选择“编辑” -> “填充”&#xff0c;在弹出的对话框中选择“填充内容”中…