Canvas clipPath clipRect不生效

Canvas clipPath clipRect不生效

自定义drawable调用canvas.clipPath不生效。我想要设置一个圆形背景,在Drawable.draw()中调用clipPath裁剪

居然没效果。代码如下:


class BorderDrawable() : Drawable() {

    //.....
    override fun draw(canvas: Canvas) {
        canvas.clipPath(mClipPath)
    }
    //.....
}

查了半天发现,把硬件加速关了,就可以了。
在这里插入图片描述

原因

View的背景是在drawBackground中绘制的,在drawBackground方法中判断了是否打开硬件加速。

开启硬件加速的调用栈:drawableBackground->getDrawableRenderNode()->drawable.draw(canvas)。

关闭硬件加速的调用栈: drawableBackground->background.draw(canvas)。

bakgrand.draw()drawable.draw()就是自定义Drawable的draw()方法。

//View
public void draw(@NonNull Canvas canvas) {
    int saveCount;

    //画背景
    drawBackground(canvas);

    // skip step 2 & 5 if possible (common case)
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
        // Step 3, draw the content
        onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        drawDefaultFocusHighlight(canvas);

        if (isShowingLayoutBounds()) {
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }
}

在调用的Drawable的draw()方法后,又调用了renderNode.setClipToBounds(false)。

private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
	//......
    try {
        drawable.draw(canvas);
    } finally {
        renderNode.endRecording();
    }

    // Set up drawable properties that are view-independent.
    renderNode.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
    renderNode.setProjectBackwards(drawable.isProjected());
    renderNode.setProjectionReceiver(true);
    //如果设置为 true,则 RenderNode 的绘制内容将被限制在其边界内
    //如果设置为 false,则 RenderNode 的绘制内容将不受边界限制。
    renderNode.setClipToBounds(false);
    return renderNode;
}

开启硬件加速后,将运行绘制的内容超过背景边界。

置于为什么Android这么做,代码里的注释是:Set up drawable properties that are view-independent
drawable的属性与view独立,与view无关的drawable属性。Android应该想要的是drawable和view不相互依赖。

解决方法

方法一:关闭硬件加速

关闭硬件加速:Android中禁用硬件加速的几种方式_android 禁用硬件加速-CSDN博客

方法二:把要裁剪的图片放在Drawable.draw()中。

开启硬件加速后,会在drawable.draw()之后调用renderNode.setClipToBounds(false) 运行绘制的内容超过边界。那可以把背景的裁剪都放在Drawable.draw() 中完成。代码如下:

public class RoundCornerDrawable extends DrawableWrapper {

    private final RectF mTempRect = new RectF();
    private final Path mClipPath = new Path();
    private float mRoundedCornersRadius = 0f;


	/**
	*@param dr 需要裁剪的drawable
	*/
    public RoundCornerDrawable(Drawable dr, float radius) {
        super(dr);
        mRoundedCornersRadius = radius;
    }

    public RoundCornerDrawable(Drawable dr) {
        super(dr);
    }

    public float getRadius() {
        return mRoundedCornersRadius;
    }

    public void setRadius(float radius) {
        mRoundedCornersRadius = radius;
    }


    @Override
    protected void onBoundsChange(@NonNull Rect bounds) {
        mTempRect.set(getBounds());
        mClipPath.reset();
        mClipPath.addRoundRect(mTempRect, mRoundedCornersRadius,
                mRoundedCornersRadius, Path.Direction.CCW);
        super.onBoundsChange(bounds);
    }

    @Override
    public final void draw(@NonNull Canvas canvas) {
        int saveCount = canvas.save();
        canvas.clipPath(mClipPath);
        //canvas裁剪之后 就马上绘制drawable
        super.draw(canvas);
        canvas.restoreToCount(saveCount);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值