zoukankan      html  css  js  c++  java
  • Picasso解决 TextView加载html图片异步显示

    项目中有这样一个需求:

      textview加载一段 html标签 其中包含 "<Img url= " 图片异步展示 而且 根据图片的比例 宽度满屏展示。

    思路:

      重写textview Html.fromHtml方法  以及 图片Picasso展示(后面会附带Picasso 的两个转换类)

    感觉网上没有合适的或者用的是Gilde加载 其实无论是Gilde还是Picasso加载豆豆都能满足我们的需求。

    需求描述完毕 上张帅图:

      

    好吧 废话不多说了 直接上实现代码 

         RichText:  

    public class RichText extends TextView {

    private Drawable placeHolder, errorImage;//占位图,错误图
    private OnImageClickListener onImageClickListener;//图片点击回调
    private HashSet<Target> targets;
    private int d_w = 500;
    private int d_h = 500;

    public RichText(Context context) {
    this(context, null);
    }

    public RichText(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

    public RichText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
    targets = new HashSet<>();
    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();
    }


    /**
    * 设置富文本
    *
    * @param text 富文本
    */
    public void setRichText(String text) {
    targets.clear();
    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());
    }

    private void addTarget(Target target) {
    targets.add(target);
    }

    /**
    * 异步加载图片(依赖于Picasso)
    */
    private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
    @Override
    public Drawable getDrawable(String source) {
    final URLDrawable urlDrawable = new URLDrawable();
    Target target = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    Drawable drawable = new BitmapDrawable(getContext().getResources(), bitmap);
    drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
    urlDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
    urlDrawable.setDrawable(drawable);
    RichText.this.setText(getText());
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
    // urlDrawable.setBounds(errorDrawable.getBounds());
    urlDrawable.setDrawable(errorDrawable);
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {
    // urlDrawable.setBounds(placeHolderDrawable.getBounds());
    urlDrawable.setDrawable(placeHolderDrawable);
    }
    };
    addTarget(target);
    Picasso.with(getContext()).load(source).transform(new ImageTransform()).into(target);//.placeholder(placeHolder).error(errorImage)
    return urlDrawable;
    }
    };

    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 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);
    }
    }

    // ============================ImageTransform 处理图片比例展示
    public class ImageTransform implements Transformation {

    private String Key = "ImageTransform";

    @Override
    public Bitmap transform(Bitmap source) {//40 是我项目中 的图片间距
    int targetWidth = ScreenUtil.getScreenWidth(App.getContext()) - DisplayUtil.dp2px(App.getContext(), 40);
    if (source.getWidth() == 0) {
    return source;
    }
    //如果图片小于设置的宽度,做处理
    if (source.getWidth() < targetWidth) {
    double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
    int targetHeight = (int) (targetWidth * aspectRatio);

    if (targetHeight != 0 && targetWidth != 0) {
    Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
    if (result != source) {
    // Same bitmap is returned if sizes are the same
    source.recycle();
    }
    return result;
    } else {
    return source;
    }
    } else {
    return source;
    }
    }

    @Override
    public String key() {
    return Key;
    }
    }

    //=========================其中 40是我项目左右两边的间距
    配置
    <!--attrs 富文本-->
    <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>
    /**
    * 获取屏幕的宽度px
    */
    public static int getScreenWidth(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics outMetrics = new DisplayMetrics();// 创建了一张白纸
    windowManager.getDefaultDisplay().getMetrics(outMetrics);// 给白纸设置宽高
    return outMetrics.widthPixels;
    }
    public static int dp2px(Context context, float dpValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
    }

    //最后使用
    <com.你的包名.RichText/> RichText.setRichText();就行


    //=============================================================================扩展 Picasso加载圆形图片 解决比例失真问题 可不看
    /**
    * 毕加索 设置圆形头像
    * Created by swplzj on 16/12/10.
    */

    public class CircleTransform implements Transformation {


    private String Key = "CircleTransform";

    private Context mContext;

    private int h = 60;
    public CircleTransform(Context context) {
    this.mContext = context;
    }


    public CircleTransform(Context context,int height) {
    this.mContext = context;
    this.h = height;
    }


    @Override
    public Bitmap transform(Bitmap source) {// 60 是我图片头像的宽高度 压缩
    Bitmap zoomBitmp = BitmapUtils.zoom(source, DisplayUtil.dp2px(mContext, h), DisplayUtil.dp2px(mContext, h));
    Bitmap bitmap = BitmapUtils.circleBitmap(zoomBitmp);
    source.recycle();
    return bitmap;//返回圆形的Bitmap对象
    }

    /**
    * 该方法没有什么实际意义,但是要保证其返回的值不能为null!
    * @return
    */
    @Override
    public String key() {
    return Key;
    }
    }


    public class BitmapUtils {

    /**将矩形的Bitmap对象转换为圆形的Bitmap
    * @param source:待处理的 矩形的Bitmap
    * @return :需返回的圆形的Bitmap
    */
    public static Bitmap circleBitmap(Bitmap source){
    //获取Bitmap的宽度
    int width = source.getWidth();
    //返回一个正方形的Bitmap对象
    Bitmap bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
    //提供指定宽高的canvas
    Canvas canvas = new Canvas(bitmap);
    //提供画笔
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    //背景:在画布上绘制一个圆
    canvas.drawCircle(width / 2, width / 2, width / 2, paint);

    //设置图片相交情况下的处理方式
    //setXfermode:设置当绘制的图像出现相交情况时候的处理方式的,它包含的常用模式有哪几种
    //PorterDuff.Mode.SRC_IN 取两层图像交集部门,只显示上层图像,注意这里是指取相交叉的部分,然后显示上层图像
    //PorterDuff.Mode.DST_IN 取两层图像交集部门,只显示下层图像
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    //前景:在画布上绘制一个bitmap
    canvas.drawBitmap(source, 0, 0, paint);

    return bitmap;

    }

    /**对bitmap进行压缩处理
    * @param source :需要被处理的Bitmap
    * @param width 需要压缩成的宽度 必须为浮点型
    * @param height 需要压缩成的高度 必须为浮点型
    * @return 返回压缩后的Bitmap
    * 注意!必须提供参数2,3为浮点型。
    */
    public static Bitmap zoom(Bitmap source,float width,float height){
    Matrix matrix = new Matrix();
    float scaleX = width / source.getWidth();
    float scaleY = height / source.getHeight();
    matrix.postScale(scaleX, scaleY);

    Bitmap bitmap = Bitmap.createBitmap(source,0,0,source.getWidth(),source.getHeight(),matrix,true);
    return bitmap;
    }
    }
    遗漏或者不清楚的可以联系我QQ群:521039620 Android&Go,Let's go!
    感谢作者 https://github.com/zzhoujay/RichText (Gilde方式实现)
    以及没提到的网上参考 谢谢大家。

    ==================2017年4月26号 更新==========================
    RichText 加载多张图片 或者图片超出屏幕处理
    private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
    @Override
    public Drawable getDrawable(String source) {
    final URLDrawable urlDrawable = new URLDrawable();
    Target target = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    int screenWidth = ScreenUtil.getScreenWidth(App.getContext());// 获取屏幕宽度
    screenWidth = screenWidth- DisplayUtil.dp2px(App.getContext(),40);// 这个是我项目中 左右距离20dp
    int height = bitmap.getHeight() * screenWidth / bitmap.getWidth();

    Bitmap result = Bitmap.createScaledBitmap(bitmap, screenWidth, height, true);//等比压缩 设置 true 3M 压缩到200多K 关于清晰度 你回头可以自己调 优化
    Drawable drawable = new BitmapDrawable(getContext().getResources(), result);
    drawable.setBounds(0, 0, result.getWidth(), result.getHeight());
    urlDrawable.setBounds(0, 0, result.getWidth(), result.getHeight());

    urlDrawable.setDrawable(drawable);
    RichText.this.setText(getText());
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
    urlDrawable.setDrawable(errorDrawable);
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {
    urlDrawable.setDrawable(placeHolderDrawable);
    }
    };
    addTarget(target);
    Picasso.with(getContext()).load(source).transform(new ImageTransform()).into(target);//.placeholder(placeHolder).error(errorImage)
    return urlDrawable;
    }
    };

    ==== ps 关于三星手机 只能成功加载一张图片 还在解决中ing 

  • 相关阅读:
    (转载)Centos7 install Openstack Juno (RDO)
    (转载)vmware esxi 6.0 开启嵌套虚拟化
    Delphi XE5 android toast
    delphi中Message消息的使用方法
    delphi中Time消息的使用方法
    Delphi中Interface接口的使用方法
    SystemParametersinfo 用法
    Delphi XE5 android openurl(转)
    Delphi XE5开发Android程序使用自定义字体文件.
    获取 TUniConnection.SpecificOptions默认值和下拉框列表值
  • 原文地址:https://www.cnblogs.com/yizuochengchi2012/p/6289126.html
Copyright © 2011-2022 走看看