android - 动态内容的自定义背景形状 - 适当缩放
问题描述
我有一个关于在android中使用自定义背景(形状)的正确方法的问题。我有以下屏幕(用 adobe xd 制作):
透明的白色形状应用作放置网格布局的滚动视图的背景。现在我希望在填充网格布局时背景能够正确缩放,这意味着只有高度应该改变。这是填充网格布局后所需的外观:
首先,我通过简单地将图像设置为网格布局的背景来尝试它,这是输出:
问题很明显,我不希望圆改变它的半径。然后我想出了分割背景的想法,我把背景的上半部分剪下来,放到一个imageview中,和gridlayout一起放在一个相对的布局里面。这似乎可行,至少在我测试过的那些设备上是这样。但我不认为这是要走的路,因为我必须为每个可能的屏幕尺寸提供图像(以及旋转怎么样 - 是的,更多自定义图像......)
有没有一种正确且更动态的方式来做这件事?我也发现了这个:Android custom vector scaling,但似乎存在与我完全相同的问题 - 当屏幕尺寸发生变化时缩放背景
解决方案
您应该能够使用MaterialShapeDrawable
.
首先将材料组件依赖项添加到您的 gradleimplementation 'com.google.android.material:material:1.1.0'
然后,您可以开始对组件的材质版本应用边角处理,例如CardView
和TextView
。
下面的代码将内置应用TriangleEdgeTreatment
到MaterialTextView
. 请参阅下图了解这在设备上的外观(不完美,只是可以实现的示例)
//In OnCreateView
val text = view.findViewById<MaterialTextView>(R.id.text)
val topEdge = TriangleEdgeTreatment(resources.getDimension(R.dimen.triangle_depth), true)
val shapeAppearance = ShapeAppearanceModel.builder()
shapeAppearance.setTopEdge(topEdge)
card.background = MaterialShapeDrawable(shape.build()).apply {
setTint(ContextCompat.getColor(this@MyFragment.context!!, R.color.colorPrimary))
paintStyle = Paint.Style.FILL
}
您可以覆盖EdgeTreatment
该类以创建自己的边缘实现。
class CurvedEdgeTreatment() : EdgeTreatment() {
override fun getEdgePath(
length: Float,
center: Float,
interpolation: Float,
shapePath: ShapePath
) {
//Your implementation
}
}
这就是我希望您解决问题的方式。有关更多信息MaterialShapeDrawable
以及可以实现的目标,您可以查看此链接,它包含更多关于拐角和边缘处理的示例,并且可能对您的特定场景有所帮助。
这是一个实际的实现,可以满足您的要求:
class CurvedEdgeTreatment(
private val diameter: Float,
private val roundedCornerRadius: Float = 0f,
private val horizontalOffset: Float = 0f,
private val verticalOffset: Float = 0f
) : EdgeTreatment() {
private val arcQuarter = 90f
private val arcHalf = 180f
private val angleUp = 270f
private val angleLeft = 180f
override fun getEdgePath(
length: Float,
center: Float,
interpolation: Float,
shapePath: ShapePath
) {
val radius = diameter / 2f
val roundedCornerOffset: Float = interpolation * roundedCornerRadius
val middle: Float = center + horizontalOffset
// The center offset of the cutout tweens between the vertical offset when attached, and the
// cradleRadius as it becomes detached.
val verticalOffset: Float =
interpolation * verticalOffset + (1 - interpolation) * radius
val verticalOffsetRatio = verticalOffset / radius
if (verticalOffsetRatio >= 1.0f) {
// Vertical offset is so high that there's no curve to draw in the edge, i.e., the fab is
// actually above the edge so just draw a straight line.
shapePath.lineTo(length, 0f)
return // Early exit.
}
// Calculate the path of the cutout by calculating the location of two adjacent circles. One
// circle is for the rounded corner. If the rounded corner circle radius is 0 the corner will
// not be rounded. The other circle is the cutout.
// Calculate the X distance between the center of the two adjacent circles using pythagorean
// theorem.
val distanceBetweenCenters = radius + roundedCornerOffset
val distanceBetweenCentersSquared =
distanceBetweenCenters * distanceBetweenCenters
val distanceY = verticalOffset + roundedCornerOffset
val distanceX =
sqrt(distanceBetweenCentersSquared - (distanceY * distanceY).toDouble()).toFloat()
// Calculate the x position of the rounded corner circles.
val leftRoundedCornerCircleX = middle - distanceX
val rightRoundedCornerCircleX = middle + distanceX
// Calculate the arc between the center of the two circles.
val cornerRadiusArcLength =
Math.toDegrees(atan(distanceX / distanceY.toDouble())).toFloat()
val cutoutArcOffset: Float = arcQuarter - cornerRadiusArcLength
// Draw the starting line up to the left rounded corner.
shapePath.lineTo( /* x= */leftRoundedCornerCircleX, 0f)
// Draw the arc for the left rounded corner circle. The bounding box is the area around the
// circle's center which is at `(leftRoundedCornerCircleX, roundedCornerOffset)`.
shapePath.addArc( /* left= */
leftRoundedCornerCircleX - roundedCornerOffset, 0f, /* right= */
leftRoundedCornerCircleX + roundedCornerOffset, /* bottom= */
roundedCornerOffset * 2, /* startAngle= */
angleUp, /* sweepAngle= */
cornerRadiusArcLength
)
// Draw the cutout circle.
shapePath.addArc( /* left= */
middle - radius, /* top= */
-radius - verticalOffset, /* right= */
middle + radius, /* bottom= */
radius - verticalOffset, /* startAngle= */
angleLeft - cutoutArcOffset, /* sweepAngle= */
cutoutArcOffset * 2 - arcHalf
)
// Draw an arc for the right rounded corner circle. The bounding box is the area around the
// circle's center which is at `(rightRoundedCornerCircleX, roundedCornerOffset)`.
shapePath.addArc( /* left= */
rightRoundedCornerCircleX - roundedCornerOffset, 0f, /* right= */
rightRoundedCornerCircleX + roundedCornerOffset, /* bottom= */
roundedCornerOffset * 2, /* startAngle= */
angleUp - cornerRadiusArcLength, /* sweepAngle= */
cornerRadiusArcLength
)
// Draw the ending line after the right rounded corner.
shapePath.lineTo( /* x= */length, 0f)
}
}
BottomAppBarEdgeTreatment
这个实现主要是从可以在这里找到的代码中复制的,进行了一些小的更改以使其对您的场景更有用。
推荐阅读
- vue.js - Vue.js 中正确的组件组合和道具的使用是什么?
- javascript - 在点击函数中使用 $emit
- regex - 如何匹配正则表达式中的确切数字?
- generics - f# 为多种类型和重载运算符定义泛型函数
- html - Electron:自定义标题栏:当光标离开应用程序时,按钮不会将其 CSS 属性更改回正常(鼠标悬停后)
- gatsby - Gatsby Starter Blog 使用表单创建页面
- c++ - 使用-Wextra-semi-stmt?
- tensorflow - 在 Ort:Value:CreateTensor 中将张量排序为线性数组
- javascript - 每当网页加载时,如何运行 NodeJS 脚本?
- excel - 复制工作表时 Excel 工作表中的 PowerQuery 损坏,但参数没有帮助