android平台TextView使用ImageSpan画廊GIF图像
android-gif-drawable(https://github.com/koral--/android-gif-drawable/releases)开源项目---是一个蛮不错的android
gif显示实现.本文在android-gif-drawable基础上介绍怎样实现TextView、EditText上展示Gif动态图。
/**
* Sets a drawable as the content of this ImageView.
*
* @param drawable The drawable to set
*/
public void setImageDrawable(Drawable drawable) {
if (mDrawable != drawable) {
...
updateDrawable(drawable);
...
}
} private void updateDrawable(Drawable d) {
if (mDrawable != null) {
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
}
mDrawable = d;
if (d != null) {
d.setCallback(this);
if (d.isStateful()) {
d.setState(getDrawableState());
}
d.setLevel(mLevel);
d.setLayoutDirection(getLayoutDirection());
d.setVisible(getVisibility() == VISIBLE, true);
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyColorMod();
configureBounds();
} else {
mDrawableWidth = mDrawableHeight = -1;
}
}
也就是说,ImageView在设置其src时。清空旧mDrawable的callback,然后将新设置的src drawable的callback设置为ImageView本身。
public class GifImageSpan extends ImageSpan{
private Drawable mDrawable = null;
public GifImageSpan(Drawable d) {
super(d);
mDrawable = d;
}
public GifImageSpan(Drawable d, int verticalAlignment) {
super(d, verticalAlignment);
mDrawable = d;
}
@Override
public Drawable getDrawable() {
return mDrawable;
}
}
public class GifEditText extends EditText {
private GifSpanChangeWatcher mGifSpanChangeWatcher;
public GifEditText(Context context) {
super(context);
initGifSpanChangeWatcher();
}
public GifEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initGifSpanChangeWatcher();
}
public GifEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGifSpanChangeWatcher();
}
private void initGifSpanChangeWatcher() {
mGifSpanChangeWatcher = new GifSpanChangeWatcher(this);
addTextChangedListener(mGifSpanChangeWatcher);
}
@Override
public void setText(CharSequence text, BufferType type) {
CharSequence oldText = null;
try {
//EditText的默认mText为""。是一个String。但getText()强转为Editable,尼玛。仅仅能try/catch了
oldText = getText();
//首先清空全部旧GifImageSpan的callback和oldText上的GifSpanChangeWatcher
if (!TextUtils.isEmpty(oldText) && oldText instanceof Spannable) {
Spannable sp = (Spannable) oldText;
final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class);
final int count = spans.length;
for (int i = 0; i < count; i++) {
spans[i].getDrawable().setCallback(null);
}
final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class);
final int count1 = watchers.length;
for (int i = 0; i < count1; i++) {
sp.removeSpan(watchers[i]);
}
}
} catch (Exception e) {
}
if (!TextUtils.isEmpty(text)) {
if (!(text instanceof Editable)) {
text = new SpannableStringBuilder(text);
}
}
if (!TextUtils.isEmpty(text) && text instanceof Spannable) {
Spannable sp = (Spannable) text;
//设置新text中全部GifImageSpan的callback为当前EditText
final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class);
final int count = spans.length;
for (int i = 0; i < count; i++) {
spans[i].getDrawable().setCallback(this);
}
//清空新text上的GifSpanChangeWatcher
final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class);
final int count1 = watchers.length;
for (int i = 0; i < count1; i++) {
sp.removeSpan(watchers[i]);
}
if (mGifSpanChangeWatcher == null) {
mGifSpanChangeWatcher = new GifSpanChangeWatcher(this);
}
//设置新text上的GifSpanChangeWatcher
sp.setSpan(mGifSpanChangeWatcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT));
}
super.setText(text, type);
}
}
public class GifSpanChangeWatcher implements SpanWatcher, TextWatcher{
private Drawable.Callback mCallback;
public GifSpanChangeWatcher(Drawable.Callback callback) {
mCallback = callback;
}
public void onSpanChanged(Spannable buf, Object what, int s, int e, int st, int en) {
//do nothing
}
public void onSpanAdded(Spannable buf, Object what, int s, int e) {
//设置callback
if (what instanceof GifImageSpan) {
((GifImageSpan)what).getDrawable().setCallback(mCallback);
}
}
public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
//清空callback
if (what instanceof GifImageSpan) {
((GifImageSpan)what).getDrawable().setCallback(null);
}
}
@Override
public void afterTextChanged(Editable s) {
if (s != null) {
s.setSpan(this, 0, s.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
}
@Override
protected boolean verifyDrawable(Drawable dr) {
return mDrawable == dr || super.verifyDrawable(dr);
}
@Override
protected boolean verifyDrawable(Drawable who) {
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop ||
who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom ||
who == mDrawables.mDrawableStart || who == mDrawables.mDrawableEnd;
}
return verified;
}
直接上代码
public class GifEditText extends EditText {
private GifImageSpan getImageSpan(Drawable drawable) {
GifImageSpan imageSpan = null;
CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
GifImageSpan[] spans = spanned.getSpans(0, text.length(), GifImageSpan.class);
if (spans != null && spans.length > 0) {
for (GifImageSpan span : spans) {
if (drawable == span.getDrawable()) {
imageSpan = span;
}
}
}
}
}
return imageSpan;
}
}
getImageSpan()方法通过getSpans()获取全部的GifImageSpan。然后对照drawable,返回对应的GifImageSpan。
最后。操作3)更新View显示。相同參考下TextView
@Override
public void invalidateDrawable(Drawable drawable) {
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getBounds();
int scrollX = mScrollX;
int scrollY = mScrollY; // IMPORTANT: The coordinates below are based on the coordinates computed
// for each compound drawable in onDraw(). Make sure to update each section
// accordingly.
final TextView.Drawables drawables = mDrawables;
if (drawables != null) {
if (drawable == drawables.mDrawableLeft) {
final int compoundPaddingTop = getCompoundPaddingTop();
final int compoundPaddingBottom = getCompoundPaddingBottom();
final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; scrollX += mPaddingLeft;
scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2;
} else if (drawable == drawables.mDrawableRight) {
final int compoundPaddingTop = getCompoundPaddingTop();
final int compoundPaddingBottom = getCompoundPaddingBottom();
final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight);
scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2;
} else if (drawable == drawables.mDrawableTop) {
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2;
scrollY += mPaddingTop;
} else if (drawable == drawables.mDrawableBottom) {
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2;
scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom);
}
} invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
}
}
计算compoundDrawable位置栏,然后运行invalidate。
对于GifEditText貌似也能够类似操作,依据GifImageSpan的start、end计算其位置栏,然后运行invalidate()。只是计算过程太过复杂了。只是android4.4的TextView提供这种方法void
invalidateRegion(int start, int end, boolean invalidateCursor) 方法用于刷新start和end之间的区域,但还是蛮复杂的看的人眼花缭乱。研究了下这种方法终于是由谁调用的。
@Override
public void invalidateDrawable(Drawable drawable) {
GifImageSpan imageSpan = getImageSpan(drawable);
Log.e("", "invalidateDrawable imageSpan:" + imageSpan);
if (imageSpan != null) {
CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
if (text instanceof Editable) {
Log.e("", "invalidateDrawable Editable:");
Editable editable = (Editable)text;
int start = editable.getSpanStart(imageSpan);
int end = editable.getSpanEnd(imageSpan);
int flags = editable.getSpanFlags(imageSpan); editable.setSpan(imageSpan, start, end, flags);
}
} } else {
super.invalidateDrawable(drawable);
}
}
直接又一次设置该ImageSpan就可以触发ChangeWatcher::onSpanChanged()回调。也就会马上刷新其区域和cursor。
public class GifSpanTextView extends GifTextView {
private GifSpanChangeWatcher mGifSpanChangeWatcher;
public GifSpanTextView(Context context) {
super(context);
initGifSpanChangeWatcher();
}
public GifSpanTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initGifSpanChangeWatcher();
}
public GifSpanTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGifSpanChangeWatcher();
}
private void initGifSpanChangeWatcher() {
mGifSpanChangeWatcher = new GifSpanChangeWatcher(this);
addTextChangedListener(mGifSpanChangeWatcher);
}
@Override
public void setText(CharSequence text, BufferType type) {
type = BufferType.EDITABLE;
CharSequence oldText = getText();
if (!TextUtils.isEmpty(oldText) && oldText instanceof Spannable) {
Spannable sp = (Spannable) oldText;
final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class);
final int count = spans.length;
for (int i = 0; i < count; i++) {
spans[i].getDrawable().setCallback(null);
}
final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class);
final int count1 = watchers.length;
for (int i = 0; i < count1; i++) {
sp.removeSpan(watchers[i]);
}
}
if (!TextUtils.isEmpty(text) && text instanceof Spannable) {
Spannable sp = (Spannable) text;
final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class);
final int count = spans.length;
for (int i = 0; i < count; i++) {
spans[i].getDrawable().setCallback(this);
}
final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class);
final int count1 = watchers.length;
for (int i = 0; i < count1; i++) {
sp.removeSpan(watchers[i]);
}
if (mGifSpanChangeWatcher == null) {
mGifSpanChangeWatcher = new GifSpanChangeWatcher(this);;
}
sp.setSpan(mGifSpanChangeWatcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT));
}
super.setText(text, type);
}
private GifImageSpan getImageSpan(Drawable drawable) {
GifImageSpan imageSpan = null;
CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
GifImageSpan[] spans = spanned.getSpans(0, text.length(), GifImageSpan.class);
if (spans != null && spans.length > 0) {
for (GifImageSpan span : spans) {
if (drawable == span.getDrawable()) {
imageSpan = span;
}
}
}
}
}
return imageSpan;
}
@Override
public void invalidateDrawable(Drawable drawable) {
GifImageSpan imageSpan = getImageSpan(drawable);
if (imageSpan != null) {
CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
if (text instanceof Editable) {
Editable editable = (Editable)text;
int start = editable.getSpanStart(imageSpan);
int end = editable.getSpanEnd(imageSpan);
int flags = editable.getSpanFlags(imageSpan);
editable.removeSpan(imageSpan);
editable.setSpan(imageSpan, start, end, flags);
}
}
} else {
super.invalidateDrawable(drawable);
}
}
}
设置其android:editable="true"或正上方setText(CharSequence text, BufferType type)将type设置BufferType.EDITABLE。
android平台TextView使用ImageSpan画廊GIF图像的更多相关文章
- Android的TextView使用Html来处理图片显示、字体样式、超链接等
一.[Android实例]实现TextView里的文字有不同颜色 转eoe:http://www.eoeandroid.com/thread-4496-1-1.html import android. ...
- 我的Android进阶之旅------> Android在TextView中显示图片方法
面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包括图像的文本信息).并简要说明实现方法. 答案:Android SDK支持例如以下显示富文本信息的方式. 1.使用T ...
- 我的Android进阶之旅------> Android在TextView中显示图片方法
面试题:请说出Android SDK支持哪些方式显示富文本信息(不同颜色.大小.并包含图像的文本信息),并简要说明实现方法. 答案:Android SDK支持如下显示富文本信息的方式. 1.使用Tex ...
- [译]:Xamarin.Android平台功能——位置服务
返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...
- android平台手电筒开发源代码
android平台手电筒开发源代码,AndroidManifest.xml文件的入口是startapp,这个文件没上传上来,大家可以自己写. 1. [代码]android 1 2 3 4 5 6 7 ...
- [Android教程]TextView使用SpannableString设置复合文本
TextView通常用来显示普通文本,但是有时候需要对其中某些文本进行样式.事件方面的设置.Android系统通过SpannableString类来对指定文本进行相关处理,具体有以下功能: 1.Bac ...
- OpenCV在Android平台上的应用
今年8月份, OpenCV 2.3.1发布了. 虽然从2.2开始, OpenCV就号称支持Android平台, 但真正能让OpenCV在Android上运行起来还是在2.3.1版本上. 在这个版本上, ...
- dp和px,那些不得不吐槽的故事——Android平台图
http://blog.sina.com.cn/s/blog_6499f8f101014ipq.html 一个优秀的手机软件,不仅要有精巧的功能,流畅的速度,让人赏心悦目的UI也往往是用户选择的重要理 ...
- 【转】Android平台下利用zxing实现二维码开发
http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
随机推荐
- Github上四种Lisp方言的流行度 | 肉山博客 (Wenshan's Blog)
Github上四种Lisp方言的流行度 | 肉山博客 (Wenshan's Blog) Github上四种Lisp方言的流行度
- tracert路由跟踪命令分析判断
可能有的会使用路由跟踪命令 ,可是却看不太明确显示出来的结果.结合我的来说明一下. (1)tracert命令介绍 tracert是路由跟踪命令,通过该命令的返回结果,能够获得本地到达目标主机所经过的网 ...
- table显示边框问题,隐藏行线,列线
只显示上边框 <table frame=above> 只显示下边框 <table frame=below> 只显示左.右边框 <table frame=vsides> ...
- Delphi Windows API判断文件共享锁定状态(使用OpenFile来判断)
一.概述 锁是操作系统为实现数据共享而提供的一种安全机制,它使得不同的应用程序,不同的计算机之间可以安全有效地共享和交换数据.要保证安全有效地操作共享数据,必须在相应的操作前判断锁的类型,然后才能确定 ...
- 14.3.5.1 An InnoDB Deadlock Example
14.3.5 Deadlocks in InnoDB 14.3.5.1 An InnoDB Deadlock Example 14.3.5.2 Deadlock Detection and Rollb ...
- C++ STL中Map的相关排序操作:按Key排序和按Value排序 - 编程小径 - 博客频道 - CSDN.NET
C++ STL中Map的相关排序操作:按Key排序和按Value排序 - 编程小径 - 博客频道 - CSDN.NET C++ STL中Map的相关排序操作:按Key排序和按Value排序 分类: C ...
- C语言数据结构----递归的应用(八皇后问题的具体流程)
本节主要讲八皇后问题的基本规则和递归回溯算法的实现以及具体的代码实现和代码分析. 转载请注明出处.http://write.blog.csdn.net/postedit/10813257 一.八皇后问 ...
- 【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现
一.dm9000_init 打印出驱动的版本,注冊dm9000_driver驱动,将驱动加入到总线上.运行match,假设匹配,将会运行probe函数. 1 static int __init 2 d ...
- Java-WebSocket 项目的研究(三) WebSocketClient 类 具体解释
通过之前两篇文章 Java-WebSocket 项目的研究(一) Java-WebSocket类图描写叙述 Java-WebSocket 项目的研究(二) 小试身手:client连接server并发送 ...
- MongoDB在实际项目
MongoDB在实际项目中的使用 MongoDB简介 MongoDB是近些年来流行起来的NoSql的代表,和传统数据库最大的区别是支持文档型数据库.当然,现在的一些数据库通过自定义复合类型,可变长 ...