Android自定义可点击的ImageSpan并在TextView中内置View
有的时候可能想在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等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。因此笔者不再赘述了。
以上就是本文的全部内容,希望对大家的学习有所帮助。
- 如何选择聚类模块数目
- 谁能告诉我,这数据测毁了么?
- 计算资源及编程-仅针对生信人员
- 从WGS测序得到的VCF文件里面提取位于外显子区域的【直播】我的基因组84
- 基因组重测序的unmapped reads assembly探究 【直播】我的基因组86
- Centos 下非 Root 安装 Microsoft R Open
- 下载TCGA所有癌症的maf文件做signature分析
- 比对NR库看看物种分布【直播】我的基因组88
- 探究某个基因的外显子覆盖度情况【直播】我的基因组87
- PHP底层的运行机制与原理
- CHROME开发者工具的小技巧
- 48条高效率的PHP优化写法
- 生信蓝领,一个不舍得分享的高通量数据分析框架
- 为什么in_array(0, ['a', 'b', 'c'])返回true
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- HTTP协议详解,浏览器和服务器交互过程详解
- 京东网页(动态)搭建,利用jquery实现
- 京东购物车(动态)网页搭建,利用JavaScript实现
- 你不知道的 TypeScript 泛型(万字长文,建议收藏)
- JavaScript中的函数、对象
- 京东购物车网页(静态)搭建
- MyBatis注解详解
- MyBatis完整环境搭建步骤
- JDBC常见操作总结
- Oracle数据中的序列、索引、视图、事务操作详解以及rowid 和 rownum的简单介绍
- Oracle子查询详解
- SQL92、SQL99中的多表连接查询区别
- Oracle中的分组查询与DML
- Oracle中的基本SQL知识
- 自定义 ESLint 规则,让代码持续美丽