Пользовательская форма фона для содержимого Dynami c - масштабирование - PullRequest
0 голосов
/ 09 марта 2020

У меня есть вопрос о том, как лучше использовать собственные фоны (фигуры) внутри android. У меня есть следующий экран (сделан с Adobe XD):

Custom background for gridlayout

Прозрачная белая фигура должна использоваться в качестве фона для просмотра прокрутки, внутри которого разметка сетки размещен. Теперь я хочу, чтобы фон правильно масштабировался при заполнении сетки, а это означает, что должна меняться только высота. Это желаемый вид после заполнения сетки:

enter image description here

Сначала я попробовал сделать это, просто установив изображение в качестве фона для сетки, и это вывод:

enter image description here

Проблема очевидна, я не хочу, чтобы круг изменил свой радиус. Затем мне пришла в голову идея разделить фон, я вырезал верхнюю часть фона, поместил его в изображение и поместил его вместе с разметкой сетки в относительную компоновку. Кажется, это работает, по крайней мере, на тех устройствах, которые я тестировал. Но я не думаю, что это путь к go, потому что я должен был бы предоставить изображение для каждого возможного размера экрана (а как насчет поворота - да, больше пользовательских изображений ...)

Есть ли androi sh способ сделать это правильно и более dynmai c способ? Я также нашел это: Android пользовательское векторное масштабирование , но кажется, что у меня точно такая же проблема - масштабирование фона при изменении размера экрана

1 Ответ

0 голосов
/ 09 марта 2020

Вы должны быть в состоянии сделать что-то подобное, используя MaterialShapeDrawable.

Сначала добавьте зависимость компонентов материала к вашему грейдеру implementation '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
}

enter image description here

Вы может переопределить класс EdgeTreatment, чтобы создать собственную реализацию ребра.

class CurvedEdgeTreatment() : EdgeTreatment() {
    override fun getEdgePath(
        length: Float,
        center: Float,
        interpolation: Float,
        shapePath: ShapePath
    ) {
    //Your implementation
    }
}

Вот как я вижу, что вы решаете вашу проблему. Для получения дополнительной информации о MaterialShapeDrawable и о том, что может быть достигнуто, вы можете обратиться по этой ссылке , она содержит еще несколько примеров обработки углов и краев и может помочь в вашем конкретном сценарии c.

Вот фактическая реализация делает то, что вы хотите:

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, который можно найти здесь , некоторые внесены небольшие изменения, чтобы сделать его более полезным для вашего сценария.

...