EditView+SpannableString+ImageSpan實現圖文混編

jethroHuang發表於2019-03-10

網上有很多圖文混編的方案,無非兩種

  1. webview
  2. SpannableString+ImageSpan

他們之間的優勢比較什麼的,網上很多,我也不再贅述,我這裡直接選擇第二種,因為簡單,需求也只是可以插入圖片.然後各種搜尋如何實現,找到一篇博文

blog.csdn.net/ljzdyh/arti…

我的程式碼就是參考這篇文章來的,因為文章中有些地方使用的api已經被標記為棄用了,所以只好改一下,加點註釋,試圖讓它更加通俗易懂.

那麼先上第一段程式碼

/**
 * 圖片上傳到本地磁碟成功
 *
 * @param path 所在本地磁碟路徑
 */
private void imgUploadSuccess(final String path) {
    Glide.with(this)
            .load(path)
            .into(new CustomTarget<Drawable>() {
                @Override
                public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
                    image(resource, path);
                }

                @Override
                public void onLoadCleared(@Nullable Drawable placeholder) {

                }
            });
} 
複製程式碼

這個方法是我選擇圖片後的回撥, String path是圖片儲存的路徑,我給放sd卡了.路徑類似於/storage/emulated/0/Android/data/com.xxx.xxx/files/imgs/strategy3121064518359412361.png 這裡選擇用 Glide 載入管理圖片,以防止 OOM ,這裡 Glide 的版本是 4.9.0 ,所以回撥跟 glide3 的不一樣,沒了 .asBitmap 用的是 CustomTarget 回撥.

圖片準備好以後會回撥 onResourceReady ,我們在這裡呼叫image(resource, path);

這個方法是重點,看下一段程式碼

/**
 * 把圖片插入到 EditView
 * @param resource 圖片資源
 * @param path 圖片所在位置 可以是url 也可是本地儲存路徑,我這裡是放在本地了
 */
private void image(Drawable resource, String path) {
    String img_str = String.format("![插圖](%s)", path);// 插入markdown格式的插圖
    imgReSize(resource);// 縮放圖片的寬度到EditView的寬度
    int start = etContent.getSelectionStart();// 獲取當前游標所在位置
    // 建立一個SpannableString物件,前後都寫上換行,然後把 img_str 給放進去
    SpannableString ss = new SpannableString("\n\n" + img_str + "\n\n");
    // 新建 ImageSpan 把圖片傳進去
    ImageSpan imageSpan = new ImageSpan(resource);
    // 這一行的意思是給SpannableString物件修飾一下,用剛才建立的 ImageSpan 修飾,
    // 第二個引數是從那個位置開始,ss 前面有兩個換行符,換行符不要修飾,所以跳過換行符,從下標2開始,
    // 換行符雖然寫的\n,但是隻算一個字元.第三個引數之所以要+2是因為前面第二個引數往後挪了兩位,
    // 後面也要挪兩位,不要就會把 img_str 的最後兩個字串給丟掉,導致圖片後面會有`g)`這種字串,
    // 一般圖片的字尾是 png jpg 所以最後的兩個字就是 g),這兩個字一定要寫 img_str.length() + 2
    ss.setSpan(imageSpan, 2, img_str.length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    // 最後把 SpannableString 物件給插入到我們的EditText中,start就是當前游標所在位置
    // 或者說游標最後所在位置
    etContent.getEditableText().insert(start, ss);// 設定ss要新增的位置
    // 最後更新一下view 請求一下焦點.完事
    etContent.requestLayout();
    etContent.requestFocus();
}

複製程式碼

這段程式碼的註釋已經說得很清楚了,我也就不再多說,這裡一定要設定 Drawable 的寬高,不然圖片無法顯示,我呼叫了 imgReSize(resource); 來設定 Drawable 物件的寬高為寬度撐滿EditView容器,然後寬高等比例縮放.下面看看 imgReSize 方法的定義

/**
 * 縮放圖片的寬度到EditView的寬度
 *
 * @param resource 圖片
 */
void imgReSize(Drawable resource) {
    double maxWidget = etContent.getMeasuredWidth() - etContent.getPaddingLeft() - etContent.getPaddingRight();//獲取 EditView 的寬度
    double widget = resource.getIntrinsicWidth();// 獲取資源的固定寬度
    double height = resource.getIntrinsicHeight();// 獲取資源的固定高度
    double bl = height / widget; // 計算寬高比例,注意這裡都是用的 double 型別的變數,以防止整除,也就是3/2=1這樣.
    double maxHeight = bl * maxWidget; // 通過寬高比例計算圖片高度.
    resource.setBounds(0, 0, (int) maxWidget, (int) maxHeight); // 重點程式碼 設定圖片的寬高
}
複製程式碼

好了,這就是插入圖片的所有程式碼了,刪除圖片的話就很簡單,直接按軟鍵盤的刪除鍵就OK.

最後來看看按儲存後列印的文字.


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.menu_img:
            selectImgTool.selectImg();
            break;
        case R.id.menu_release:
            Log.d(TAG, "onSave: " + etContent.getText().toString());
            break;
    }
    return super.onOptionsItemSelected(item);
}
複製程式碼

列印結果

>>> 2019-03-10 11:19:39.139 23523-23523/com.xxx.xxx D/StrategyActivity: onSave: 現在插入一張圖片
    
    ![插圖](/storage/emulated/0/Android/data/com.xxx.xxx/files/imgs/strategy150272438965405431.png)
    
    好了,圖片插入完成。

複製程式碼

介面效果

介面效果

相關文章