我想创建一个通用的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):

android - 自定义布局,圆满完成其内容-LMLPHP

因此,要实现圆形边框,您需要执行以下操作:
@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);
}

android - 自定义布局,圆满完成其内容-LMLPHP

您实际上可以创建任何复杂的路径:)

请记住,您可以通过“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。

祝你好运

10-07 19:23
查看更多