如何使用 Kotlin 在 Android 中创建自己的自定义视图?
在本文中,我们将逐步讨论如何在 Android 中创建我们自己的自定义视图。我们都知道一开始 android 平台为我们提供了一些基本的视图,例如——TextView、ImageView、EditText、Button、ImageButton、RadioButton 等。但是,有时我们需要一种全新类型的视图,而我们的预建布局和小部件却无法满足我们的需求。所以,让我们深入了解一下。
什么是 Android 中的自定义视图?
首先,Android 中自定义视图的概念无非就是拥有我们自己的自定义视图模型,而不是预先构建的视图模型。我们如何实现这一目标?通过简单地将不同类型的预构建视图组合在一起,然后将组合的视图用作单个视图。
为什么我们甚至需要在我们的应用程序中使用它们?
我们都知道一开始 android 平台为我们提供了一些基本的视图,例如——TextView、ImageView、EditText、Button、ImageButton、RadioButton 等。但是,有时我们需要一个更具交互性和复杂性的视图类型,其中我们的预建布局和小部件无法满足我们的需求。自定义视图出现在图片中。现在 Android 中的自定义视图完全由开发人员自定义以实现目标。尽管并非到处都需要自定义视图。但是,在复杂性更高的更高开发级别,我们需要它们。
我们将如何创建我们自己的自定义视图?
每当我们要为我们的应用程序创建自定义视图时,我们都需要考虑一些项目。向自己介绍 Android 中视图的生命周期。
现在,要重视 3 种基本方法。
- 测量()
- 布局()
- 绘制()
这 3 个方法将在相应的Java/Kotlin 类中被覆盖。
1. onMeasure() 方法
正如方法名称所暗示的那样,它用于测量目的。基本上,我们可以控制自定义视图的宽度和高度。
- 如果未被覆盖:视图的大小将为“match_parent”或“wrap_content”。
- 如果被覆盖:在覆盖此方法时,我们可以更好地控制自定义视图的大小。不要调用方法'super.onMeasure()' 。相反,我们将调用方法“setMeasuredDimension(width, height)”。
覆盖 onMeasure():
当这个方法被调用时,我们得到'widthMeasureSpec'和'heightMeasureSpec'作为参数。大小模式是由父级为其子级视图设置的约束。下面列出了可用的 3 种模式。
- UNSPECIFIED:没有给出任何限制,所以它可以是它想要的任何大小。
- EXACTLY:确定子视图的确切大小。
- AT_MOST:子项可以根据需要达到指定大小。
例子:
Kotlin
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// requested width and mode
val reqWidth = MeasureSpec.getSize(widthMeasureSpec)
val reqWidthMode = MeasureSpec.getMode(widthMeasureSpec)
// requested height and mode
val reqHeight = MeasureSpec.getSize(heightMeasureSpec)
val reqHeightMode = MeasureSpec.getMode(heightMeasureSpec)
// your choice
val desiredWidth: Int = // TODO("Define your desired width")
val desiredHeight: Int = // TODO("Define your desired height")
val width = when (requestedWidthMode) {
MeasureSpec.EXACTLY -> reqWidth
MeasureSpec.UNSPECIFIED -> desiredWidth
else -> Math.min(reqWidth, desiredWidth) // AT_MOST condition
}
val height = when (requestedHeightMode) {
MeasureSpec.EXACTLY -> reqHeight
MeasureSpec.UNSPECIFIED -> desiredHeight
else -> Math.min(reqHeight, desiredHeight) // AT_MOST condition
}
// set the width and the height of the view
setMeasuredDimension(width, height)
}
Kotlin
protected fun onDraw(canvas:Canvas) {
// Grab canvas dimensions.
val canvasWidth = canvas.getWidth()
val canvasHeight = canvas.getHeight()
// Calculate horizontal center.
val centerX = canvasWidth * 0.5f
// Draw the background.
backgroundRect.set(0f, 0f, canvasWidth, canvasHeight)
canvas.drawRoundRect(backgroundRect, cornerRadius, cornerRadius, backgroundPaint)
// Draw baseline.
val baselineY = Math.round(canvasHeight * 0.6f).toFloat()
canvas.drawLine(0, baselineY, canvasWidth, baselineY, linePaint)
// Draw text.
// Measure the width of text to display.
val textWidth = numberPaint.measureText(displayedCount)
// Figure out an x-coordinate that will center the text in the canvas.
val textX = Math.round(centerX - textWidth * 0.5f).toFloat()
// Draw.
canvas.drawText(displayedCount, textX, baselineY, numberPaint)
}
2. onLayout() 方法
父视图使用此方法通知我们的自定义视图其位置。人们应该使用它来计算他们的绘图宽度和高度。要记住onMeasure() 中发生的任何事情都会影响从父级获得的位置。建议在继续绘制视图之前始终在此处计算图纸尺寸。
3. onDraw() 方法
所有的绘图都发生在这部分内。这一点都不难,因为你得到了一个 Canvas 对象的实例,你可以随意绘制任何你想要的东西。
覆盖 onDraw() :
首先为简单起见,使用'getWidth()'和'getHeight()'方法获取画布对象的宽度和高度。
例子:
科特林
protected fun onDraw(canvas:Canvas) {
// Grab canvas dimensions.
val canvasWidth = canvas.getWidth()
val canvasHeight = canvas.getHeight()
// Calculate horizontal center.
val centerX = canvasWidth * 0.5f
// Draw the background.
backgroundRect.set(0f, 0f, canvasWidth, canvasHeight)
canvas.drawRoundRect(backgroundRect, cornerRadius, cornerRadius, backgroundPaint)
// Draw baseline.
val baselineY = Math.round(canvasHeight * 0.6f).toFloat()
canvas.drawLine(0, baselineY, canvasWidth, baselineY, linePaint)
// Draw text.
// Measure the width of text to display.
val textWidth = numberPaint.measureText(displayedCount)
// Figure out an x-coordinate that will center the text in the canvas.
val textX = Math.round(centerX - textWidth * 0.5f).toFloat()
// Draw.
canvas.drawText(displayedCount, textX, baselineY, numberPaint)
}