public class SpannableStringAndImageSpanActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spannbalestring_imagespan);
        init();
    }

    private void init() {

        TextView tv_test = (TextView) findViewById(R.id.tv_test);
        SpannableString spannableString = new SpannableString("點選 按鈕有驚喜 \n 後面隨便換個行,加點東西");

        //呼叫自定義的imageSpan,實現文字與圖片的橫向居中對齊
        CustomImageSpan imageSpan = new CustomImageSpan(this, R.mipmap.ic_launcher, 2);

        //setSpan插入內容的時候,起始位置不替換,會替換起始位置到終止位置間的內容,含終止位置。
        //Spanned.SPAN_EXCLUSIVE_EXCLUSIVE模式用來控制是否同步設定新插入的內容與start/end 位置的字型樣式,此處沒設定具體字型,所以可以隨意設定
        spannableString.setSpan(imageSpan, 2, 3, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        tv_test.setText(spannableString);
    }

    /**
     * 自定義imageSpan實現圖片與文字的居中對齊
     */
    class CustomImageSpan extends ImageSpan {

        //自定義對齊方式--與文字中間線對齊
        private static final int ALIGN_FONTCENTER = 2;

        public CustomImageSpan(Drawable drawable,int verticalAlignment){
            super(drawable,verticalAlignment);
        }

        public CustomImageSpan(Context context, int resourceId) {
            super(context, resourceId);
        }

        public CustomImageSpan(Context context, int resourceId, int verticalAlignment) {
            super(context, resourceId, verticalAlignment);
        }

//        public CustomImageSpan(Context context, int resourceId,int width,int height) {
//            super(context, ThumbnailUtils.extractThumbnail(BitmapFactory.decodeResource(context.getResources(),resourceId),
//                    AutoUtils.getPercentWidthSize(width), AutoUtils.getPercentWidthSize(height)), 2);
//
//        }
        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,
                         Paint paint) {

            //draw 方法是重寫的ImageSpan父類 DynamicDrawableSpan中的方法,在DynamicDrawableSpan類中,雖有getCachedDrawable(),
            // 但是私有的,不能被呼叫,所以呼叫ImageSpan中的getrawable()方法,該方法中 會根據傳入的drawable ID ,獲取該id對應的
            // drawable的流物件,並最終獲取drawable物件
            Drawable drawable = getDrawable(); //呼叫imageSpan中的方法獲取drawable物件
            canvas.save();

            //獲取畫筆的文字繪製時的具體測量資料
            Paint.FontMetricsInt fm = paint.getFontMetricsInt();

            //系統原有方法,預設是Bottom模式)
            int transY = bottom - drawable.getBounds().bottom;
            if (mVerticalAlignment == ALIGN_BASELINE) {
                transY -= fm.descent;
            } else if (mVerticalAlignment == ALIGN_FONTCENTER) {    //此處加入判斷, 如果是自定義的居中對齊
                //與文字的中間線對齊(這種方式不論是否設定行間距都能保障文字的中間線和圖片的中間線是對齊的)
                // y+ascent得到文字內容的頂部座標,y+descent得到文字的底部座標,(頂部座標+底部座標)/2=文字內容中間線座標
                transY = ((y + fm.descent) + (y + fm.ascent)) / 2 - drawable.getBounds().bottom / 2;
            }

            canvas.translate(x, transY);
            drawable.draw(canvas);
            canvas.restore();
        }

        /**
         * 重寫getSize方法,只有重寫該方法後,才能保證不論是圖片大於文字還是文字大於圖片,都能實現中間對齊
         */
        public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            Drawable d = getDrawable();
            Rect rect = d.getBounds();
            if (fm != null) {
                Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
                int fontHeight = fmPaint.bottom - fmPaint.top;
                int drHeight = rect.bottom - rect.top;

                int top = drHeight / 2 - fontHeight / 4;
                int bottom = drHeight / 2 + fontHeight / 4;

                fm.ascent = -bottom;
                fm.top = -bottom;
                fm.bottom = top;
                fm.descent = top;
            }
            return rect.right;
        }
    }
}