Я создал следующий составной вид, чтобы представить небольшой раздел, который будет использоваться на страницах с подробностями. Он предназначен для многоразового использования контейнера со стилизованным заголовком и должен по умолчанию устанавливать правильные поля для всех его дочерних элементов (чтобы я мог изменить это поле в 1 месте и не беспокоиться об этом в другом месте).
import android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import androidx.core.view.children
import com.developerpaul123.mhwdatabase.R
import kotlinx.android.synthetic.main.component_detail_section.view.*
import kotlin.math.roundToInt
public class DetailSectionLayout : LinearLayout {
private var leftMarginDp: Float = 0f
private var verticalMarginDp: Float = 0f
constructor(context: Context, title: String?) : super(context) {
init(context, title)
}
constructor(context: Context, attributes: AttributeSet?) : super(context, attributes) {
val attrTitle: String? = getSectionTitleAttribute(context, attributes)
init(context, attrTitle)
}
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context, attrs, defStyleAttr) {
val attrTitle: String? = getSectionTitleAttribute(context, attrs)
init(context, attrTitle)
}
private fun getSectionTitleAttribute(
context: Context,
attributes: AttributeSet?
): String? {
val attrs = context.obtainStyledAttributes(attributes, R.styleable.DetailSectionLayout)
val attrTitle: String?
try {
attrTitle = attrs.getString(R.styleable.DetailSectionLayout_detailSectionTitle)
} finally {
attrs.recycle()
}
return attrTitle
}
private fun init(context: Context, title: String?) {
val inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.component_detail_section, this, true)
leftMarginDp =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 28f, resources.displayMetrics)
verticalMarginDp =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 10f, resources.displayMetrics)
orientation = VERTICAL
layoutParams = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
)
(layoutParams as LayoutParams).setMargins(
leftMarginDp.roundToInt(),
verticalMarginDp.roundToInt(),
0,
verticalMarginDp.roundToInt()
)
setTitleText(title)
if (childCount > 0) {
for (child in children) {
child.layoutParams = layoutParams
}
}
}
fun setTitleText(title: String?) {
detail_section_title.text = title
}
override fun addView(child: View?) {
child?.apply {
val params = layoutParams as LinearLayout.LayoutParams
// force margins
params.setMargins(
leftMarginDp.roundToInt(),
verticalMarginDp.roundToInt(),
0,
verticalMarginDp.roundToInt()
)
child.layoutParams = params
}
super.addView(child)
}
}
И раздуваемый макет:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="vertical"
tools:parentTag="android.widget.LinearLayout">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/detail_section_title"
style="@style/TextAppearance.App.CategoryHeadline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
tools:text="Detail Section" />
</merge>
У меня проблема в том, что поля по умолчанию, которые я устанавливаю, не передаются детям должным образом. «Хак», который я сделал, переопределив addView()
, работал для детей, которые добавляются программно в Kotlin, но это не влияет на детей, которые добавляются в XML, например:
<com.developerpaul123.mhwdatabase.components.DetailSectionLayout
android:id="@+id/detailSectionLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:detailSectionTitle="Camps">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test"/>
</com.developerpaul123.mhwdatabase.components.DetailSectionLayout>
Результат компоновки следующий:
Где «Test» - это MaterialTextView
, добавленный в XML, а другие представления - это представления, которые раздуваются во время выполнения и вставляются в макет программно. Обратите внимание, что последние имеют правильное начальное поле (выровненное по названию Camps
).
Мой вопрос: возможно ли выполнить sh то, что я ищу, и правильно передать параметры поля всем дочерним элементам? по умолчанию, включая как те, что добавлены в XML, так и те, которые добавлены в код?