使用剪切来修复ViewGroup的圆角

Nei*_*ard 14 java android android-custom-view android-layout android-view

我有一个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);
}
Run Code Online (Sandbox Code Playgroud)

可悲的是没有剪辑发生,我期待它剪辑我的RelativeLayout的所有四个角落,但没有任何事情发生."onSizeChanged"和"dispatchDraw"方法都被调用,我测试过.我也试图关闭硬件加速,但它什么也没做.

RelativeLayout是一个更大的布局的一部分,并且该布局在子类中膨胀FrameLayout,然后该子类在a中使用a行RecyclerView,如果它改变任何东西.

azi*_*ian 19

定义了这个布局:

<?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>
Run Code Online (Sandbox Code Playgroud)

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项目的实施被无耻地窃取.


Qui*_*inn 8

这是 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

作为奖励,这里介绍了如何将 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>
Run Code Online (Sandbox Code Playgroud)

然后将此 init 方法添加到RoundedRelativeLayout类中:

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

现在,当您使用布局时,您可以像这样在 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"/>

    .
    .
    .

Run Code Online (Sandbox Code Playgroud)