Android程序员

TextView图文混排(加载网络图片+本地图片、表情)

2016-12-12  本文已影响1228人  CaiBird

具体Demo见GitHub:RichTextDemo

无图无真相,下图中的所有内容全在一个TextView中展示。

TextView实现图文混排

主要由SpannableString.setSpan()ImageSapn实现TextView显示图片功能,核心代码如下:

String text = "图文混排内容";
SpannableString spannableString = new SpannableString(text);
Drawable drawable = 来自本地或者网络的图片;
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);  

其中spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)text中图片对应的文本,替换成相应的文图片,实现图文混排。ImageSpan中需传入的Drawable 可以读取本地图片,或者从网络获取。

详细代码如下,具体说明看代码注释:

其中简书中正则表达式显示有问题,正确的为


正则表达式.png
private void setRichText(Context context) {
    float imageSize = textView.getTextSize();
    //图文混排的text,这里用"[]"标示图片
    String text = "这是图文混排的例子:\n网络图片:"
            + "[http://hao.qudao.com/upload/article/20160120/82935299371453253610.jpg][http://b.hiphotos.baidu.com/zhidao/pic/item/d6ca7bcb0a46f21f27c5c194f7246b600d33ae00.jpg]"
            + "\n本地图片:" + "[哈哈][泪][多肉][多肉2]";
    SpannableString spannableString = new SpannableString(text);
    //匹配"[(除了']'任意内容)]"的正则表达式,获取网络图片和本地图片替换位置
    //简书中正则表达式显示有问题,正确如上图
    Pattern pattern = Pattern.compile("\\[[^\\]]+\\]");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        ImageSpan imageSpan;
        //匹配的内容,例如[http://hao.qudao.com/upload/article/20160120/82935299371453253610.jpg]或[哈哈]
        String group = matcher.group();
        if (group.contains("http")) {
            //网络图片
            //获取图片url(去掉'['和']')
            String url = group.substring(1, group.length() - 1);
            //异步获取网络图片
            Drawable drawableFromNet = new URLImageParser(textView, context, (int) imageSize).getDrawable(url);
            imageSpan = new ImageSpan(drawableFromNet, ImageSpan.ALIGN_BASELINE);
            //设置网络图片
            spannableString.setSpan(imageSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        } else {
            //本地图片
            if (localIconMap.get(group) != null) {
                //获取本地图片Drawable
                Drawable drawableFromLocal = context.getResources().getDrawable(localIconMap.get(group));
                //获取图片宽高比
                float ratio = drawableFromLocal.getIntrinsicWidth() * 1.0f / drawableFromLocal.getIntrinsicHeight();
                //设置图片宽高
                drawableFromLocal.setBounds(0, 0, (int) (imageSize * ratio), (int)(imageSize));
                imageSpan = new ImageSpan(drawableFromLocal, ImageSpan.ALIGN_BASELINE);
                //设置本地图片
                spannableString.setSpan(imageSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }

    textView.setText(spannableString);
}

private void initData(Context context) {
    ResourceUtils utils = ResourceUtils.getInstance(context);
    localIconMap = new HashMap<>();
    //获取本地图片标示对应的图片ID(例如[哈哈]对应的R.drawable.haha)
    localIconMap.put(utils.getString("haha"), utils.getDrawableId("haha"));
    localIconMap.put(utils.getString("lei"), utils.getDrawableId("lei"));
    localIconMap.put(utils.getString("duorou"), utils.getDrawableId("duorou"));
    localIconMap.put(utils.getString("duorou2"), utils.getDrawableId("duorou2"));
}  

其中URLImageParser代码如下:

public class URLImageParser {
    private Context mContext;
    private TextView mTextView;
    private int mImageSize;

    /**
     *
     * @param textView 图文混排TextView
     * @param context
     * @param imageSize 图片显示高度
     */
    public URLImageParser(TextView textView, Context context, int imageSize) {
        mTextView = textView;
        mContext = context;
        mImageSize = imageSize;
    }

    public Drawable getDrawable(String url) {
        URLDrawable urlDrawable = new URLDrawable();
        new ImageGetterAsyncTask(mContext, url, urlDrawable).execute(mTextView);
        return urlDrawable;
    }

    public class ImageGetterAsyncTask extends AsyncTask<TextView, Void, Bitmap> {

        private URLDrawable urlDrawable;
        private Context context;
        private String source;
        private TextView textView;

        public ImageGetterAsyncTask(Context context, String source, URLDrawable urlDrawable) {
            this.context = context;
            this.source = source;
            this.urlDrawable = urlDrawable;
        }

        @Override
        protected Bitmap doInBackground(TextView... params) {
            textView = params[0];
            try {
                //下载网络图片,以下是使用Picasso和Glide获取网络图片例子,也可以其他方式下载网络图片

                // 使用Picasso获取网络图片Bitmap
                return Picasso.with(context).load(source).get();
                // 使用Glide获取网络图片Bitmap(使用Glide获取图片bitmap还有待研究)
//                return Glide.with(context).load(source).asBitmap().fitCenter().into(mImageSize * 3, mImageSize * 3).get();
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        protected void onPostExecute(final Bitmap bitmap) {
            try {
                //获取图片宽高比
                float ratio = bitmap.getWidth() * 1.0f / bitmap.getHeight();
                Drawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
                bitmapDrawable.setBounds(0, 0, (int) (mImageSize * ratio), mImageSize);
                //设置图片宽、高(这里传入的mImageSize为字体大小,所以,设置的高为字体大小,宽为按宽高比缩放)
                urlDrawable.setBounds(0, 0, (int) (mImageSize * ratio), mImageSize);
                urlDrawable.drawable = bitmapDrawable;
                //两次调用invalidate才会在异步加载完图片后,刷新图文混排TextView,显示出图片
                urlDrawable.invalidateSelf();
                textView.invalidate();
            } catch (Exception e) {
                /* Like a null bitmap, etc. */
            }
        }
    }
}    

URLDrawable代码如下:

public class URLDrawable extends BitmapDrawable {
    // the drawable that you need to set, you could set the initial drawing
    // with the loading image if you need to
    protected Drawable drawable;

    @Override
    public void draw(Canvas canvas) {
        // override the draw to facilitate refresh function later
        if(drawable != null) {
            drawable.draw(canvas);
        }
    }
}  

ResourceUtils代码如下:

public class ResourceUtils {
    private static ResourceUtils resourceUtils;
    private static Context context;
    private Resources resources;
    private String packageName;

    public static ResourceUtils getInstance(Context context) {
        if (resourceUtils == null) {
            synchronized (ResourceUtils.class) {
                resourceUtils = new ResourceUtils(context);
            }
        }

        return resourceUtils;
    }

    public ResourceUtils(Context context) {
        this.context = context;
        resources = context.getResources();
        packageName = context.getPackageName();
    }

    public int getStringId(String name) {
        try {
            //对应values:strings.xml文件
            return this.resources.getIdentifier(name, "string", packageName);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    public String getString(String name) {
        try {
            return this.resources.getString(getStringId(name));
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public int getDrawableId(String name) {
        try {
            //对应drawable-***文件夹中的图片
            return this.resources.getIdentifier(name, "drawable", packageName);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

}  

具体Demo见GitHub:RichTextDemo

参考:

Android之Glide获取图片Path、Bitmap用法
Display images on Android using TextView and Html.ImageGetter asynchronously?
Android HTML ImageGetter as AsyncTask
Android ImageGetter images overlapping text

上一篇 下一篇

猜你喜欢

热点阅读