ConstraintHelper で ConstraintLayout な RadioGroup を実装する
ConstraintHelper とは
指定した複数の View に対して同様な処理をさせることができます。ConstraintHelper を拡張したクラスとして Barrier, Flow, Group などがすでにあります。
今回は既存の拡張クラスにならって独自の拡張クラスを作成しようと思います。
素の RadioGroup 内では ConstraintLayout を使用できない
RadioGroup は LinearLayout を継承しているため、ConstraintLayout では RadioButton のスコープを設定することができません。なので、ConstraintLayout を継承した独自 RadioGroup クラスを作成する方法がありますが、正直面倒くさいです。
Y.A.M の 雑記帳: Android TableLayout, RelativeLayout で RadioButton を使う
今回は複数の RadioButton を同一のスコープに設定する、つまり OnCheckedChangeListener
を共通化したいだけですので、上記の方法だとちょっと重い気がします。
そこで ConstraintHelper
の出番です。リスナーを共通化する処理を拡張クラスとして書けば ConstraintLayout で RadioButton を同一のスコープ(リスナー)で扱うことができます。
コード
import android.content.Context import android.util.AttributeSet import android.widget.CompoundButton import androidx.constraintlayout.widget.ConstraintHelper import androidx.constraintlayout.widget.ConstraintLayout class ConstraintRadioGroup @JvmOverloads constructor( context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintHelper(context, attributeSet, defStyleAttr) { private var radioButtonGroup: List<CompoundButton> = emptyList() override fun updatePreLayout(container: ConstraintLayout?) { super.updatePreLayout(container) container ?: return radioButtonGroup = mIds.take(mCount) .mapNotNull { container.findViewById(it) as? CompoundButton } .onEach { it.setOnCheckedChangeListener(listener) } } private val listener: CompoundButton.OnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { button, isChecked -> clear() button.isChecked = isChecked } private fun clear() { radioButtonGroup.map { it.isChecked = false } } }
使いたいところで app:constraint_referenced_ids
に RadioButton の id を渡してあげれば同一のスコープ(リスナー)として扱うことができます。
<xxx.xxx.xxx.ConstraintRadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content" app:constraint_referenced_ids="radio1, radio2, radio3, radio4" />
注意点
ConstraintHelper 自体は ConstraintLayout 1.1.0 で追加された機能ですが、上記の拡張クラスは 1.1.2 でなぜか動作しませんでした🤔
2.0.0〜で使うことをお勧めします。