热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidImageSpan图文混排

本文示例项目地址对于Android开发者,部分场景下需要实现图文混排的排版方式(即在一个UI控件中同时显示图片与文字),较为

本文示例项目地址

对于 Android 开发者,部分场景下需要实现图文混排的排版方式(即在一个UI 控件中同时显示图片与文字),较为常见的场景如社交类 App 中对文字与表情的展示。

实现图文混排,主要有以下方法:

  1. 使用 WebView 加载 HTML
  2. 使用 Html.fromHtml(String source, int flags)获取 Spanned 对象后,通过 TextView 展示
  3. 使用 ImageSpan 展示图片

实际上方法 2 与方法 3 是相似的,而方法 3 对于不熟悉 HTML 的 Android 开发者更为友好并且提供了更高的自由度。本文主要分析方法 3 的基本使用与自定义绘制。

基本使用

ImageSpanDynamicDrawableSpan 的直接子类,开发者通过 SpannableString/SpannableStringBuildersetSpan() 方法将字符串的指定部分设置为由 ImageSpan 构造方法传入的图片。ImageSpan 的基本使用可以参考如下代码,完整代码可以查看示例项目的 BasicImageSpanActivity 类。

Drawable fuDrawable = getResources().getDrawable(R.drawable.image_fu);
fuDrawable.setBounds(0, 0, textView.getLineHeight(),textView.getLineHeight());
ImageSpan imageSpan = new ImageSpan(fuDrawable);
spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(spannableString);

对齐方式

图片与文字的位置关系需要通过设置 ImageSpan 的对齐方式来实现。通过在 ImageSpan 的构造方法中传入 verticalAlignment 参数可以实现对图片与文字的纵向对齐方式的设置。ImageSpan 为开发者提供了两种对齐方式:

  1. ALIGN_BOTTOM:图片底部与所在行底部对齐。
  2. ALIGN_BASELINE:图片底部与文字基线对齐。文字基线(baseline)是字体排印学的概念之一,其具体含义可以参考 维基百科的基线词条。同时,开发者可以将其简单的理解为文字的“重心”位置。

自定义绘制

对于图文混排,不少场景需要图片与文字居中对齐(即图片的中线与文字的中线重合),而 ImageSpan 并未提供这样的对齐方式。开发者可以通过重写(Override) ImageSpan 父类 DynamicDrawableSpandraw 方法来实现自己的绘制逻辑,从而实现图片与文字的居中对齐。DynamicDrawableSpandraw 方法的实现如下:

@Overridepublic void draw(Canvas canvas, CharSequence text,int start, int end, float x, int top, int y, int bottom, Paint paint) {Drawable b = getCachedDrawable();canvas.save();int transY = bottom - b.getBounds().bottom;if (mVerticalAlignment == ALIGN_BASELINE) {transY -= paint.getFontMetricsInt().descent;}canvas.translate(x, transY);b.draw(canvas);canvas.restore();}

方法中 bottom 为图片所在行底部坐标(以 TextView 左上角为原点),DynamicDrawableSpan 根据 mVerticalAlignment 的值,使用 canvastranslate 方法,将画布移动对应的距离(ALIGN_BOTTOM 移动 bottomdrawable 底部差值的距离,ALIGN_BASELINE 在前者的基础上减去 descent 值,descent 为基线到行底部的距离),来实现对应的对齐方式。开发者可以参考 DynamicDrawSpan 的实现方式,来实现居中对齐乃至更多的绘制逻辑。居中对齐的实现代码可以参考如下代码,完整代码可以参考示例项目中 CustomImageSpandraw 方法。

@Overridepublic void draw(Canvas canvas, CharSequence text, int start, int end,float x, int top, int y, int bottom, Paint paint) {Drawable b = getDrawable();Paint.FontMetricsInt fm = paint.getFontMetricsInt();int transY = (y + fm.descent + y + fm.ascent) / 2 - (b.getBounds().bottom + b.getBounds().top) / 2;canvas.save();canvas.translate(x, transY);b.draw(canvas);canvas.restore();}

代码中 y 为基线的纵坐标(以 TextView 左上角为坐标原点),fm.descent 为基线至文字底部的距离(为正值),fm.ascent 为基线至文字顶部的距离(为负值),y + fm.descent 的值为文字底部纵坐标,y + fm.ascent 的值为文字顶部纵坐标,二者相加除以2则得到了文字纵向中点的纵坐标,(b.getBounds().bottom + b.getBounds().top) / 2 则为 drawable 绘制区域纵向中点的纵坐标,而二者的差值即为实现居中对齐画布的纵向偏移。

为验证代码的效果,笔者使用一个高16、宽48的黑色(#000000)色块作为传入 ImageSpandrawable,展示了在不同对齐方式下图文混排的效果,该示例的完整代码可以参考示例项目的 CustomImageSpanActivity 类,代码的运行效果如下图:

为了方便读者对比不同对齐方式,截图中开启了开发者选项中的显示布局边界

由于开发者可以在 DynamicDrawableSpandraw 方法中实现自己的绘制逻辑,使用该方案来实现图文混排给予了开发者极大的自由度,开发者可以对与文字混排的图片进行更为细致的排版,本文仅以较为常见居中对齐作为示例,相信读者可以实现更多更好的图文混排绘制逻辑。

转:https://juejin.im/post/5a5f5e095188253dc33203b4



推荐阅读
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • [c++基础]STL
    cppfig15_10.cppincludeincludeusingnamespacestd;templatevoidprintVector(constvector&integer ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • 实验九:使用SharedPreferences存储简单数据
    本实验旨在帮助学生理解和掌握使用SharedPreferences存储和读取简单数据的方法,包括程序参数和用户选项。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 两个条件,组合控制#if($query_string~*modviewthread&t(&extra(.*)))?$)#{#set$itid$1;#rewrite^ ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤
    开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤 ... [详细]
  • 本文详细解析了Autofac在高级应用场景中的具体实现,特别是如何通过注册泛型接口的类来优化依赖注入。示例代码展示了如何使用 `builder.RegisterAssemblyTypes` 方法,结合 `typeof(IEventHandler).Assembly` 和 `Where` 过滤条件,动态注册所有符合条件的类,从而简化配置并提高代码的可维护性。此外,文章还探讨了这一方法在复杂系统中的实际应用及其优势。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
author-avatar
yema狂想曲
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有