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);
}
}