MaterialButtonToggleGroup은 Material Design에서 제공하는 Toggle 형식 버튼이다.
사용 방법은 Toggle Group을 만들고, 그 안에 Material Button을 여러개 배치하는 방식으로 간단하게 구현이 가능하다.
<com.google.android.material.button.MaterialButtonToggleGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toggle_button_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
style="?attr/materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_label_private"/>
<com.google.android.material.button.MaterialButton
style="?attr/materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_label_team"/>
...
</com.google.android.material.button.MaterialButtonToggleGroup>
그런데, 문제는 여기에서 발생한다.
MaterialButtonToggleGroup은 버튼 디자인이 고정되어있다.
가장 왼쪽 버튼의 왼쪽 위, 아래 / 가장 오른쪽 버튼의 오른쪽 위, 아래를 제외한 부분 (버튼과 버튼이 맞닿는 부분)은 뾰족한 사각형을 유지한다.
아무리 CornerRadius를 변경한다고 한들, 양 옆 사이드의 CornerRadius만 변경된다는 것이다.

하지만 현재 제작중인 프로젝트는 이러한 디자인의 버튼을 사용하길 원한다.

며칠간 다룰 수 있는 속성들로 씨름해보기도 하고, 검색도 많이 해봤지만 Material Design상에서 Component 생성 시 자동으로 안쪽의 CornerRadius를 계산해서 초기화해버리기 때문에 아무리 속성을 건드려도 수정할 수 없다는 결론이 나왔다.
그러면 정말 방법이 없는걸까?
MaterialButtonToggleGroup이 CornerRadius를 바꿔버린다면, 나도 다시 강제로 CornerRadius를 바꿔버리면 되지 않을까?
MaterialButtonToggleGroup를 상속하는 Custom Layout을 만들어서 CornerRadius를 변경해보자.
class MaterialButtonToggleGroupWithRadius: MaterialButtonToggleGroup() {}

빨간줄을 확인해보니까 3가지 인자가 필요하다는 듯 하다.
그런데 보면 3가지 인자가 따로따로 주어지는 것이 아니라 겹쳐져서 존재하고 있다.
커스텀 뷰는 호출되는 시점에 따라 다른 생성자가 필요하기 때문에 반드시 생성자 오버로딩이 필수적이다.
첫번째 생성자는 View를 인스턴스화 할 때, 두번째 생성자는 xml을 적용하기 위해 View를 inflating할 때 호출된다.
세번째 생성자의 defStyleAttr는 뷰의 기본적인 속성을 지정한다.
따라서, 3가지 생성자가 모두 정의되어야한다는 뜻이다.
보통 통상적으로 코드를 작성한다면 이런식으로 작성될 것이다.
class CustomView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
// ...
}
생성자가 3개나 쓰이다보니까 코드가 굉장히 더러워진다. 이때, @JvmOverloads를 쓰면 한번에 이 과정을 구현할 수 있다.
@JvmOverloads은 컴파일러에게 인수를 default값으로 대체하는 생성자를 만들도록 지시한다.
class MaterialButtonToggleGroupWithRadius
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: MaterialButtonToggleGroup(context, attrs, defStyleAttr)
아주 코드가 간결해졌다.
이제 class를 정의했으므로, CornerRadius를 정의해보자.
class MaterialButtonToggleGroupWithRadius
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: MaterialButtonToggleGroup(context, attrs, defStyleAttr) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
for (i in 0 until childCount) {
val button = getChildAt(i) as MaterialButton
if (button.visibility == GONE) {
continue
}
val builder = button.shapeAppearanceModel.toBuilder()
val radius = resources.getDimension(R.dimen.button_toggle_group_radius)
button.shapeAppearanceModel = builder.setTopLeftCornerSize(radius)
.setBottomLeftCornerSize(radius)
.setTopRightCornerSize(radius)
.setBottomRightCornerSize(radius).build()
}
}
}
<-- values/dimen.xml !-->
<resources>
<dimen name="button_toggle_group_radius">4dp</dimen>
</resources>
https://github.com/material-components/material-components-android/issues/3008
아주 다행히도, 나와 같은 문제를 이미 겪고있는 개발자가 코드를 만들어두었다.
간략하게 코드를 분석해보면, 해당 Layout의 크기를 설정하면서 각 Child View(여기에선 Material Button이 될 것이다)들의 모양을 변경하는 것이다.
이때, shapeAppearanceModel를 사용했는데, 이게 바로 MaterialShapeDrawable라는 것을 생성해서 Material Component의 모양을 결정하는 것이다!
나는 모서리가 둥근 버튼을 원했기 때문에 CornerSize를 조정했지만, Cut을 사용해서 마름모 꼴의 버튼을 제작할 수도 있다.
아무튼, 이렇게 제작된 Custom View를 MaterialButtonToggleGroup 대신에 사용해주면 끝!
androidx.constraintlayout.widget... 처럼 다른 View를 사용하듯이 com.example.test.MaterialButtonToggleGroupWithRadius 와 같이 Layout에서 선언해주면 된다.