Home > front end >  ConstraintLayout: Second view snaps to parent instead of bottom of custom view
ConstraintLayout: Second view snaps to parent instead of bottom of custom view

Time:12-07

I have two views in a constraint layout, one custom and one a standard ImageView. I want to place the ImageView below the custom view. I'm setting app:layout_constraintTop_toBottomOf on the ImageView attach it to the bottom of the custom view.

However, my ImageView snaps to the top of the parent layout, rather than the bottom of the custom view. This happens both in the Design view of Android Studio as well as when running the app.

How can I fix this?

android studio showing imageView attached to top of constraint layout rather than bottom of custom view

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.app.DrawCharacterView
        android:id="@ id/drawCharacterView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@ id/imageView"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginTop="16dp"
        android:background="#F44336"
        android:padding="1dp"
        android:scaleType="fitCenter"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@ id/drawCharacterView" />
</androidx.constraintlayout.widget.ConstraintLayout>

DrawCharacterView.kt

package com.example.app

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
import androidx.core.content.res.ResourcesCompat

class DrawCharacterView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : View(context) {
    private lateinit var extraCanvas: Canvas
    private lateinit var extraBitmap: Bitmap

    private val backgroundColor = ResourcesCompat.getColor(resources, R.color.black, null)

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        if (::extraBitmap.isInitialized) extraBitmap.recycle()
        extraBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        extraCanvas = Canvas(extraBitmap)
        extraCanvas.drawColor(backgroundColor)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        canvas.drawBitmap(extraBitmap, 0f, 0f, null)
    }
}

CodePudding user response:

You need to pass attrs into your View constructor too:

To allow Android Studio to interact with your view, at a minimum you must provide a constructor that takes a Context and an AttributeSet object as parameters. This constructor allows the layout editor to create and edit an instance of your view.

That's about the Layout Editor really, but I'm assuming the important ConstraintLayout attributes are required so that other views can define constraints on your custom view (I don't know the details about how they're resolved, but it would make sense).

Just FYI, if you start typing this:

DrawCharacterView : View

and with your cursor at the end of View, hit Alt Enter (or whatever your suggestions shortcut is, or click the bulb icon that pops up) you can choose Add Android View constructors using '@JvmOverloads'. That will generate this:

class DrawCharacterView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)

which saves you time, and automatically passes the attrs and defStyleAttr parameters / some defaults, which is generally what you want!

  • Related