使用剪切来修复ViewGroup的圆角

问题描述 投票:13回答:2

我有一个需要左上角和右上角的RelativeLayout。我可以使用XML定义的可绘制背景来实现这一点,其中包含角topLeftRadius和topRightRadius。但是......这个RelativeLayout还需要一个背景,它是一个带有平铺位图和形状组合的图层列表,并且平铺的位图在可绘制的XML中没有一个角参数。所以我的想法是用以下代码制作一个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中使用了一行,如果它改变了什么。

java android android-layout android-view android-custom-view
2个回答
18
投票

定义了这个布局:

<?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项目无耻地窃取了实施。


1
投票

这是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()
    }
}

Edit

作为奖励,这里是如何将cornerRadius添加为您可以设置的额外xml属性,只需将其添加到res/values/styleable.xml

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

然后将此init方法添加到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"/>

    .
    .
    .

© www.soinside.com 2019 - 2024. All rights reserved.