TextView圖文混排(加載網(wǎng)絡(luò)圖片+本地圖片、表情)

具體Demo見GitHub:RichTextDemo

無圖無真相吱晒,下圖中的所有內(nèi)容全在一個TextView中展示甸饱。

TextView實現(xiàn)圖文混排

主要由SpannableString.setSpan()ImageSapn實現(xiàn)TextView顯示圖片功能,核心代碼如下:

String text = "圖文混排內(nèi)容";
SpannableString spannableString = new SpannableString(text);
Drawable drawable = 來自本地或者網(wǎng)絡(luò)的圖片;
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中圖片對應的文本叹话,替換成相應的文圖片偷遗,實現(xiàn)圖文混排。ImageSpan中需傳入的Drawable 可以讀取本地圖片驼壶,或者從網(wǎng)絡(luò)獲取氏豌。

詳細代碼如下,具體說明看代碼注釋:

其中簡書中正則表達式顯示有問題热凹,正確的為


正則表達式.png
private void setRichText(Context context) {
    float imageSize = textView.getTextSize();
    //圖文混排的text泵喘,這里用"[]"標示圖片
    String text = "這是圖文混排的例子:\n網(wǎng)絡(luò)圖片:"
            + "[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);
    //匹配"[(除了']'任意內(nèi)容)]"的正則表達式,獲取網(wǎng)絡(luò)圖片和本地圖片替換位置
    //簡書中正則表達式顯示有問題碌嘀,正確如上圖
    Pattern pattern = Pattern.compile("\\[[^\\]]+\\]");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        ImageSpan imageSpan;
        //匹配的內(nèi)容涣旨,例如[http://hao.qudao.com/upload/article/20160120/82935299371453253610.jpg]或[哈哈]
        String group = matcher.group();
        if (group.contains("http")) {
            //網(wǎng)絡(luò)圖片
            //獲取圖片url(去掉'['和']')
            String url = group.substring(1, group.length() - 1);
            //異步獲取網(wǎng)絡(luò)圖片
            Drawable drawableFromNet = new URLImageParser(textView, context, (int) imageSize).getDrawable(url);
            imageSpan = new ImageSpan(drawableFromNet, ImageSpan.ALIGN_BASELINE);
            //設(shè)置網(wǎng)絡(luò)圖片
            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();
                //設(shè)置圖片寬高
                drawableFromLocal.setBounds(0, 0, (int) (imageSize * ratio), (int)(imageSize));
                imageSpan = new ImageSpan(drawableFromLocal, ImageSpan.ALIGN_BASELINE);
                //設(shè)置本地圖片
                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 {
                //下載網(wǎng)絡(luò)圖片,以下是使用Picasso和Glide獲取網(wǎng)絡(luò)圖片例子股冗,也可以其他方式下載網(wǎng)絡(luò)圖片

                // 使用Picasso獲取網(wǎng)絡(luò)圖片Bitmap
                return Picasso.with(context).load(source).get();
                // 使用Glide獲取網(wǎng)絡(luò)圖片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);
                //設(shè)置圖片寬霹陡、高(這里傳入的mImageSize為字體大小,所以止状,設(shè)置的高為字體大小烹棉,寬為按寬高比縮放)
                urlDrawable.setBounds(0, 0, (int) (mImageSize * ratio), mImageSize);
                urlDrawable.drawable = bitmapDrawable;
                //兩次調(diào)用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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市集峦,隨后出現(xiàn)的幾起案子伏社,更是在濱河造成了極大的恐慌,老刑警劉巖塔淤,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摘昌,死亡現(xiàn)場離奇詭異,居然都是意外死亡高蜂,警方通過查閱死者的電腦和手機聪黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來备恤,“玉大人稿饰,你說我怎么就攤上這事÷恫矗” “怎么了喉镰?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惭笑。 經(jīng)常有香客問我梧喷,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任铺敌,我火速辦了婚禮汇歹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偿凭。我一直安慰自己产弹,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布弯囊。 她就那樣靜靜地躺著痰哨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匾嘱。 梳的紋絲不亂的頭發(fā)上斤斧,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音霎烙,去河邊找鬼撬讽。 笑死,一個胖子當著我的面吹牛悬垃,可吹牛的內(nèi)容都是我干的游昼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼尝蠕,長吁一口氣:“原來是場噩夢啊……” “哼烘豌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起看彼,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤廊佩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后靖榕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體标锄,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年序矩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跋破。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡簸淀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毒返,到底是詐尸還是另有隱情租幕,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布拧簸,位于F島的核電站劲绪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贾富,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一歉眷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颤枪,春花似錦汗捡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盗胀,卻和暖如春艘蹋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背票灰。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工女阀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人米间。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓强品,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屈糊。 傳聞我的和親對象是個殘疾皇子的榛,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容