アイコン付きButtonへのTips

アイコン付きのボタンを実装する機会が度々ある。drawableLeft で文字とは別でアイコンを設置するのではなくアイコンと文字を合わせて真ん中寄せにしたいことが多い。 ViewGroupで囲えば実現できるがもうちょっとスマートにできる方法がないか毎回思うので試したメモを残しておく。

ImageSpanを使うとStringの中に画像を扱える。 画像とテキストのtext alignをvertical centerになるように表示したかったため(ImageSpanを使うとbaselineで表示される)、ImageSpanを継承したCenteredImageSpanを作成して対応した。

f:id:chiiia12:20190715200854j:plain

ImageSpanで表示するとbaselineに合わせて画像とテキストが並ぶ。

ref:

Androidアプリでボタン内に画像とテキストを中央寄せで表示する - Qiita

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ImageSpan;

public class CenteredImageSpan extends ImageSpan {

    public CenteredImageSpan(final Drawable drawable) {
        this(drawable, DynamicDrawableSpan.ALIGN_BOTTOM);
    }

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


    public void draw(Canvas canvas, CharSequence text, int start,
                     int end, float x, int top, int y, int bottom,
                     Paint paint) {
        Drawable b = getDrawable();
        canvas.save();

        int transY = bottom - b.getBounds().bottom;
        // this is the key
        transY -= paint.getFontMetricsInt().descent / 2;

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

stackoverflow.com

Android DataBinding + Kotlin でつまずいたこと(BindingAdapter多め) - Qiita

 <Button
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/space_8dp"
            android:layout_marginEnd="@dimen/space_8dp"
            android:layout_marginBottom="@dimen/comment_area_margin_bottom"
            android:background="@drawable/bg_brown"
            android:text="@string/label_voice_comment"
            android:textAllCaps="false"
            app:drawableWithText="@{R.drawable.btn_icn_comment}"
/>

動的な値でなくてもBindingAdapterを使うときは@{}で囲わないとうまく表示されない。 android:textAllCaps="false" が必要。

ref:

DataBindingでカスタムセッターに定数を指定する場合にattributeがnot foundになる場合の対処 - Qiita

@BindingAdapter("drawableWithText")
fun Button.setDrawableWithText(resource: Int) {
    val ss = SpannableString("  " + this.text)
    val d = ContextCompat.getDrawable(this.context, resource)
    d?.setBounds(0, 0, d.intrinsicWidth, d.intrinsicHeight)
    val span = CenteredImageSpan(d)
    ss.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
    this.text = ss
}

f:id:chiiia12:20190715200905j:plain

これでvertical centerに表示される。

ref: android - SpannableString with Image example - Stack Overflow android - Why is this BindingAdapter not working in Kotlin? - Stack Overflow