使用裁剪可以将ViewGroup的角落变为圆形

18

我有一个RelativeLayout需要拥有圆角的左上角和右上角。我可以通过在XML中定义具有topLeftRadius和topRightRadius属性的drawable背景来实现这一点。 但是...这个RelativeLayout还需要一个图层列表作为背景,其中包含平铺位图和形状组合,而平铺位图在drawable XML中没有corners参数。 所以我的想法是使用以下代码创建RelativeLayout

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

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

不幸的是,没有发生任何裁剪。我原本希望它可以裁剪RelativeLayout的四个角落,但什么也没发生。"onSizeChanged"和"dispatchDraw"方法都被调用了,我已经测试过了。我还尝试关闭硬件加速,但没有任何效果。

我的RelativeLayout是一个更大布局的一部分,在FrameLayout的子类中膨胀,然后该子类再在RecyclerView的行中使用。如果这会有任何改变,请注意。

2个回答

27

定义了这个布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent">

    <com.playground.RoundedRelativeLayout
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="@color/colorPrimary" />

</FrameLayout>

其中,RoundedRelativeLayout具有以下实现:


    public class RoundedRelativeLayout extends RelativeLayout {
private RectF rectF; private Path path = new Path(); private float cornerRadius = 15;
public RoundedRelativeLayout(Context context) { super(context); }
public RoundedRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); }
public RoundedRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); rectF = new RectF(0, 0, w, h); resetPath(); }
@Override public void draw(Canvas canvas) { int save = canvas.save(); canvas.clipPath(path); super.draw(canvas); canvas.restoreToCount(save); }
@Override protected void dispatchDraw(Canvas canvas) { int save = canvas.save(); canvas.clipPath(path); super.dispatchDraw(canvas); canvas.restoreToCount(save); }
private void resetPath() { // 重置路径,创建一个圆角矩形路径,并将其添加到当前路径中 path.reset(); path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW); path.close(); } }

您将获得以下输出:

该实现无耻地从RoundKornerLayouts项目中窃取。


哈,我有完全相同的代码,只是结构略有不同,但我缺少了绘制重写。我以为只有dispatchDraw会被调用,而不是draw,对于ViewGroup来说。 - Neigaard
因为您正在将背景应用于“ViewGroup”,这会导致调用“ViewGroup”的“onDraw()”方法,否则默认情况下不会调用该方法,除非通过“setWillNotDraw(false)”API明确指示。 - azizbekian

9

这是azizbekian答案的Kotlin版本:

class RoundedRelativeLayout(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {

    private lateinit var rectF: RectF
    private val path = Path()
    private var cornerRadius = 15f

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        rectF = RectF(0f, 0f, w.toFloat(), h.toFloat())
        resetPath()
    }

    override fun draw(canvas: Canvas) {
        val save = canvas.save()
        canvas.clipPath(path)
        super.draw(canvas)
        canvas.restoreToCount(save)
    }

    override fun dispatchDraw(canvas: Canvas) {
        val save = canvas.save()
        canvas.clipPath(path)
        super.dispatchDraw(canvas)
        canvas.restoreToCount(save)
    }

    private fun resetPath() {
        path.reset()
        path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW)
        path.close()
    }
}

编辑

作为一个额外的xml属性,您可以添加cornerRadius,并将其设置为以下内容,只需将其添加到res/values/styleable.xml中即可:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundedRelativeLayout">
        <attr name="cornerRadius" format="float"/>
    </declare-styleable>
</resources>

然后将此初始化方法添加到RoundedRelativeLayout类中:

init {
    val ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundedRelativeLayout)
    cornerRadius = ta.getFloat(R.styleable.RoundedRelativeLayout_cornerRadius, 15f)
    ta.recycle()
}

现在当您使用布局时,您可以在XML中像这样设置cornerRadius:

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto" <-- Make sure you include this line
        android:layout_width="80dp"
        android:layout_height="80dp">

    .
    .
    .

    <your.package.name.RoundedRelativeLayout
            android:id="@+id/roundedRect"
            app:cornerRadius="24"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    .
    .
    .


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接