非平凡背景可绘制的问题

时间:2018-04-07 10:39:34

标签: android android-layout android-graphics

我有一些非常简单的卡片视图背景。

enter image description here

我想,最好的方法是在Canvas中绘制这个矩形,但是如何与它相交?如何将此自定义绘制设置为背景?此外,我需要为此卡添加阴影。

我的尝试没有成功)

这是矩形布局

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">

    <corners android:radius="20dp"/>
    <solid android:color="@color/common.white"/>
    <stroke android:color="@color/group_flights.bubble.border"/>

</shape>

这是drawable

的实现
class CardBackgroudDrawable(val context: Context?, val widht: Float, val height: Float): Drawable() {

    val paint = Paint()

    override fun draw(canvas: Canvas?) {
        val bitmap = BitmapFactory.decodeResource(context?.resources, R.drawable.drawable_flight_card_background)
        with(paint) {
            strokeWidth = 1f
            isAntiAlias = true
            isDither = true
        }

        canvas?.drawBitmap(bitmap, bounds, RectF(0f, 0f, widht, height), paint)

        // How to intersect shapes??
    }

    override fun setAlpha(alpha: Int) {}

    override fun getOpacity(): Int = PixelFormat.TRANSLUCENT

    override fun setColorFilter(colorFilter: ColorFilter?) {}

}

和卡片布局

class FlightCardLayout: ConstraintLayout {

    constructor(context: Context?) : super(context) {
        init(context)
    }
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        init(context)
    }
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context)
    }

    private fun init(context: Context?) {
        val drawable = CardBackgroudDrawable(context, measuredWidth.toFloat(), measuredHeight.toFloat())

        background = drawable
    }

}

另外,我想用.9.png格式。这是好主意吗?

U.P.D

我找到了漂亮的工具“PainCode”,并尝试将此形状转换为Bezier,

public class BezierFlightCard {


    // Resizing Behavior
    public enum ResizingBehavior {
        AspectFit, //!< The content is proportionally resized to fit into the target rectangle.
        AspectFill, //!< The content is proportionally resized to completely fill the target rectangle.
        Stretch, //!< The content is stretched to match the entire target rectangle.
        Center, //!< The content is centered in the target rectangle, but it is NOT resized.
    }

    // In Trial version of PaintCode, the code generation is limited to 3 canvases.

    // Canvas Drawings
    // Tab

    private static class CacheForCard {
        private static Paint paint = new Paint();
        private static RectF bezier2Rect = new RectF();
        private static Path bezier2Path = new Path();
    }


    public static void drawCard(Canvas canvas, RectF frame) {
        // General Declarations
        Paint paint = CacheForCard.paint;

        // Local Colors
        int strokeColor = Color.argb(255, 215, 215, 215);
        int fillColor = Color.argb(255, 255, 255, 255);

        // Bezier 2
        RectF bezier2Rect = CacheForCard.bezier2Rect;
        bezier2Rect.set(frame.left + 19.5f,
            frame.top + 16.5f,
            frame.left + 370.5f,
            frame.top + 334.5f);
        Path bezier2Path = CacheForCard.bezier2Path;
        bezier2Path.reset();
        bezier2Path.moveTo(frame.left + frame.width() * 0.57926f, frame.bottom - 23.5f);
        bezier2Path.cubicTo(frame.left + frame.width() * 0.57018f, frame.bottom - 37.3f, frame.left + frame.width() * 0.53813f, frame.bottom - 47.5f, frame.left + frame.width() * 0.5f, frame.bottom - 47.5f);
        bezier2Path.cubicTo(frame.left + frame.width() * 0.46187f, frame.bottom - 47.5f, frame.left + frame.width() * 0.42982f, frame.bottom - 37.3f, frame.left + frame.width() * 0.42074f, frame.bottom - 23.5f);
        bezier2Path.lineTo(frame.left + 39.5f, frame.bottom - 23.5f);
        bezier2Path.cubicTo(frame.left + 28.45f, frame.bottom - 23.5f, frame.left + 19.5f, frame.bottom - 32.45f, frame.left + 19.5f, frame.bottom - 43.5f);
        bezier2Path.lineTo(frame.left + 19.5f, frame.top + 36.5f);
        bezier2Path.cubicTo(frame.left + 19.5f, frame.top + 25.45f, frame.left + 28.45f, frame.top + 16.5f, frame.left + 39.5f, frame.top + 16.5f);
        bezier2Path.lineTo(frame.right - 40.5f, frame.top + 16.5f);
        bezier2Path.cubicTo(frame.right - 29.45f, frame.top + 16.5f, frame.right - 20.5f, frame.top + 25.45f, frame.right - 20.5f, frame.top + 36.5f);
        bezier2Path.lineTo(frame.right - 20.5f, frame.bottom - 43.5f);
        bezier2Path.cubicTo(frame.right - 20.5f, frame.bottom - 32.45f, frame.right - 29.45f, frame.bottom - 23.5f, frame.right - 40.5f, frame.bottom - 23.5f);
        bezier2Path.lineTo(frame.left + frame.width() * 0.57926f, frame.bottom - 23.5f);
        bezier2Path.close();

        paint.reset();
        paint.setFlags(Paint.ANTI_ALIAS_FLAG);
        bezier2Path.setFillType(Path.FillType.EVEN_ODD);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(fillColor);
        canvas.drawPath(bezier2Path, paint);

        paint.reset();
        paint.setFlags(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(1f);
        paint.setStrokeMiter(10f);
        canvas.save();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(strokeColor);
        canvas.drawPath(bezier2Path, paint);
        canvas.restore();
    }


    // Resizing Behavior
    public static void resizingBehaviorApply(ResizingBehavior behavior, RectF rect, RectF target, RectF result) {
        if (rect.equals(target) || target == null) {
            result.set(rect);
            return;
        }

        if (behavior == ResizingBehavior.Stretch) {
            result.set(target);
            return;
        }

        float xRatio = Math.abs(target.width() / rect.width());
        float yRatio = Math.abs(target.height() / rect.height());
        float scale = 0f;

        switch (behavior) {
            case AspectFit: {
                scale = Math.min(xRatio, yRatio);
                break;
            }
            case AspectFill: {
                scale = Math.max(xRatio, yRatio);
                break;
            }
            case Center: {
                scale = 1f;
                break;
            }
        }

        float newWidth = Math.abs(rect.width() * scale);
        float newHeight = Math.abs(rect.height() * scale);
        result.set(target.centerX() - newWidth / 2,
            target.centerY() - newHeight / 2,
            target.centerX() + newWidth / 2,
            target.centerY() + newHeight / 2);
    }


}

但是......它仍然不起作用,因为它没有对措施调整大小进行优化。也许是拼写“拼写”的最佳方式?)

1 个答案:

答案 0 :(得分:1)

您可以使用Canvas系列和(来自API 26){{{}来控制Canvas的当前剪辑,即clipXXX所使用的区域1}}方法。您可以找到记录here的这些方法。

要解决您的问题,您可以准备一个描述底部圆圈的clipOutXXX并将其剪切出Path

首先,为了避免在自定义Canvas的draw方法中分配对象,您可能会有一个字段:

Drawable

现在,只要您的自定义drawable的边界发生变化,您就会重新计算圆圈的位置:

private Path bottomCircle = new Path();

最后你画出你的背景:

@Override
protected void onBoundsChange(Rect bounds) {
    super.onBoundsChange(bounds);

    bottomCircle.reset();
    bottomCircle.addCircle(bounds.centerX(), bounds.bottom, bottomCircleRadius, CW);
}