android - 自定义布局,使其内容的角落变得圆润

标签 android android-layout

我想创建一个通用的 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 包括一个方形 ImageView 与 facebook个人资料图片):

circle clipping

所以要实现圆形边框,你可以这样做:

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

round border clipping

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

请记住,您可以使用您喜欢的“Op”操作多次调用clipPath,以您喜欢的方式与多个剪辑相交。

注意:我在 onSizeChanged 中创建了路径,因为在 onDraw 中这样做不利于性能。

注意2:剪切路径是在没有抗锯齿的情况下完成的:/所以如果你想要平滑的边框,你需要以其他方式来做。我现在不知道有什么方法可以让剪辑使用抗锯齿。

更新(大纲)

由于 Android Lollipop (API 21) 可以将高程和阴影应用于 View 。一个名为 Outline 的新概念已介绍。这是一条路径,告诉框架要使用的 View 的形状 compute the shadow和其他东西(如涟漪效应)。

View 的默认 Outline 是 View 大小的矩形,但可以很容易地制作成椭圆形/圆形或圆角矩形。要定义自定义 Outline,您必须使用方法 setOutlineProvider()在 View 上,如果它是自定义 View ,您可能希望在构造函数中将其设置为自定义 ViewOutlineProvider 定义为自定义 View 的内部类。您可以使用您选择的 Path 定义自己的 Outline 提供程序,只要它是 convex path (数学概念是指没有凹槽和孔的封闭路径,例如星形和齿轮形都不是凸形的)。

您也可以使用setClipToOutline(true)的方法使大纲也剪辑(我认为这也适用于抗锯齿,有人可以在评论中确认/反驳吗?),但这仅支持非路径大纲。

祝你好运

关于android - 自定义布局,使其内容的角落变得圆润,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13517221/

相关文章:

android - 尽管实现了 doOnError,但 RxAndroid 使应用程序崩溃

android - 在 Android 布局中显示 3 个等高的可滚动 ListView

android - 将 DrawerLayout 与 ListFragment 一起使用时,过度敏感的 DrawerLayout 会中断垂直滚动

android - 如何更改操作栏上菜单项的位置

java - 在简历上重新加载 SharedPreferences? (或如何刷新/重新加载 Activity )

android - 如何在每次单击 WebView 中的链接时显示 ProgressDialog

android - 更改 ProgressDialog 的背景

android - 如何实现注销功能

android - float 操作按钮不响应单击

java - HashMap<String,Object>.toMap 的意义是什么