I'm creating a custom compound view which extends LinearLayout (also tried ConstraintLayout) which contains 2 child views. When the app is running the view lays out correctly, however in the preview window it shows as 0 height when I use wrap_content. When the view was extending ConstraintLayout it was rendering as match_parent when the view was set to wrap_content.
If I override onMeasure and force a size when isInEditMode it is still showing up as 0 height.
Can someone point out what I'm doing wrong? This is the custom view class:
package com.classdojo.android.nessie
import android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import com.airbnb.paris.utils.getFont
import com.classdojo.android.nessie.icon.Icon
import kotlinx.android.synthetic.main.nessie_button_view.view.*
class NessieButton : LinearLayout {
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(attrs)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context) : super(context) {
init(null)
}
enum class Style(
@DrawableRes
val backgroundRes: Int,
@ColorRes
val textColor: Int
) {
PRIMARY(
backgroundRes = R.drawable.nessie_button_style_primary,
textColor = R.color.nessie_button_style_primary_text_color
),
SECONDARY(
backgroundRes = R.drawable.nessie_button_style_secondary,
textColor = R.color.nessie_button_style_secondary_text_color
),
TERTIARY(
backgroundRes = R.drawable.nessie_button_style_tertiary,
textColor = R.color.nessie_button_style_tertiary_text_color
),
DESTRUCTIVE(
backgroundRes = R.drawable.nessie_button_style_destructive,
textColor = R.color.nessie_button_style_destructive_text_color
),
BEYOND(
backgroundRes = R.drawable.nessie_button_style_beyond,
textColor = R.color.nessie_button_style_beyond_text_color
);
companion object {
fun from(index: Int) = values().getOrElse(index) { Style.PRIMARY }
}
}
enum class Size(
@DimenRes
val horizontalPadding: Int,
@DimenRes
val verticalPadding: Int,
@DimenRes
val textSize: Int
) {
SMALL(
horizontalPadding = R.dimen.nessie_default_size_3x,
verticalPadding = R.dimen.nessie_default_size_2x,
textSize = R.dimen.nessie_textSizeDetail
),
MEDIUM(
horizontalPadding = R.dimen.nessie_default_size_3x,
verticalPadding = R.dimen.nessie_default_size_2x,
textSize = R.dimen.nessie_textSizeAction
),
LARGE(
horizontalPadding = R.dimen.nessie_default_size_4x,
verticalPadding = R.dimen.nessie_default_size_3x,
textSize = R.dimen.nessie_textSizeAction
);
companion object {
fun from(index: Int) = values().getOrElse(index) { Size.LARGE }
}
}
var text: CharSequence?
set(value) {
nessie_button_text_view.text = value
nessie_button_text_view.isVisible = !value.isNullOrBlank()
}
get() = nessie_button_text_view.text
var icon: Icon? = null
set(value) {
field = value
when (value) {
null -> {
nessie_button_icon_view.isVisible = false
}
else -> {
nessie_button_icon_view.icon = value
nessie_button_icon_view.isVisible = true
}
}
}
var style: Style = Style.PRIMARY
set(value) {
field = value
setBackgroundResource(value.backgroundRes)
val color = ResourcesCompat.getColor(resources, value.textColor, null)
nessie_button_icon_view.iconColor = color
nessie_button_text_view.setTextColor(color)
}
var size: Size = Size.LARGE
set(value) {
field = value
val horizontalPadding = resources.getDimensionPixelSize(value.horizontalPadding)
val verticalPadding = resources.getDimensionPixelSize(value.verticalPadding)
setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
val fontSize = resources.getDimension(value.textSize)
nessie_button_text_view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
nessie_button_icon_view.apply {
iconPixelSize = fontSize.toInt()
layoutParams.apply {
width = fontSize.toInt()
height = fontSize.toInt()
}
}
}
init {
init(null)
}
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
super.setFocusable(enabled)
val colorStateList = ResourcesCompat.getColorStateList(resources, style.textColor, null)
val viewState = drawableState
val color = colorStateList!!.getColorForState(viewState, 0)
nessie_button_text_view.setTextColor(color)
nessie_button_icon_view.iconColor = color
}
private fun init(attrs: AttributeSet?) {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER
inflate(context, R.layout.nessie_button_view, this)
val styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.nessie_NessieButton)
try {
val iconIndex = styledAttributes.getInteger(R.styleable.nessie_NessieButton_nessie_icon, -1)
icon = when (iconIndex) {
-1 -> null
else -> Icon.from(iconIndex)
}
text = styledAttributes.getString(R.styleable.nessie_NessieButton_android_text)
style = Style.from(styledAttributes.getInteger(R.styleable.nessie_NessieButton_nessie_buttonStyle, Style.PRIMARY.ordinal))
size = Size.from(styledAttributes.getInteger(R.styleable.nessie_NessieButton_nessie_buttonSize, Size.LARGE.ordinal))
isEnabled = styledAttributes.getBoolean(R.styleable.nessie_NessieButton_android_enabled, true)
} finally {
styledAttributes.recycle()
}
nessie_button_text_view.typeface = context.getFont(R.font.nessie_proximanova_bold)
}
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) {
if (child.id == R.id.nessie_button_icon_view || child.id == R.id.nessie_button_text_view) {
super.addView(child, index, params)
} else {
throw IllegalArgumentException("Cannot add more views to a NessieButton")
}
}
}
And this is the view layout:
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/nessie_default_size_2x"
tools:background="@drawable/nessie_button_primary_background"
tools:parentTag="android.widget.LinearLayout">
<com.classdojo.android.nessie.icon.IconImageView
android:id="@+id/nessie_button_icon_view"
android:layout_width="@dimen/nessie_default_size_3x"
android:layout_height="@dimen/nessie_default_size_3x"
android:layout_marginEnd="@dimen/nessie_default_size" />
<TextView
android:id="@+id/nessie_button_text_view"
style="@style/nessie_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineHeight="22sp"
android:textStyle="bold"
tools:text="My button"
tools:textColor="@color/nessie_white" />
</merge>
