博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android平台TextView使用ImageSpan画廊GIF图像
阅读量:6282 次
发布时间:2019-06-22

本文共 14937 字,大约阅读时间需要 49 分钟。

 

(https://github.com/koral--/android-gif-drawable/releases)开源项目---是一个蛮不错的android gif显示实现.本文在基础上介绍怎样实现TextView、EditText上展示Gif动态图。

网上有蛮多介绍这个框架使用的文章,比方http://www.open-open.com/lib/view/open1404888098200.html。
核心类GifDrawable间隔一定时间读取下一帧数据,然后运行invalidateSelf()----》CallBack::invalidateDrawable()---》View::verifyDrawable()和View::invalidate(),该帧数据刷新流程就运行结束。

而android-gif-drawable框架眼下已支持GifImageView、GifImageButton、GifTextView三个android widget,且 GifImageView、GifImageButton支持对src和backgroud设置Gif,而GifTextView对支持backgroud和CompoundDrawables设置Gif。
如今非常多app都支持Gif表情。但貌似还没有一个app对输入框(等)支持GIF。而基本全部的表情图片(包含Emoji)都是使用ImageSpan实现的。但默认的ImageSpan是无法支持GIF的。
參考android-gif-drawable框架中gif帧数据刷新流程,要支持GIF须要考虑并完毕以下三个操作:
1)对ImageSpan中的GifDrawable,何时设置其Callback,又何时清空该Callback,眼下TextView、ImageSpan和Spaned都没有设置Callback的地方。我们须要找一个合适的地方将TextView设置为GifDrawable的Callback;
2)在TextView::invalidateDrawable()中实现对GifDrawable的校验,即验证该GifDrawable是TextView的内容,须要刷新;
3)在TextView::invalidateDrawable()中实现怎样刷新TextView显示;
首先对于1)。我们參考下ImageView和TextView实现。ImageView的src drawable相应实现例如以下:
/**     * 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本身。
同理。TextView对于CompoundDrawables的callback处理也是在setCompoundDrawables()时。

而ImageSpan须要在什么时机设置GifDrawable的callback呢,
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			}}
也就是,在setText()和onSpanAdded()、onSpanRemoved()中运行操作(1)
然后,对于2),相同參考ImageView和TextView
@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之间的区域,但还是蛮复杂的看的人眼花缭乱。研究了下这种方法终于是由谁调用的。

invalidateRegion()<<---invalidateCursor()<<---spanChange()<<---ChangeWatcher::onSpanChanged()、ChangeWatcher::onSpanAdded()、ChangeWatcher::onSpanRemoved()
也就是说,仅仅要TextView内容中span发生变化都会触发invalidateRegion()来刷新相应区域和cursor。

@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。
大功告成。执行ok。

上面是对EditText的实现,针对TextView实现略微有点差别
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。

转载地址:http://acxva.baihongyu.com/

你可能感兴趣的文章
程序员修炼之道读后感2
查看>>
DWR实现服务器向客户端推送消息
查看>>
js中forEach的用法
查看>>
Docker之功能汇总
查看>>
!!a标签和button按钮只允许点击一次,防止重复提交
查看>>
(轉貼) Eclipse + CDT + MinGW 安裝方法 (C/C++) (gcc) (g++) (OS) (Windows)
查看>>
还原数据库
查看>>
作业调度框架 Quartz.NET 2.0 beta 发布
查看>>
mysql性能的检查和调优方法
查看>>
项目管理中的导向性
查看>>
Android WebView 学习
查看>>
(转)从给定的文本中,查找其中最长的重复子字符串的问题
查看>>
HDU 2159
查看>>
spring batch中用到的表
查看>>
资源文件夹res/raw和assets的使用
查看>>
UINode扩展
查看>>
LINUX常用命令
查看>>
百度云盘demo
查看>>
概率论与数理统计习题
查看>>
初学structs2,简单配置
查看>>