一、今天在做NavigatorBar(导航栏,系统级应用)时,遇到一个问题,在调用TextView.setText方法后,会出现文本重叠的问题,如下图所示:


二、我的实现场景比较特殊,我的导航栏是无Activity做为容器的,在Service中,直接通过WindowManager.addView()添加NavigatorBar,而NavigatorBar本身是一个自定义View,直接继承自FrameLayout实现的。所以,我的NavigatorBar的getParent()返回的是ViewRootImpl,而不是传统的DecorView。

我的整体排查思路如下:

  1. TextView的实例是否有过变化?,经log查看,确实是同个实例。
  2. 是否有2个TextView存在,导致重叠? 经log查看,只有一个TextView。
  3. 在setText之后,打印text,看看打出来的text是不是最新的?经log查看,是最新的text,出现重叠是旧的text没被清除而留下来的残影。
  4. 调用invalidate()方法,有效? 不起作用。
  5. 尝试调用WindowManager的updateViewLayout,不起作用。(除非修改长宽,但这个会导致闪烁,用户体验不佳)
  6. 改成动态添加TextView,每次设置文本,都先remove现有的,再创建个新的。结果,仍然不起作用。
  7. 在网上搜索相同问题,查出来的文章,有的说,给根布局设置背景,比如,这篇:Android 绘制产生重影(重叠)。后面,经实验,设置个非透明的颜色,比如黑色,白色,都可以,但透明、半透明、不设置,都不行。如果可以给根布局设置纯色背景,那问题到此可以解决。
  8. 但像我这种业务场景的话就不行,原因是一旦某个app设置了跟我背景色不一致的颜色,就会出现颜色相左的问题 【详见图1】 ,如图所示,一条黑条横在页面中间。既然不行,那就继续排查。


  1. 重新屡了一下思路: 首先,Navigator是自定义View。自定义View就会走onDraw(),那么,我是不是可以考虑在onDraw里面做什么清除操作,或者自已绘制文本,不用TextView,就没这问题了?然后,我就重写Navigator(FrameLayout的子类)的onDraw()方法,并加log。但发现,onDraw方法,并没有被触发。后面,想起来,以前做继承自ViewGroup的自定义View时,也遇到过onDraw没被调用过,原因是ViewGroup默认是不触发draw的,源码如下所示:
 // 摘自 ViewGroup。private void initViewGroup() {// ViewGroup doesn't draw by defaultif (!isShowingLayoutBounds()) {setFlags(WILL_NOT_DRAW, DRAW_MASK);}.... 省略其它}

  1. FrameLayout直接继承自ViewGroup,默认也是没有调onDraw方法。问题搞清楚了,我们现在只需要加这行代码,就可以触发ViewGroup/FrameLayout/NavigatorBar的onDraw()方法:
   // 摘自 NavigatorBarinit {setWillNotDraw(false)}

  1. 现在,NavigatorBar的onDraw方法有触发了,但重叠问题仍然存在。此时,我第一个猜想是,这个重叠,既然是个残影,即上个绘制没有被清空导致的。那我加上清空画布的代码,是不是就OK了?然后,我就在onDraw方法里面加上清空画布的代码,如下所示:
  // 摘自 NavigatorBaroverride fun onDraw(canvas: Canvas) {super.onDraw(canvas)// 清空画布,解决TextView.setText时,出现内容重叠的问题。canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)}
  1. 现在,一切都OK了,残影也不见了。但还有个问题,就是虽然根布局的background设置了透明色,但显示出来的,仍然是有个黑色背景(也可能是其它背景,这个颜色猜测是与主题相关也可能无关)。【如果你的到这一步,没有这个问题,可以直接忽略后面的内容】
  2. 这个时候,就好办了,先把onDraw注释掉,发现,什么都不绘制,也会存在这条黑条。那么,就可以证明,这个黑色背景,不是我们的自定义View产生的。此时,我就去google查WindowManager背景色的相关资料,最后在stackoverflow的这个提问Android: Transparend background when using WindowManager里找到了答案,只需要在WindowManager.addView时,给WindowManager.LayoutParams设置format为透明即可,代码如下:
val layoutParams = WindowManager.LayoutParams()
// 设置背景透明
layoutParams.format = PixelFormat.TRANSLUCENT
  1. 【追加】后面经过测试,只要加了第13步的代码,即可解决设置背景透明后,文本会重叠的问题。第10、11步不是必要的。但留着做为参考,在某些类似的场景,也可以做为借鉴思路。

经验证,问题成功解决,关键代码就3行,一行触发onDraw,一行在onDraw里面清空画布,一行设置window为透明。这样,就可以给根布局设置透明背景,或不设置背景,也能解决文本重叠的问题。


最后,分享一下相关代码,共2个类,业务相关的已删除,只保留该博文主题相关的核心代码:

1. NavigatorBarManager.kt

/*** @author lyf* @date 2022/7/26 20:20* @describe 导航栏添加类,封装WindowManager添加View的逻辑*/
object NavigatorBarManager {// navigatorBar显示动画private const val animation = android.R.style.Animation_Dialog/*** 显示navigatorBar* @param navigatorBar vnavigatorBar的View*/fun showNavigatorBar(navigatorBar: View, height: Int = 60) {val windowManager = navigatorBar.context.getWindowManager()val layoutParams = WindowManager.LayoutParams()layoutParams.gravity = Gravity.BOTTOMlayoutParams.width = WindowManager.LayoutParams.MATCH_PARENTlayoutParams.height = heightlayoutParams.packageName = navigatorBar.context.packageNamelayoutParams.y = +navigatorBar.context.resources.getDimension(R.dimen.p30).toInt()layoutParams.windowAnimations = animation// 设置背景透明layoutParams.format = PixelFormat.TRANSLUCENT// 用这个级别的level,可以不挡到toastlayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOGlayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE.or(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL).or(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN).or(WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR).or(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)windowManager.addView(navigatorBar, layoutParams)}fun removeNavigatorBar(navigatorBar: View) {navigatorBar.context.getWindowManager().removeView(navigatorBar)}private fun Context.getWindowManager() =getSystemService(Context.WINDOW_SERVICE) as WindowManager}

2、NavigatorBar.kt

/*** @author lyf* @date 2022/7/26 20:20* @describe 底部导航栏*/
class NavigatorBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {companion object {private val TAG = NavigatorBar::class.java.simpleName}private var guideTv: TextView? = nullinit {// 自定义View,重写ViewGroup及其子类时,默认是不会去调onDraw方法。// 加上这个,就会触发onDraw,然后,在onDraw里面,清空画布,// 就可以解决,TextView.setText时,出现内容重叠的问题。setWillNotDraw(false)LayoutInflater.from(context).inflate(R.layout.layout_navigator_bar, this)guideTv = findViewById(R.id.tv_guide)setOnSystemUiVisibilityChangeListener {val isFullScreen =it.and(View.SYSTEM_UI_FLAG_FULLSCREEN) == View.SYSTEM_UI_FLAG_FULLSCREENval isHideNavigator =it.and(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == View.SYSTEM_UI_FLAG_HIDE_NAVIGATIONvisibility = if (isFullScreen || isHideNavigator) View.GONE else View.VISIBLELog.d(TAG, "UiVisibility=$it , isFullScreen=$isFullScreen")}}fun setGuideText(guideText: String?) {guideTv?.text = guideText.orEmpty()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)// 清空画布,解决TextView.setText时,出现内容重叠的问题。canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.OVERLAY)}}

Android setText 出现文本重叠的问题相关推荐

  1. 【Android开发】文本框、按钮、文本编辑框、提交登录、单选框

    程序中用到的图标,可以到EasyIcon去下载,样式和大小都比较全 界面功能 目录结构 样式浏览 Main java代码 package com.hanquan.helloworld;import a ...

  2. android开发文字滚动代码,android实现滚动文本效果

    本文实例为大家分享了android实现滚动文本效果的具体代码,供大家参考,具体内容如下 效果图 实现方法 直接上代码 首先是一个自定义layout,继承自FrameLayout public clas ...

  3. android settext 速度,Android TextView setText卡顿问题

    TextView 是经常使用控件之中的一个,最经常使用的方法是setText()  . 可是 我们在显示大量的文本的时候,使用setText还是会有一些性能的问题. 这篇文章 关于TextView的s ...

  4. android activity焦点,android启动activity文本框不获得焦点

    在开发中,常常会碰到这种情况,打开一个activity后,第一个文本框自动获得焦点,同时会弹出软键盘输入框,这样很影响用户体验,现在来看解决方法. 我们先来看看为什么会出现上述情况,原因很简单,文本框 ...

  5. android端富文本编辑器HRichEditor

    HRichEditor android端富文本编辑器 项目地址:https://github.com/huangdali/HRichEditor 使用方法见DEMO 所有功能已经实现,期待你的star ...

  6. android 文字选中朗读,Android开发之文本内容自动朗读功能实现方法

    本文实例讲述了Android开发之文本内容自动朗读功能实现方法.分享给大家供大家参考,具体如下: Android提供了自动朗读支持.自动朗读支持可以对指定文本内容进行朗读,从而发生声音:不仅如此,An ...

  7. android阅读软件怎么开发,Android平台手机文本阅读软件的开发与设计.doc

    Android平台手机文本阅读软件的开发与设计 Android平台手机文本阅读软件的开发与设计 摘要:本文主要阐述了在Android平台上十分流行的文本阅读软件的设计与实现,具有阅读字体大小调节.阅读 ...

  8. android的发音功能实现,Android开发之文本内容自动朗读功能实现方法

    本文实例讲述了Android开发之文本内容自动朗读功能实现方法.分享给大家供大家参考,具体如下: Android提供了自动朗读支持.自动朗读支持可以对指定文本内容进行朗读,从而发生声音:不仅如此,An ...

  9. Android Unicode与文本字符串互相转换

    Android Unicode与文本字符串互相转换 代码 完事 代码 kotlin /*** 补全length位,不够的在后面加0* @param str* @return*/fun upToNStr ...

最新文章

  1. How to recover from 'programmers burnout(转)
  2. JRebel 代理激活
  3. 一文理清面向对象(封装、继承、多态)+ 实战案例
  4. 原理详解与标准解法——蓝桥杯_2016年省赛B组 第七题 剪邮票(暴力+迷宫变形)
  5. 微软超融合私有云测试08-SCVMM部署之SQL Server与前置条件安装
  6. sensei鼠标测试软件,'黑科技'传感器打造出的FPS利器 - 赛睿Sensei 310 鼠标
  7. 负载报服务器无响应,nodejs HTTP服务器无法在高负载下处理大量响应
  8. IOS和Android测试分别有什么侧重点?
  9. iOS 对 HTTPS 证书链的验证
  10. 局域网文档服务器搭建,局域网服务器的搭建.pdf
  11. 读书寄语:蒲苇时韧、磐石永坚
  12. 技术人生:故事之八 OFFICE是软件打字机?
  13. 杀不死的人狼——我读《人月神话》(一)
  14. 【LAB4-Cisco】OSPF邻居建立过程与LSDB分析
  15. 2019年秋冬季读书笔记
  16. AI比赛-推荐系统(一)-新闻推荐03:多路召回【用不同策略分别召回部分候选集,然后把候选集混在一起供后续排序模型使用】【①、YoutubeDNN双塔召回;②、基于物品召回;③、基于用户召回】【天池】
  17. IOS 将文字写绘制成图片并转换为像素数据
  18. mysql 生成日历视图_mysql sql语句生成日历表
  19. 2021 ICPC Southeastern Europe Regional Contest Werewolves(树上背包)
  20. GOPS 2021 上海站 《钟炯恩 大数据云原生运维平台实践》

热门文章

  1. SPOJ PHT【二分】+SPOJ INUM【最小/大值重复】
  2. 每日一题10022-潜伏者
  3. 【算法竞赛入门练习题】判断给定的字符串中是否包含某个字符串
  4. Laravel表格操作Laravel-Excel
  5. 如何使用Camtasia去除纯色背景
  6. 按头安利 好看又实用的餐饮美食海外PPT模板素材看这里
  7. 【AppCan 开发者故事】Samlee:西北青年的移动开发梦
  8. 工业4.0区块链Startup 05
  9. ssm+JSP计算机毕业设计演出赛事购票系统xy8hy【源码、程序、数据库、部署】
  10. 一个前端初学者的学习总结