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 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
随机推荐
- Network Panel说明
Chrome Developer Tools:Network Panel说明 官方资料:Chrome Developer Tools: Network Panel 一.chrome Develop ...
- 【Visual C++】Windows GDI贴图闪烁解决方法
一般的windows 复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小的时候出现闪烁. 先来谈谈闪烁产生的原因 原因一:如果熟悉显卡原理的话,调用GDI函数向屏幕输出的 ...
- javascript (六) 引用外部js文件
外部的 JavaScript 也可以把脚本保存到外部文件中.外部文件通常包含被多个网页使用的代码. 外部 JavaScript 文件的文件扩展名是 .js. 如需使用外部文件,请在 <scrip ...
- windows时间函数
介绍 我们在衡量一个函数运行时间,或者判断一个算法的时间效率,或者在程序中我们需要一个定时器,定时执 行一个特定的操作,比如在多媒体中,比如在游戏中等,都会用到时间函数.还比如我们通过记 ...
- SLIC superpixel实现分析
http://infoscience.epfl.ch/record/149300这是SLIC算法的官网,网站有和SLIC相关的资源. SLIC主要运用K-means聚类算法进行超像素的处理,聚类算法中 ...
- [Android学习笔记]RelativeLayout的使用
RelativeLayout是相对布局控件,在屏幕适配的时候非常有用,在此记录一些它的常用属性 第一类:属性值为true或falseandroid:layout_centerHrizontal ...
- Java Thread.join()详解(转)
(1)join方法是可以中断的(2)在线程joiner在另一个线程t上调用t.join(),线程joiner将被挂起,直到线程t结束(即t.isAlive()返回为false)才恢复 package ...
- JavaBean在DAO设计模式简介
一.信息系统开发框架 客户层-------显示层-------业务层---------数据层---------数据库 1.客户层:客户层是client,简单的来说就是浏览器. 2.显示层:JSP/Se ...
- CheckBox和RadioButton以及RadioGroup
CheckBox:复选框 有两种状态 选中状态(true),未选状态(false) 属性 android:checked= "false"(表示该复选框未被选中) RadioGro ...
- [Linux]Centos git报错fatal: HTTP request failed
在使用git pull.git push.git clone会报类似例如以下的错误: error: The requested URL returned error: 401 Unauthorized ...