Table of Contents
TextView の左に画像を設置するには
- TextView の左に画像を設定したい、まず最初に考えるの
drawableLeft
を使って画像追加し、パディングなどを調整する。
<!--?xml version="1.0" encoding="utf-8"?-->
<framelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="30dp" tools:context=".BlankFragment"></framelayout>
<textview android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:linespacingmultiplier="2" android:drawableleft="@drawable/icon_smoking" tools:text="@string/hello_blank_fragment"></textview>
- 1 行のときは問題ないが、改行があると 2 行の中央になってしまう。
- 今回は改行があったり、なかったりする TextView で改行した場合は1行目に画像を設置したい。
SpannableStringBuilder、ImageSpan で左に画像を設置する
- SpannableString、SpannableStringBuilder などを使って ImageSpan を追加して、最後に TextView に画像を設定する。
- Fragment の onCreateView に実装してる
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_blank, container, false);
// テキストビュー ※ line-height的な「android:lineSpacingMultiplier="2"」を設定してるTextView
TextView textView = view.findViewById(R.id.textView);
// テキストビューの文言
String str = getActivity().getResources().getString(R.string.hello_blank_fragment);
// スパンのビルダー ※ 1つスペースを設ける、ここに画像を追加する予定
SpannableStringBuilder ssb = new SpannableStringBuilder(" " + str);
// 画像のサイズ 15dp
int size = getActivity().getResources().getDimensionPixelSize(R.dimen.icon_size);
// 禁煙マークのアイコン
Drawable drawable = getActivity().getDrawable(R.drawable.icon_nosmoking);
// 禁煙マークのアイコンを15dp x 15dpにする
drawable.setBounds(0, 0, size, size);
// ImageSpanに設定する
ImageSpan imageSpan = new CustomImageSpan(drawable);
// スパンに追加
ssb.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
// TextViewに設定
textView.setText(ssb);
return view;
}
- TextView に
android:lineSpacingMultiplier
、android:lineSpacingExtra
を設定した状態(css でいうところ line-height)を設定していたら、ImageSpan の位置をうまく計算できないようです。
ALIGN_BASELINE、ALIGN_BOTTOM を確認したがダメだった
DynamicDrawableSpan.ALIGN_BASELINE | DynamicDrawableSpan.ALIGN_BOTTOM | ||
---|---|---|---|
1 行 | ![]() |
![]() |
|
2 行 | ![]() |
![]() |
ImageSpan を継承した独自のクラスを作って調整するみたい
参考サイト
- https://stackoverflow.com/questions/25628258/align-text-around-imagespan-center-vertical/31491580
ImageSpan をオーバーライドしたクラス
public class CustomImageSpan extends ImageSpan {
public CustomImageSpan(Drawable drawable) {
super(drawable);
}
/**
* update the text line height
*/
@Override
public int getSize(Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fontMetricsInt) {
Drawable drawable = getDrawable();
Rect rect = drawable.getBounds();
if (fontMetricsInt != null) {
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent - fmPaint.ascent;
int drHeight = rect.bottom - rect.top;
int centerY = fmPaint.ascent + fontHeight / 2;
fontMetricsInt.ascent = centerY - drHeight / 2;
fontMetricsInt.top = fontMetricsInt.ascent;
fontMetricsInt.bottom = centerY + drHeight / 2;
fontMetricsInt.descent = fontMetricsInt.bottom;
}
return rect.right;
}
/**
* see detail message in android.text.TextLine
*
* @param canvas the canvas, can be null if not rendering
* @param text the text to be draw
* @param start the text start position
* @param end the text end position
* @param x the edge of the replacement closest to the leading margin
* @param top the top of the line
* @param y the baseline
* @param bottom the bottom of the line
* @param paint the work paint
*/
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable drawable = getDrawable();
canvas.save();
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent - fmPaint.ascent;
int centerY = y + fmPaint.descent - fontHeight / 2;
int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
canvas.translate(x, transY);
drawable.draw(canvas);
canvas.restore();
}
}
先ほどのFragmentの実装を修正
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_blank, container, false);
// テキストビュー ※ line-height的な「android:lineSpacingMultiplier="2"」を設定してるTextView
TextView textView = view.findViewById(R.id.textView);
// テキストビューの文言
String str = getActivity().getResources().getString(R.string.hello_blank_fragment);
// スパンのビルダー ※ 1つスペースを設ける、ここに画像を追加する予定
SpannableStringBuilder ssb = new SpannableStringBuilder(" " + str);
// 画像のサイズ 15dp
int size = getActivity().getResources().getDimensionPixelSize(R.dimen.icon_size);
// 禁煙マークのアイコン
Drawable drawable = getActivity().getDrawable(R.drawable.icon_nosmoking);
// 禁煙マークのアイコンを15dp x 15dpにする
drawable.setBounds(0, 0, size, size);
// 独自のCustomImageSpanを使う
CustomImageSpan customImageSpan = new CustomImageSpan(drawable);
// スパンに追加
ssb.setSpan(customImageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
// TextViewに設定
textView.setText(ssb);
return view;
}