Glide无疑是一个好东西,自从它被google推荐以来,我就一直钟情与它,不离不弃。
不过这家伙很淘气,API变的太快了,导致因为某些原因被迫升级以后,还得挨个改它的很多调用。
最近我碰到一个项目,类似今日头条,要求在文章详情中,显示文字、图片、GIF等。
由于前期架构的问题,没有去用webview,直接作死的去挑战textview,利用HTML.fromHtml()的方式去自定义Spanned满足需求。不过缺点也很明显,由于没有写重用和资源回收机制,当图片过多时,直接OOM....如果有机会去完善这个框架,我会去考虑加入资源回收和重用,毕竟每张图片即使它压缩了,依旧蛮耗内存的。
言归正传,先讲重点,后上代码。
1、自定义textview,包括维护它的点击事件,这样每张图都可以点开。
核心代码:
在自定义的textview里,定义一个setRichText的方法,用于设置ImageGetter和点击事件。
public void setRichText(String text) {
Html.ImageGetter imgGetter = new UrlImageGetter(this, getContext()); //设置ImageGetter
Spanned spanned = Html.fromHtml(text, imgGetter, new HtmlTagHandler());
SpannableStringBuilder spannableStringBuilder;
if (spanned instanceof SpannableStringBuilder) {
spannableStringBuilder = (SpannableStringBuilder) spanned;
} else {
spannableStringBuilder = new SpannableStringBuilder(spanned);
}
// 处理图片的点击事件
ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
final List<String> imageUrls = new ArrayList<>();
for (int i = 0, size = imageSpans.length; i < size; i++) {
ImageSpan imageSpan = imageSpans[i];
String imageUrl = imageSpan.getSource();
int start = spannableStringBuilder.getSpanStart(imageSpan);
int end = spannableStringBuilder.getSpanEnd(imageSpan);
imageUrls.add(imageUrl);
final int finalI = i;
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
if (onRichTextImageClickListener != null) {
onRichTextImageClickListener.imageClicked(imageUrls, finalI);
}
}
};
ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
if (clickableSpans != null && clickableSpans.length != 0) {
for (ClickableSpan cs : clickableSpans) {
spannableStringBuilder.removeSpan(cs);
}
}
spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
super.setText(spanned);
setMovementMethod(LinkMovementMethod.getInstance());
}
2、自定义Html.ImageGetter,它会解析HTML中的图片部分,每当发现是图片资源时,则进入它的代码,进行图片的相关处理。我们自定义它主要是用来启动GIF动画以及重设图片大小。
核心代码:
(1)在重写的getDrawable方法中,利用Glide加载图片,并将加载好的图片选择是显示在GifViewTarget中还是BitmapTarget中
@Override
public Drawable getDrawable(String source) {
if (isGif(source)) {
final UrlDrawable2 urlDrawable2 = new UrlDrawable2();
Glide.with(mContext).asGif().load(source).into(new GifViewTarget(urlDrawable2));
return urlDrawable2;
} else {
final UrlDrawable urlDrawable = new UrlDrawable();
Glide.with(mContext).asBitmap().load(source).into(new BitmapTartget(urlDrawable));
return urlDrawable;
}
}
(2)自定义BitmapTarget和GifViewTarget:
private class BitmapTartget extends SimpleTarget<Bitmap> {
private final UrlDrawable urlDrawable;
public BitmapTartget(UrlDrawable drawable) {
this.urlDrawable = drawable;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
float scaleWidth = ((float) mWidth) / resource.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleWidth);
resource.setConfig(Bitmap.Config.ARGB_4444);
resource = Bitmap.createBitmap(resource, 0, 0,
resource.getWidth(), resource.getHeight(),
matrix, true);
urlDrawable.bitmap = resource;
urlDrawable.setBounds(0, 0, resource.getWidth(),
resource.getHeight());
mTextView.invalidate();
mTextView.setText(mTextView.getText());
}
}
private class GifViewTarget extends SimpleTarget<GifDrawable> {
private final UrlDrawable2 mDrawable;
private GifViewTarget(UrlDrawable2 drawable) {
mTargets.add(this);
this.mDrawable = drawable;
}
private Request request;
@Override
public Request getRequest() {
return request;
}
@Override
public void onResourceReady(GifDrawable resource, Transition<? super GifDrawable> transition) {
Rect rect;
if (resource.getIntrinsicWidth() > 100) {
float width;
float height;
Log.e("koen", "resource width is " + resource.getIntrinsicWidth());
Log.e("koen", "mTextView width is " + mWidth);
if (resource.getIntrinsicWidth() >= mWidth) {
float downScale = (float) resource.getIntrinsicWidth() / mWidth;
width = (float) resource.getIntrinsicWidth() / (float) downScale;
height = (float) resource.getIntrinsicHeight() / (float) downScale;
} else {
float multiplier = (float) mWidth / resource.getIntrinsicWidth();
width = (float) resource.getIntrinsicWidth() * (float) multiplier;
height = (float) resource.getIntrinsicHeight() * (float) multiplier;
}
Log.e("koen", "Final view width is " + width);
rect = new Rect(0, 0, Math.round(width), Math.round(height));
} else {
Log.e("koen", "else view width");
rect = new Rect(0, 0, resource.getIntrinsicWidth() * 2, resource.getIntrinsicHeight() * 2);
}
resource.setBounds(rect);
mDrawable.setBounds(rect);
mDrawable.setDrawable(resource);
//if (resource.isRunning()) {
mDrawable.setCallback(get(mTextView));
resource.setLoopCount(GifDrawable.LOOP_FOREVER);
resource.start();
// }
mTextView.setText(mTextView.getText());
mTextView.invalidate();
}
demo已上传,地址是:http://download.youkuaiyun.com/download/xuehaiwuya1212/10206290
最后,感谢两位大神给我的思路...大神的文章出处已经失传了...我贴不出来