首页 > 解决方案 > 在 ConstraintLayout 中放置自定义视图

问题描述

我是在 Android 中实现自定义视图的新手。我已经实现了一个自定义视图(一个简单的矩形)。这里的代码:

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

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        // Paint styles used for rendering are initialized here. This
        // is a performance optimization, since onDraw() is called
        // for every screen refresh.
        style = Paint.Style.FILL
        textAlign = Paint.Align.CENTER
        textSize = 55.0f
        typeface = Typeface.create("", Typeface.BOLD)
        color = Color.GREEN
    }

    private val rectangle = RectF(100f, 100f, 500f, 120f)

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

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawRect(rectangle, paint)
    }
}

由于我的布局文件,它应该放在屏幕中间:

<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".CustomViewFragment">

        <com.example.learningcustomviewimplementation.CustomView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

但它位于左上角附近。从我在输出中看到的,没有使用约束。我需要更改什么以便根据我在布局文件中使用的约束放置自定义视图?

标签: androidandroid-custom-viewandroid-constraintlayout

解决方案


正如您所说,您的视图未正确放置到布局中,因为未指定其尺寸。当android:layout_width和/或android:layout_height视图被定义wrap_content时,视图本身应该计算它的尺寸。这样,容器布局就知道了孩子的宽度和高度。但是,您可以通过onMeasure如下方式覆盖来实现此目的。


自定义视图.kt

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View


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

    private var viewWidth = 0

    var text: String = ""
        set(value) {
            field = value
            calculateSize()
            invalidate()
        }

    var textSize = 20f
        set(value) {
            field = value
            paint.textSize = value
            calculateSize()
            invalidate()
        }

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).also {
        // Paint styles used for rendering are initialized here. This
        // is a performance optimization, since onDraw() is called
        // for every screen refresh.
        it.style = Paint.Style.FILL
        it.textAlign = Paint.Align.CENTER
        it.textSize = textSize
        it.typeface = Typeface.create("", Typeface.BOLD)
        it.color = Color.GREEN
    }

    init {
        calculateSize()
        if (isInEditMode) {
            invalidate()
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = paddingLeft + viewWidth + paddingRight
        val height = paddingTop + textSize.toInt() + paddingBottom
        setMeasuredDimension(width, height)
    }

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

    private fun calculateSize() {
        val bounds = Rect()
        paint.getTextBounds(text, 0, text.length, bounds)
        viewWidth = bounds.width()
    }

    private fun drawText(canvas: Canvas) {
        val x = paddingLeft + viewWidth / 2f
        var y = paddingTop + textSize / 2f
        paint.run {
            y -= ((descent() + ascent()) / 2)
            canvas.drawText(text, x, y, this)
        }
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        customView.text = "Hello World!"
        customView.textSize = 80f
    }
}

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">

    <com.example.learningcustomviewimplementation.CustomView
        android:id="@+id/customView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#AEAEAE"
        android:padding="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

结果:

在此处输入图像描述


推荐阅读