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 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
随机推荐
- Opencv笔记(1) 命名规则数据结构(CvMat,...)
网上搜索了很多,检查中发现的信息劣势,检查源代码 同Cv为类的开始.包含详细的数据不(仅存储指针) CvMat typedef struct CvMat { int type; int step; / ...
- 让动态创建的ActiveX控件响应Windows消息
当我们通过 CWnd::CreateControl() 动态创建 ActiveX 控件时, Windows 消息并不会被发送给我 们的由 CWnd 派生得控件类.例如,即使我们为 WM_KIL ...
- 设定十分钟android在状态栏上集成的开源project推荐
1.前言 于android kitkat 有一个新功能可以设置背景的手机状态栏,让手机风的整个界面格是一致的,它看起来非常酷,在今年的google i/o向上android l这样的风格.来如今看我们 ...
- 依据不同的操作系统读取配置文件/java读取属性文件代码
package cn.com.css.common.util; /** * @brief OSEnum.java 操作系统的枚举 * @attention * @author 涂作权 * @d ...
- Keywords Search (ac 自己主动机)
Keywords Search Problem Description In the modern time, Search engine came into the life of everybod ...
- ThinkPHP框架模型连贯操作(八)
原文:ThinkPHP框架模型连贯操作(八) Thinkphp的连贯操作使用起来也是很灵活: *可能这里有的mysql函数没全部罗列出来,大家可以举一反三,形式雷同 一.常用连贯操作 1.where ...
- NetBeans工具学习之道:NetBeans的(默认)快捷键
没什么好介绍的,是netbeans的快捷键,比較全面.看到好多坛子里还在问eclipse下的这个快捷键怎么netbeans下没有呢.曾经收集的,如今列在以下: 事实上,在当前安装的netbeans的 ...
- Struts工作机制图+OGNL+EL+值栈(Map,对象栈)
struts 值栈 通过get set方法 方便的获取,设置属性值 比如从jsp页面传来的參数...从Action设置jsp所要回显的内容 注意EL表达式,struts2对request进 ...
- hdu 1240 Asteroids!(BFS)
题目链接:点击链接 简单BFS,和二维的做法相同(需注意坐标) 题目大意:三维的空间里,给出起点和终点,“O”表示能走,“X”表示不能走,计算最少的步数 #include <iostream&g ...
- IIS 添加mime 支持 apk,exe,.woff,IIS MIME设置 ,Android apk下载的MIME 设置 苹果ISO .ipa下载mime 设置
原文:IIS 添加mime 支持 apk,exe,.woff,IIS MIME设置 ,Android apk下载的MIME 设置 苹果ISO .ipa下载mime 设置 站点--右键属性--http头 ...