Я посмотрел, как это реализовано в библиотеке компонентов материалов. И для этого они используют CutoutDrawable
. К сожалению, его нельзя использовать напрямую, так как конструктор является закрытым пакетом. Но можно просто «напрямую» импортировать этот класс в проект.
Чтобы получить желаемый эффект, я бы добавил два представления к ConstraintLayout
, одно для метки и одно для содержимого. Решение также будет работать с любым другим родительским макетом для позиционирования видов, но с ConstraintLayout
легко позиционировать виды по мере необходимости:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="200dp"
android:layout_height="200dp">
<View
android:id="@+id/content"
android:layout_margin="8dp"
android:layout_height="0dp"
android:layout_width="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="Test"
app:layout_constraintBottom_toTopOf="@+id/content"
app:layout_constraintLeft_toLeftOf="@+id/content"
app:layout_constraintTop_toTopOf="@+id/content" />
</androidx.constraintlayout.widget.ConstraintLayout>
Представление содержимого - это то, которое получит CutoutDrawable , Помимо настройки фона, нам также нужно добавить механизм, который обновляет позицию, которая должна была быть «вырезана» - в зависимости от позиции представления метки. Я решил это с помощью OnLayoutChangeListener
:
// configuration of the shape for the outline
val shape = ShapeAppearanceModel.Builder()
.setAllCorners(RoundedCornerTreatment())
.setAllCornerSizes(16f)
.build()
// configuration of the CutOutDrawable - replace with themed values instead of hard coded colors
val drawable = CutoutDrawable(shape).apply {
setStroke(4f, Color.BLACK)
fillColor = ColorStateList.valueOf(Color.WHITE)
}
// content is the view with @+id/content
content.background = drawable
// label is the view with @+id/label
label.addOnLayoutChangeListener { _, left, top, right, bottom, _, _, _, _ ->
// offset the position by the margin of the content view
val realLeft = left - content.left
val realTop = top - content.top
val realRigth = right - content.left
val realBottom = bottom - content.top
// update the cutout part of the drawable
drawable.setCutout(
realLeft.toFloat(),
realTop.toFloat(),
realRigth.toFloat(),
realBottom.toFloat()
)
}
Также должна быть возможность поместить эту стратегию в пользовательское представление, которое настраивает CutoutDrawable
в качестве фона и регистрирует слушателя для положения другого представления в обновить позицию обрезки.
Это результат:
Также можно настроить интервал между текстом и строкой регулируя отступ метки TextView