【问题标题】:Placing a Custom View inside a ConstraintLayout在 ConstraintLayout 中放置自定义视图
【发布时间】:2020-07-06 10:16:03
【问题描述】:

我是在 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>

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

【问题讨论】:

    标签: android android-custom-view android-constraintlayout


    【解决方案1】:

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


    CustomView.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>
    

    结果:

    【讨论】:

    • 首先感谢您。这对我帮助很大。但是如果我有一个矩形呢?正如我正确理解的那样,您首先在 getTextBounds() 方法的帮助下通过传入文本及其文本长度等来计算视图宽度。我可以先确定矩形的长度吗?
    • 不客气 :) 那么,矩形大小变化的原因是什么?我的意思是应该有一个像文本或其他东西这样的元素来确定视图的大小。如果你想显示一个固定大小的矩形,我认为这样做是没有问题的。我不知道我理解你的意图对吗?
    【解决方案2】:

    据此https://developer.android.com/training/custom-views/custom-drawing#layouteevent。您需要覆盖 onSizeChanged() 或 onMeasure() 来定义视图的父级希望您的视图有多大。在您的代码中,您只需覆盖 onDraw(),此函数会在您的视图中绘制一个矩形,而不是定义您的视图大小。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 2020-01-22
      • 1970-01-01
      • 2018-11-26
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多