有的时候可能想在TextView中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个view。

当然,如果你不是view而是固定的图片,比如发信息时用表情图片替代特殊符号,那么实现起来会更加简单。又或许,你希望这个图片是可点击的。这里,笔者要介绍的就是怎么用一个自定义的ImageSpan来实现在文本里插入可点击的图片或View。

在此之前,如果你还不了解SpannableString.setSpan(),不了解LinkMovementMethod是什么,建议先看下笔者的解析TextView中的URL等指定特殊字符串与点击事件

首先,因为ImageSpan没有继承ClickableSpan,因此没有 onClick()方法。所以我写了个ClickableImageSpan 。

public abstract class ClickableImageSpan extends ImageSpan {

public ClickableImageSpan(Drawable b) {

super(b);

}

public abstract void onClick(View view);

}

同时,我们发现google提供的LinkMovementMethod只会执行ClickableSpan的onClick()方法.下面是LinkMovementMethod的onTouchEvent()的源码。这个方法是在我们点击Spanned的时候响应。

public boolean onTouchEvent(TextView widget, Spannable buffer,

MotionEvent event) {

int action = event.getAction();

if (action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_DOWN) {

int x = (int) event.getX();

int y = (int) event.getY();

x -= widget.getTotalPaddingLeft();

y -= widget.getTotalPaddingTop();

x += widget.getScrollX();

y += widget.getScrollY();

Layout layout = widget.getLayout();

int line = layout.getLineForVertical(y);

int off = layout.getOffsetForHorizontal(line, x);

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

if (link.length != 0) {

if (action == MotionEvent.ACTION_UP) {

link[0].onClick(widget);

} else if (action == MotionEvent.ACTION_DOWN) {

Selection.setSelection(buffer,

buffer.getSpanStart(link[0]),

buffer.getSpanEnd(link[0]));

}

return true;

} else {

Selection.removeSelection(buffer);

}

}

return super.onTouchEvent(widget, buffer, event);

}

发现这个方法其实就是通过坐标找到相应的Span。然后,当link数组不为空时,将会得到span并执行他的onClick()方法。这里我们注意到了这一句代码

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

这说明该方法只获得了ClickableSpan,因为如果我们直接使用系统的LinkMovementMethod类,是无法让ImageSpan响应点击事件的。。因为我们知道,ImageSpan没有继承ClickableSpan。所以,笔者写了一个ClickableMovementMethod

public class ClickableMovementMethod extends LinkMovementMethod {

private static ClickableMovementMethod sInstance;

public static ClickableMovementMethod getInstance() {

if (sInstance == null) {

sInstance = new ClickableMovementMethod();

}

return sInstance;

}

public boolean onTouchEvent(TextView widget, Spannable buffer,

MotionEvent event) {

int action = event.getAction();

if (action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_DOWN) {

int x = (int) event.getX();

int y = (int) event.getY();

x -= widget.getTotalPaddingLeft();

y -= widget.getTotalPaddingTop();

x += widget.getScrollX();

y += widget.getScrollY();

Layout layout = widget.getLayout();

int line = layout.getLineForVertical(y);

int off = layout.getOffsetForHorizontal(line, x);

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

ClickableImageSpan[] imageSpans = buffer.getSpans(off, off, ClickableImageSpan.class);

if (link.length != 0) {

if (action == MotionEvent.ACTION_UP) {

link[0].onClick(widget);

} else if (action == MotionEvent.ACTION_DOWN) {

Selection.setSelection(buffer,

buffer.getSpanStart(link[0]),

buffer.getSpanEnd(link[0]));

}

return true;

} else if (imageSpans.length != 0) {

if (action == MotionEvent.ACTION_UP) {

imageSpans[0].onClick(widget);

} else if (action == MotionEvent.ACTION_DOWN) {

Selection.setSelection(buffer,

buffer.getSpanStart(imageSpans[0]),

buffer.getSpanEnd(imageSpans[0]));

}

return true;

} else {

Selection.removeSelection(buffer);

}

}

return false;

}

}

只是做了很小的改动,这样,这个类既可以支持ClickableSpan也可以支持我们自己写的ClickableImageSpan。

到此为止,一个可点击的ImageSpan就完成了。剩下的步骤就跟实现文字样式的方式一样,首先new一个SpannableString传入文本,然后找到你需要放置ImageSpan的位置(一般使用正则表达式),接着new一个ClickableImageSpan传入图片,通过SpannableString的setSpan()方法传入ClickableImageSpan对象。最后别忘了TextView调用setMovementMethod时,传入的是我们的ClickableMovementMethod.getInstance()方法。具体代码实现参照文字样式那边的,稍作修改即可。具体的笔者不再贴这部分的代码了。

那么,如果我们不是传一个简单的图片,而是需要显示一个定制的View,应该怎么做呢。其实只要把View转化成Drawable就好,下面是主要的实现代码:

private BitmapDrawable createDrawble(Context ctx, String content) {

View view = LayoutInflater.from(ctx).inflate(R.layout.viewt, null);

((TextView) view.findViewById(R.id.tv_content)).setText(content);

int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);

view.measure(spec, spec);

view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

Canvas c = new Canvas(b);

c.translate(-view.getScrollX(), -view.getScrollY());

view.draw(c);

view.setDrawingCacheEnabled(true);

Bitmap cacheBmp = view.getDrawingCache();

Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);

view.destroyDrawingCache();

return new BitmapDrawable(ctx.getResources(), viewBmp);

}

public void filter(Spannable sp) {

/**

.....此处省略.

**/

BitmapDrawable bd = createDrawble(tv.getContext(), sp.toString); bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());

MyClickableImageSpan span = new MyClickableImageSpan(bd,text);

sp.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

}

createDrawble()方法是通过View的getDrawingCache()方法将一个View转化成BItmap,然后在获得BitmapDrawable 后别忘了调用setBounds(),这个方法是决定图片的大小,如果不设置,那么图片长宽都为0! 当然,你如果嫌显示的效果太大或太小,也可以通过这个方法调整图片大小。其他步骤相信大家看过笔者的 解析TextView中的URL等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。因此笔者不再赘述了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

android 自定义span_Android自定义可点击的ImageSpan并在TextView中内置View相关推荐

  1. android角度计算器,Calckit高级版一款安卓多功能计算器,内置高度定制的科学计算器...

    前言 手机中的计算器功能你多久用一次呢,我相信对于多数人来说计算器是一个低频使用软件. 其原因可能是用不上计算功能也很可能是单一的计算功能并不能满足你对于计算的需求. 那么如果现在有一款可以完成中学到 ...

  2. Android 系统工程中内置APK

    有些时候需要将一些自已写的apk内置到系统,也就是第三方内置.内置的原因一部是让apk拥有系统级权限,一部分为了内置apk定制刷机包. 系统级: 1.先在package/apps中创建一个文件夹,如H ...

  3. python自定义类的属性_我可以将自定义方法/属性添加到内置Python类型吗?

    For example-say I want to add a helloWorld() method to Python's dict type. Can I do this? JavaScript ...

  4. 8.Hive基础—函数—系统内置函数、常用内置函数、自定义函数、自定义UDF函数、自定义UDTF函数

    本文目录如下: 第8章 函数 8.1 系统内置函数 8.2 常用内置函数 8.2.1 空字段赋值 8.2.2 CASE WHEN THEN ELSE END 8.2.3 行转列 8.2.4 列转行 8 ...

  5. Struts2内置拦截器和自定义拦截器

    内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截器,拦截器才可以正常 ...

  6. java 多重注解_Java注解-元数据、注解分类、内置注解和自定义注解

    大家好,我是乐字节的小乐,上次说过了Java多态的6大特性|乐字节,接下来我们来看看Java编程里的注解. Java注解有以下几个知识点:元数据 注解的分类 内置注解 自定义注解 注解处理器 Serv ...

  7. 2字节取值范围_Java注解-元数据、注解分类、内置注解和自定义注解|乐字节

    大家好,我是乐字节的小乐,上次说过了Java多态的6大特性|乐字节,接下来我们来看看Java编程里的注解. Java注解有以下几个知识点: 元数据 注解的分类 内置注解 自定义注解 注解处理器 Ser ...

  8. ASP.NET 中验证的自定义返回和统一社会信用代码的内置验证实现

    目录 项目演示 创建项目 创建用户注册信息接收类 创建测试 Controller 执行测试 统一错误返回 自定义验证规则 验证规则介绍 编码实现 最后 References 本文介绍 ASP.NET ...

  9. 【Android 12 AOSP学习】Android内置第三方apk到系统

    这篇文章将介绍如何在Android系统中内置第三方apk 一.准备工作 编译好的Android源码 Android apk文件:也就是安卓应用安装包 二.步骤 (1)在源码packages/apps目 ...

最新文章

  1. iis 无法连接mysql_远程无法连接SQL2000及MySQL的原因和解决办法
  2. 砥砺前行,比特币现金周年国际峰会正式召开
  3. 十几年后我才知道,嫁了一个硬核老公
  4. linux启动顺序详解
  5. 谁的bug? 正则 拷贝和粘贴 regulator工具
  6. Linux目录结构FHS
  7. 建模准备一定要做的这几件事
  8. php ci提交表单验证,ci表单验证代码
  9. 【Linux】一步一步学Linux——dpkg-split命令(274)
  10. python win+r时不成功_Win与R(不使用Anaconda的情况下)
  11. mongoDB的副本机制
  12. 将所有文件从目录复制到Python中的另一个目录
  13. NLP《语言模型(三)-- 基于循环神经网络的RNNLM语言模型》
  14. 吴恩达深度学习5.2练习_Sequence Models_Operations on word vectors
  15. 【王道操作系统笔记】操作系统的发展和分类
  16. 什么是Semantic(语义)?从互联网爬虫、自动驾驶到对话机器人
  17. php获取客户端IP
  18. sqlite库学习(7)sqlite读取
  19. 回归分析结果表格怎么填_excel回归分析结果解读
  20. 新手操作低客单价时常见的误区有哪些?

热门文章

  1. 关于windows的进程处理(七)
  2. 有关nginx location规则
  3. 低版本系统兼容的ActionBar(四)添加Tab+添加自定义的Tab视图+Fragment
  4. 文件目录Android SDK目录结构
  5. 只有与众不同才能生存
  6. Linux VI编辑器
  7. Azure CosmosDB (3) 选择适当的一致性级别
  8. 一个浙江人的23条经典经商法则
  9. SpringCloud Eureka Client和Server侧配置及Eureka高可用配置
  10. nginx伪静态之try_files和rewrite讲解