【问题标题】:Use clipping to round corners of ViewGroup使用裁剪来圆化 ViewGroup 的角
【发布时间】:2017-11-10 13:03:02
【问题描述】:

我有一个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


    【解决方案1】:

    已定义此布局:

    <?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项目中窃取了实现。

    【讨论】:

    • 哈,我有完全相同的代码,只是结构略有不同,但我错过了绘制覆盖。我以为 ViewGroup 只会调用 dispatchDraw,而不是 draw。
    • 因为您正在为ViewGroup 应用背景,所以会调用ViewGrouponDraw() 方法,否则默认情况下它会,除非通过setWillNotDraw(false) API 明确指示。
    【解决方案2】:

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

    编辑

    作为奖励,这里是如何将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"/>
    
        .
        .
        .
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      • 1970-01-01
      • 1970-01-01
      • 2016-12-31
      • 1970-01-01
      • 2013-05-21
      • 1970-01-01
      相关资源
      最近更新 更多