Android で TextView を継承しているウィジェットの minWidth を表示した String の幅にする黒魔術

# シチュエーション

[Instagram](https://play.google.com/store/apps/details?id=com.instagram.android) や [Twitter](https://play.google.com/store/apps/details?id=com.twitter.android) などのサービスに見られる「フォロー」ボタンを実装するとします。
未フォロー状態の文言を「フォローする」、フォローしている状態の文言を「フォロー中」とした場合、ボタンの表記は 6 文字と 5 文字のパターンが存在することになります。

# 問題点

ボタンの横幅を `wrap_content` にすると、フォロー状態が切り替わる度にボタンのサイズが変化するという少しダサい挙動になってしまいます[^1]。

| フォローする | フォロー中 |
|:------:|:-------:|
|![Screenshot_1537127857.png](https://qiita-image-store.s3.amazonaws.com/0/6881/076c758b-f1e5-9bc0-9355-5dfd88838bf5.png)|![Screenshot_1537129653.png](https://qiita-image-store.s3.amazonaws.com/0/6881/d3598708-7585-2077-83ca-288024e83607.png)|

# 解決策

予め [TextView.setHint()](https://developer.android.com/reference/android/widget/TextView.html#setHint(java.lang.CharSequence)) で文字列「フォローする」を設定しておくことで全角 6 文字分の幅を最低限確保します。

## サンプル

``` strings.xml
<resources>
    <string name="follow">フォローする</string>
    <string name="following">フォロー中</string>
</resources>
```

``` FollowButton.kt
import android.content.Context
import android.content.res.ColorStateList
import android.support.design.button.MaterialButton
import android.support.v4.content.ContextCompat
import android.util.AttributeSet

/**
 * 「フォロー」ボタン
 */
class FollowButton(context: Context?, attrs: AttributeSet?) : MaterialButton(context, attrs) {

    init {
        setHint(R.string.follow)
        switchFollow()
        setOnClickListener { toggle() }
    }

    private fun switchFollow() = context?.let { context ->
        setText(R.string.follow)
        setTextColor(ContextCompat.getColor(context, android.R.color.white))
        backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, android.R.color.holo_blue_light))
    }

    private fun switchFollowing() = context?.let { context ->
        setText(R.string.following)
        setTextColor(ContextCompat.getColor(context, android.R.color.black))
        backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, android.R.color.white))
    }

    private fun toggle() = context?.let { if (text == it.getString(R.string.following)) switchFollow() else switchFollowing() }
}
```

``` activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <tokyo.sample.FollowButton
            android:id="@+id/follow_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:cornerRadius="16dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    </android.support.constraint.ConstraintLayout>
</layout>
```

``` MainActivity.kt
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import tokyo.sample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }
}
```

## 結果

[MaterialButton](https://developer.android.com/reference/com/google/android/material/button/MaterialButton) の幅を常時テキストが「フォローする」である場合のサイズに固定することができました。

| フォローする | フォロー中 |
|:------:|:-------:|
|![Screenshot_1537127857.png](https://qiita-image-store.s3.amazonaws.com/0/6881/076c758b-f1e5-9bc0-9355-5dfd88838bf5.png)|![Screenshot_1537127859.png](https://qiita-image-store.s3.amazonaws.com/0/6881/73d17341-7fee-b780-3e25-75995c0c9468.png)|

[^1]: 主観的な価値観です。