/**
* Created by sunche on 15/11/10. coffee in code out!
*/
public class RichText extends TextView {
private Drawable placeHolder, errorImage;//占位图,错误图
private OnImageClickListener onImageClickListener;//图片点击回调
MultiDraweeHolder<GenericDraweeHierarchy> mMultiDraweeHolder;
private int d_w = 200;
private int d_h = 200;
public RichText(Context context) {
this(context, null);
init(context, null);
}
public RichText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init(context, attrs);
}
public RichText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichText);
placeHolder = typedArray.getDrawable(R.styleable.RichText_placeHolder);
errorImage = typedArray.getDrawable(R.styleable.RichText_errorImage);
d_w = typedArray.getDimensionPixelSize(R.styleable.RichText_default_width, d_w);
d_h = typedArray.getDimensionPixelSize(R.styleable.RichText_default_height, d_h);
if (placeHolder == null) {
placeHolder = new ColorDrawable(Color.GRAY);
}
placeHolder.setBounds(0, 0, d_w, d_h);
if (errorImage == null) {
errorImage = new ColorDrawable(Color.GRAY);
}
errorImage.setBounds(0, 0, d_w, d_h);
typedArray.recycle();
initDraweeHolder();
}
private void initDraweeHolder() {
mMultiDraweeHolder = new MultiDraweeHolder<>();
}
/**
* 设置富文本
*
* @param text 富文本
*/
public void setRichText(String text) {
Spanned spanned = Html.fromHtml(text, asyncImageGetter, null);
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 (onImageClickListener != null) {
onImageClickListener.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());
}
/**
* 异步加载图片(依赖于fresco)
*/
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
Log.i("RichText", "asyncImageGetter getDrawable.source:" + source);
final URLDrawable urlDrawable = new URLDrawable();
GenericDraweeHierarchy mHierarchy = new GenericDraweeHierarchyBuilder(getResources())
.build();
final DraweeHolder draweeHolder = new DraweeHolder<GenericDraweeHierarchy>(mHierarchy);
mMultiDraweeHolder.add(draweeHolder);
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(Uri.parse(source))
.setOldController(draweeHolder.getController())
.setControllerListener(new ControllerListener<ImageInfo>() {
@Override
public void onSubmit(String id, Object callerContext) {
urlDrawable.setBounds(placeHolder.getBounds());
urlDrawable.setDrawable(placeHolder);
RichText.this.setText(getText());
}
@Override
public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
final Drawable drawable = draweeHolder.getHierarchy().getTopLevelDrawable();
drawable.setBounds(0, 0, imageInfo.getWidth(), imageInfo.getHeight());
urlDrawable.setBounds(0, 0, imageInfo.getWidth(), imageInfo.getHeight());
urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
Log.i("RichText", "onFinalImageSet width:" + imageInfo.getWidth() + ",height:" + imageInfo.getHeight());
}
@Override
public void onIntermediateImageSet(String id, ImageInfo imageInfo) {
Log.i("RichText", "onIntermediateImageSet width:" + imageInfo.getWidth() + ",height:" + imageInfo.getHeight());
}
@Override
public void onIntermediateImageFailed(String id, Throwable throwable) {
}
@Override
public void onFailure(String id, Throwable throwable) {
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
RichText.this.setText(getText());
}
@Override
public void onRelease(String id) {
}
})
.build();
draweeHolder.setController(controller);
return urlDrawable;
}
};
@Override
protected boolean verifyDrawable(Drawable who) {
if (who instanceof URLDrawable && mMultiDraweeHolder.verifyDrawable(((URLDrawable) who).getDrawable())) {
return true;
}
// 对其他Drawable的验证逻辑
return super.verifyDrawable(who);
}
private static final class URLDrawable extends BitmapDrawable {
private Drawable drawable;
@SuppressWarnings("deprecation")
public URLDrawable() {
}
@Override
public void draw(Canvas canvas) {
if (drawable != null)
drawable.draw(canvas);
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
public Drawable getDrawable() {
return drawable;
}
}
public void setPlaceHolder(Drawable placeHolder) {
this.placeHolder = placeHolder;
this.placeHolder.setBounds(0, 0, d_w, d_h);
}
public void setErrorImage(Drawable errorImage) {
this.errorImage = errorImage;
this.errorImage.setBounds(0, 0, d_w, d_h);
}
public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
this.onImageClickListener = onImageClickListener;
}
public interface OnImageClickListener {
/**
* 图片被点击后的回调方法
*
* @param imageUrls 本篇富文本内容里的全部图片
* @param position 点击处图片在imageUrls中的位置
*/
void imageClicked(List<String> imageUrls, int position);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mMultiDraweeHolder.onDetach();
}
@Override
public void onStartTemporaryDetach() {
super.onStartTemporaryDetach();
mMultiDraweeHolder.onDetach();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mMultiDraweeHolder.onAttach();
}
@Override
public void onFinishTemporaryDetach() {
super.onFinishTemporaryDetach();
mMultiDraweeHolder.onAttach();
}
* Created by sunche on 15/11/10. coffee in code out!
*/
public class RichText extends TextView {
private Drawable placeHolder, errorImage;//占位图,错误图
private OnImageClickListener onImageClickListener;//图片点击回调
MultiDraweeHolder<GenericDraweeHierarchy> mMultiDraweeHolder;
private int d_w = 200;
private int d_h = 200;
public RichText(Context context) {
this(context, null);
init(context, null);
}
public RichText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init(context, attrs);
}
public RichText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichText);
placeHolder = typedArray.getDrawable(R.styleable.RichText_placeHolder);
errorImage = typedArray.getDrawable(R.styleable.RichText_errorImage);
d_w = typedArray.getDimensionPixelSize(R.styleable.RichText_default_width, d_w);
d_h = typedArray.getDimensionPixelSize(R.styleable.RichText_default_height, d_h);
if (placeHolder == null) {
placeHolder = new ColorDrawable(Color.GRAY);
}
placeHolder.setBounds(0, 0, d_w, d_h);
if (errorImage == null) {
errorImage = new ColorDrawable(Color.GRAY);
}
errorImage.setBounds(0, 0, d_w, d_h);
typedArray.recycle();
initDraweeHolder();
}
private void initDraweeHolder() {
mMultiDraweeHolder = new MultiDraweeHolder<>();
}
/**
* 设置富文本
*
* @param text 富文本
*/
public void setRichText(String text) {
Spanned spanned = Html.fromHtml(text, asyncImageGetter, null);
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 (onImageClickListener != null) {
onImageClickListener.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());
}
/**
* 异步加载图片(依赖于fresco)
*/
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
Log.i("RichText", "asyncImageGetter getDrawable.source:" + source);
final URLDrawable urlDrawable = new URLDrawable();
GenericDraweeHierarchy mHierarchy = new GenericDraweeHierarchyBuilder(getResources())
.build();
final DraweeHolder draweeHolder = new DraweeHolder<GenericDraweeHierarchy>(mHierarchy);
mMultiDraweeHolder.add(draweeHolder);
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(Uri.parse(source))
.setOldController(draweeHolder.getController())
.setControllerListener(new ControllerListener<ImageInfo>() {
@Override
public void onSubmit(String id, Object callerContext) {
urlDrawable.setBounds(placeHolder.getBounds());
urlDrawable.setDrawable(placeHolder);
RichText.this.setText(getText());
}
@Override
public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
final Drawable drawable = draweeHolder.getHierarchy().getTopLevelDrawable();
drawable.setBounds(0, 0, imageInfo.getWidth(), imageInfo.getHeight());
urlDrawable.setBounds(0, 0, imageInfo.getWidth(), imageInfo.getHeight());
urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
Log.i("RichText", "onFinalImageSet width:" + imageInfo.getWidth() + ",height:" + imageInfo.getHeight());
}
@Override
public void onIntermediateImageSet(String id, ImageInfo imageInfo) {
Log.i("RichText", "onIntermediateImageSet width:" + imageInfo.getWidth() + ",height:" + imageInfo.getHeight());
}
@Override
public void onIntermediateImageFailed(String id, Throwable throwable) {
}
@Override
public void onFailure(String id, Throwable throwable) {
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
RichText.this.setText(getText());
}
@Override
public void onRelease(String id) {
}
})
.build();
draweeHolder.setController(controller);
return urlDrawable;
}
};
@Override
protected boolean verifyDrawable(Drawable who) {
if (who instanceof URLDrawable && mMultiDraweeHolder.verifyDrawable(((URLDrawable) who).getDrawable())) {
return true;
}
// 对其他Drawable的验证逻辑
return super.verifyDrawable(who);
}
private static final class URLDrawable extends BitmapDrawable {
private Drawable drawable;
@SuppressWarnings("deprecation")
public URLDrawable() {
}
@Override
public void draw(Canvas canvas) {
if (drawable != null)
drawable.draw(canvas);
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
public Drawable getDrawable() {
return drawable;
}
}
public void setPlaceHolder(Drawable placeHolder) {
this.placeHolder = placeHolder;
this.placeHolder.setBounds(0, 0, d_w, d_h);
}
public void setErrorImage(Drawable errorImage) {
this.errorImage = errorImage;
this.errorImage.setBounds(0, 0, d_w, d_h);
}
public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
this.onImageClickListener = onImageClickListener;
}
public interface OnImageClickListener {
/**
* 图片被点击后的回调方法
*
* @param imageUrls 本篇富文本内容里的全部图片
* @param position 点击处图片在imageUrls中的位置
*/
void imageClicked(List<String> imageUrls, int position);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mMultiDraweeHolder.onDetach();
}
@Override
public void onStartTemporaryDetach() {
super.onStartTemporaryDetach();
mMultiDraweeHolder.onDetach();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mMultiDraweeHolder.onAttach();
}
@Override
public void onFinishTemporaryDetach() {
super.onFinishTemporaryDetach();
mMultiDraweeHolder.onAttach();
}
}
<declare-styleable name="RichText"> <attr name="placeHolder" format="reference" /> <attr name="errorImage" format="reference" /> <attr name="default_width" format="dimension" /> <attr name="default_height" format="dimension" /> </declare-styleable>