LoginSignup
10
0

More than 3 years have passed since last update.

【Android】SpannableStringを使って文字列に画像を挿入する

Last updated at Posted at 2020-12-11

はじめに

この記事はand factory Advent Calendar 2020 の12日目の記事です。
昨日は@k_shinnさんの 【Android】ちょっと便利なDrawableを書く でした。

やりたいこと

  • 画像を文字列として扱いたい

背景

Androidアプリを作っていく中で「テキストの横に画像を表示したいんだけど〜」
という旨のオーダーをもらうことがありまして
「そんなの簡単です!」と素直〜にImageViewの横にTextViewを置いて…と下記xmlで実装しました。

hoge.xml
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="20dp"
        android:background="@color/background">

        <ImageView
            android:id="@+id/icon"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:src="@mipmap/ic_launcher"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/repository_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/icon"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="テキストテキストテキスト" />
    </androidx.constraintlayout.widget.ConstraintLayout>

できた!
スクリーンショット 2020-12-11 23.58.36.png

ですが長い文字列が来た際に困ったことが起きました。
そうです、文字の折り返し地点が不自然なものとなってしまうのです…!

スクリーンショット 2020-12-12 0.02.34.png

今回求められていた要件としては
文字列の2行目の位置がアイコンの真下に来るイメージのものが求められていました。

★hogehoge
hogehoge

そのため、SpannableStringでちょっとゴリった実装をしていくことにしました。

ザックリ言ってしまうと画像を挿入する分の文字スペースを作って
そのスペースにSpannableStringで画像をねじ込んでいくスタイルです。

コード

IconSpannable.kt
class IconSpannable(private val targetTextView: TextView) {

    fun insertIconPrefix(@DrawableRes prefixDrawableRes: Int, sourceString: String): SpannableString {
        // 画像を挿入するため、一文字分の空白を作る
        val resultString = SpannableString("  $sourceString")
        return insertImage(prefixDrawableRes, resultString, 0, 1)
    }

    private fun insertImage(
        @DrawableRes drawableRes: Int,
        resultString: SpannableString,
        startSpan: Int,
        endSpan: Int
    ): SpannableString {
        val context = targetTextView.context
        // 対象のTextViewの高さを計測
        val size = (targetTextView.paint.descent() - targetTextView.paint.ascent()).toInt()
        val drawable = context.resources.getDrawable(drawableRes, null)
        drawable?.let {
            it.setBounds(0, 0, size, size)
            val span = ImageSpan(it)
            resultString.setSpan(span, startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
        }
        return resultString
    }
}

使い方

    binding.repositoryName.text =
        IconSpannable(binding.repositoryName).insertIconPrefix(
            R.mipmap.ic_launcher,
            "テキストテキストテキストテキストテキストテキスト"
        )

結果

無事にTextViewの2行目が画像の下にでてきてくれました:v:
Screenshot_20201212-012349.png

終わりに

以上、SpannableStringを使って文字列に画像を挿入する方法でした!
単純そうな要件であっても意外とめんどくさいのがAndroid、というよりエンジニアの常ですよね。
どなたかの助けになれば幸いです。

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0