##はじめに
この記事はand factory Advent Calendar 2020 の12日目の記事です。
昨日は@k_shinnさんの 【Android】ちょっと便利なDrawableを書く でした。
##やりたいこと
- 画像を文字列として扱いたい
##背景
Androidアプリを作っていく中で「テキストの横に画像を表示したいんだけど〜」
という旨のオーダーをもらうことがありまして
「そんなの簡単です!」と素直〜にImageViewの横にTextViewを置いて…と下記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>
ですが長い文字列が来た際に困ったことが起きました。
そうです、文字の折り返し地点が不自然なものとなってしまうのです…!
今回求められていた要件としては
文字列の2行目の位置がアイコンの真下に来るイメージのものが求められていました。
★hogehoge
hogehoge
そのため、SpannableStringでちょっとゴリった実装をしていくことにしました。
ザックリ言ってしまうと画像を挿入する分の文字スペースを作って
そのスペースにSpannableStringで画像をねじ込んでいくスタイルです。
##コード
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行目が画像の下にでてきてくれました
終わりに
以上、SpannableStringを使って文字列に画像を挿入する方法でした!
単純そうな要件であっても意外とめんどくさいのがAndroid、というよりエンジニアの常ですよね。
どなたかの助けになれば幸いです。