kovac777
kovac777

Reputation: 730

Why ChipGroup inside HorizontalScrollView with Chip's marginEnd not working?

I have Chips inside ChipGroup and this ChipGroup inside HorizontalScrollView. But my problem is marginEnd on last Chip not working!

I'm tried change layout params programmatically, but not successfull

<HorizontalScrollView
    android:id="@+id/chips_container"
    android:layout_width="0dp"
    android:layout_height="56dp"
    android:layout_gravity="center_vertical"
    android:visibility="gone"
    app:layout_constraintBottom_toTopOf="@+id/dividerSmartReplies"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    tools:visibility="visible">

    <com.google.android.material.chip.ChipGroup
        android:id="@+id/chip_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        app:chipSpacing="8dp"
        app:singleLine="true"
        app:singleSelection="true">

        <com.google.android.material.chip.Chip
            style="@style/Widget.MaterialComponents.Chip.Choice"
            android:layout_width="wrap_content"
            android:layout_height="42dp"
            android:layout_marginStart="8dp"
            android:textColor="@color/selector_smart_reply_chip_text"
            app:checkedIconVisible="false"
            app:chipBackgroundColor="@color/selector_smart_reply_chip"
            app:chipCornerRadius="21dp"
            app:chipStrokeColor="@color/app_base_blue"
            app:chipStrokeWidth="1dp" />

        <com.google.android.material.chip.Chip
            style="@style/Widget.MaterialComponents.Chip.Choice"
            android:layout_width="wrap_content"
            android:layout_height="42dp"
            android:textColor="@color/selector_smart_reply_chip_text"
            app:checkedIconVisible="false"
            app:chipBackgroundColor="@color/selector_smart_reply_chip"
            app:chipCornerRadius="21dp"
            app:chipStrokeColor="@color/app_base_blue"
            app:chipStrokeWidth="1dp" />

        <com.google.android.material.chip.Chip
            style="@style/Widget.MaterialComponents.Chip.Choice"
            android:layout_width="wrap_content"
            android:layout_height="42dp"
            android:textColor="@color/selector_smart_reply_chip_text"
            app:checkedIconVisible="false"
            app:chipBackgroundColor="@color/selector_smart_reply_chip"
            app:chipCornerRadius="21dp"
            app:chipStrokeColor="@color/app_base_blue"
            app:chipStrokeWidth="1dp" />

        <com.google.android.material.chip.Chip
            style="@style/Widget.MaterialComponents.Chip.Choice"
            android:layout_width="wrap_content"
            android:layout_height="42dp"
            android:layout_marginEnd="8dp"
            android:textColor="@color/selector_smart_reply_chip_text"
            app:checkedIconVisible="false"
            app:chipBackgroundColor="@color/selector_smart_reply_chip"
            app:chipCornerRadius="21dp"
            app:chipStrokeColor="@color/app_base_blue"
            app:chipStrokeWidth="1dp" />
    </com.google.android.material.chip.ChipGroup>

</HorizontalScrollView>

I need extra space after last Chip. ChipGroup's padding worked but it does not expected UI result.

Upvotes: 6

Views: 3446

Answers (2)

Filipe Larga
Filipe Larga

Reputation: 123

Hey I know this is a bit late and I'm glad you've found an answer that works for you. However I think i have a better solution:

  • Add 8dp start and end padding to your HorizontalScrollView
  • Make clipToPadding false

So this is what your HorizontalScrollView should look like:

    <HorizontalScrollView
        android:id="@+id/chips_container"
        android:layout_width="0dp"
        android:layout_height="56dp"
        android:layout_gravity="center_vertical"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@+id/dividerSmartReplies"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:visibility="visible"

        android:paddingStart="8dp"
        android:paddingEnd="8dp"
        android:clipToPadding="false"
>

And you can remove the margins on both the first and last chip!

Upvotes: 7

kovac777
kovac777

Reputation: 730

I figured out this issue! ChipGroup extends FlowLayout. FlowLayout, in onMeasure() method, does't take into account marginRight of last child to finalWidth.
My final solution:

class SmartRepliesGroup @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyle: Int = 0
) : ChipGroup(context, attributeSet, defStyle) {

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val maxWidth =
            if (widthMode != -2147483648 && widthMode != 1073741824) 2147483647 else width
        var childLeft = this.paddingLeft
        var childTop = this.paddingTop
        var childBottom = childTop
        var maxChildRight = 0
        val maxRight = maxWidth - this.paddingRight

        var finalWidth = 0
        var lastChildMarginRight = 0
        while (finalWidth < this.childCount) {
            val child = this.getChildAt(finalWidth)
            if (child.visibility != 8) {
                this.measureChild(child, widthMeasureSpec, heightMeasureSpec)
                val lp = child.layoutParams
                var leftMargin = 0
                var rightMargin = 0
                if (lp is MarginLayoutParams) {
                    leftMargin += lp.leftMargin
                    rightMargin += lp.rightMargin
                    lastChildMarginRight = rightMargin
                }

                var childRight = childLeft + leftMargin + child.measuredWidth
                if (childRight > maxRight && !this.isSingleLine) {
                    childLeft = this.paddingLeft
                    childTop = childBottom + this.lineSpacing
                }

                childRight = childLeft + leftMargin + child.measuredWidth
                childBottom = childTop + child.measuredHeight
                if (childRight > maxChildRight) {
                    maxChildRight = childRight
                }

                childLeft += leftMargin + rightMargin + child.measuredWidth + this.itemSpacing
            }
            ++finalWidth
        }

        finalWidth =
            getMeasuredDimension(width, widthMode, maxChildRight + lastChildMarginRight)
        val finalHeight = getMeasuredDimension(height, heightMode, childBottom)
        this.setMeasuredDimension(finalWidth, finalHeight)
    }

    private fun getMeasuredDimension(size: Int, mode: Int, childrenEdge: Int): Int =
        when (mode) {
            -2147483648 -> Math.min(childrenEdge, size)
            1073741824 -> size
            else -> childrenEdge
        }
}

Upvotes: 0

Related Questions