我想创建一个通用的ViewGroup,然后可以在XML布局中重用它以使放入其中的任何内容都变得圆满。
由于某种原因,canvas.clipPath()
似乎没有效果。我究竟做错了什么?
这是Java代码:
package rounded;
import static android.graphics.Path.Direction.CCW;
public class RoundedView extends FrameLayout {
private float radius;
private Path path = new Path();
private RectF rect = new RectF();
public RoundedView(Context context, AttributeSet attrs) {
super(context, attrs);
this.radius = attrs.getAttributeFloatValue(null, "corner_radius", 0f);
}
@Override
protected void onDraw(Canvas canvas) {
int savedState = canvas.save();
float w = getWidth();
float h = getHeight();
path.reset();
rect.set(0, 0, w, h);
path.addRoundRect(rect, radius, radius, CCW);
path.close();
boolean debug = canvas.clipPath(path);
super.onDraw(canvas);
canvas.restoreToCount(savedState);
}
}
在XML中的用法:
<?xml version="1.0" encoding="utf-8"?>
<rounded.RoundedView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
corner_radius="40.0" >
<RelativeLayout
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
...
</RelativeLayout>
</rounded.RoundedView>
最佳答案
创建裁剪其子项的ViewGroup
的正确方法是在dispatchDraw(Canvas)
方法中进行操作。
这是一个示例,说明如何使用圆圈剪切ViewGroup
的所有子级:
private Path path = new Path();
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// compute the path
float halfWidth = w / 2f;
float halfHeight = h / 2f;
float centerX = halfWidth;
float centerY = halfHeight;
path.reset();
path.addCircle(centerX, centerY, Math.min(halfWidth, halfHeight), Path.Direction.CW);
path.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(circlePath);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
dispatchDraw
方法是一种用于裁剪子对象的方法。如果您的布局仅剪切其子级,则无需setWillNotDraw(false)
。该图像是通过上面的代码获得的,我只是扩展了Facebook
ProfilePictureView
(这是一个FrameLayout
,其中包括带有facebook个人资料图片的正方形ImageView
):因此,要实现圆形边框,您需要执行以下操作:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// compute the path
path.reset();
rect.set(0, 0, w, h);
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
path.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
您实际上可以创建任何复杂的路径:)
请记住,您可以通过“Op”操作多次调用clipPath,以便以您喜欢的方式与多个裁剪相交。
注意:我在
onSizeChanged
中创建了路径,因为在onDraw
中这样做会降低性能。注意2:剪切路径是在没有抗锯齿的情况下完成的:/,因此,如果要平滑边框,则需要以其他方式进行。我现在还不知道有什么方法可以使剪辑使用抗锯齿功能。
更新(概述)
由于Android Lollipop(API 21),可以将高程和阴影应用于 View 。引入了一个名为Outline的新概念。这是一条告诉框架将compute the shadow和其他事物(如涟漪效应)使用的 View 形状的路径。
View 的默认
Outline
是 View 大小的矩形,但可以轻松地将其设置为椭圆/圆形或圆角矩形。要定义自定义Outline
,您必须在 View 上使用方法 setOutlineProvider()
,如果它是自定义View,则可能需要在构造函数中使用自定义ViewOutlineProvider
定义为自定义View的内部类来对其进行设置。您可以使用自己选择的Outline
定义自己的Path
提供程序,只要它是convex path(数学概念表示没有凹口且没有孔的闭合路径,例如,星形和齿轮形都不是凸形的)。您也可以使用
setClipToOutline(true)
方法来剪辑Outline(我认为这也适用于抗锯齿,有人可以确认/驳斥注释吗?),但这仅适用于非Path
Outline。祝你好运