Android:如何仅剪切顶部圆角

Posted

技术标签:

【中文标题】Android:如何仅剪切顶部圆角【英文标题】:Android: how to clip only top rounded corners 【发布时间】:2019-09-21 08:50:33 【问题描述】:

我正在创建一个内部带有 FrameLayout 的 ScrollView。我想设计它,以便在 ScrollView 上只有顶角是圆角的。我创建了一个可绘制的形状,如下所示

<shape>
    <solid android:color="@color/white"/>
    <corners
        android:bottomLeftRadius="0dp"
        android:bottomRightRadius="0dp"
        android:topLeftRadius="16dp"
        android:topRightRadius="16dp"/>
    <padding android:padding="0dp"/>
</shape>

然后我在 ScrollView 上设置了以下内容

 scrollView.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
 scrollView.setClipToOutline(true);

当我尝试滚动时,FrameLayout 中的元素最终会突出到我的滚动视图的轮廓中

Excuse the drawing, but what i'm looking to achieve

但是,如果我改为创建这样的形状

<shape>
    <solid android:color="@color/white"/>
    <corners
        android:radius="16dp"/>
    <padding android:padding="0dp"/>
</shape> 

它剪辑得很好。

如果我只希望顶部被拐角,我将如何剪辑它。

【问题讨论】:

【参考方案1】:

这是@Jankers 答案的 Kotlin 变体,也是对@Tony 问题的回答。说明如何仅放置一个圆角或两个相邻的圆角。

  fun setCorners() 
        
        val outlineProvider = object : ViewOutlineProvider() 
            override fun getOutline(view: View, outline: Outline) 

                val left = 0
                val top = 0;
                val right = view.width
                val bottom = view.height
                val cornerRadiusDP = 16f
                val cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, resources.displayMetrics).toInt()

                // all corners
                outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat())

                /* top corners
                outline.setRoundRect(left, top, right, bottom+cornerRadius, cornerRadius.toFloat())*/

                /* bottom corners
                outline.setRoundRect(left, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

                /* left corners
                outline.setRoundRect(left, top, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* right corners
                outline.setRoundRect(left - cornerRadius, top, right, bottom, cornerRadius.toFloat())*/

                /* top left corner
                outline.setRoundRect(left , top, right+ cornerRadius, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* top right corner
                outline.setRoundRect(left - cornerRadius , top, right, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* bottom left corner
                outline.setRoundRect(left, top - cornerRadius, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* bottom right corner
                outline.setRoundRect(left - cornerRadius, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

            
        

        myView.outlineProvider = outlineProvider
        myView.clipToOutline = true

    

【讨论】:

【参考方案2】:

我已经设法通过创建自定义 ViewOutlineProvider 并使用它而不是背景值来实现此功能

ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() 
    @Override
    public void getOutline(final View view, final Outline outline) 
        float cornerRadiusDP = 16f;
        float cornerRadius = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, getResources().getDisplayMetrics());
            outline.setRoundRect(0, 0, view.getWidth(), (int)(view.getHeight() + cornerRadius), cornerRadius);
        
;
scrollView.setOutlineProvider(mViewOutlineProvider);
scrollView.setClipToOutline(true);

【讨论】:

非常有用的答案。为我工作。谢谢。 只对一个角落怎么做 @Tony 我不是 100% 确定,但也许可以尝试查看 outline.setRoundRect 方法,看看是否可以使用它来选择一个角 原理是什么【参考方案3】:

我从@Jankers 和@Dragan 那里得到了解决方案并对其进行了浓缩,并添加了数据绑定,以便可以从xml 中完成。

注意:带轮廓的剪裁不支持角落不同 尺寸!

OutlineProviders:

class RoundedCornersOutlineProvider(
    val radius: Float? = null,
    val topLeft: Float? = null,
    val topRight: Float? = null,
    val bottomLeft: Float? = null,
    val bottomRight: Float? = null,
) : ViewOutlineProvider() 

    private val topCorners = topLeft != null && topLeft == topRight
    private val rightCorners = topRight != null && topRight == bottomRight
    private val bottomCorners = bottomLeft != null && bottomLeft == bottomRight
    private val leftCorners = topLeft != null && topLeft == bottomLeft
    private val topLeftCorner = topLeft != null
    private val topRightCorner = topRight != null
    private val bottomRightCorner = bottomRight != null
    private val bottomLeftCorner = bottomLeft != null

    override fun getOutline(view: View, outline: Outline) 
        val left = 0
        val top = 0
        val right = view.width
        val bottom = view.height

        if (radius != null) 
            val cornerRadius = radius //.typedValue(resources).toFloat()
            outline.setRoundRect(left, top, right, bottom, cornerRadius)
         else 
            val cornerRadius = topLeft ?: topRight ?: bottomLeft ?: bottomRight ?: 0F

            when 
                topCorners -> outline.setRoundRect(left, top, right, bottom + cornerRadius.toInt(), cornerRadius)
                bottomCorners -> outline.setRoundRect(left, top - cornerRadius.toInt(), right, bottom, cornerRadius)
                leftCorners -> outline.setRoundRect(left, top, right + cornerRadius.toInt(), bottom, cornerRadius)
                rightCorners -> outline.setRoundRect(left - cornerRadius.toInt(), top, right, bottom, cornerRadius)
                topLeftCorner -> outline.setRoundRect(
                    left, top, right + cornerRadius.toInt(), bottom + cornerRadius.toInt(), cornerRadius
                )
                bottomLeftCorner -> outline.setRoundRect(
                    left, top - cornerRadius.toInt(), right + cornerRadius.toInt(), bottom, cornerRadius
                )
                topRightCorner -> outline.setRoundRect(
                    left - cornerRadius.toInt(), top, right, bottom + cornerRadius.toInt(), cornerRadius
                )
                bottomRightCorner -> outline.setRoundRect(
                    left - cornerRadius.toInt(), top - cornerRadius.toInt(), right, bottom, cornerRadius
                )
            
        
    


class CircleOutlineProvider : ViewOutlineProvider() 
    override fun getOutline(view: View, outline: Outline) 
        val size = view.run  min(width, height) 
        outline.setRoundRect(0, 0, size, size, (size).toFloat())
    

Data Binding (@BindingAdapter):

@BindingAdapter("clipCircle")
fun View.bindClipCircle(clipCircle: Boolean?) 
    outlineProvider = CircleOutlineProvider()
    clipToOutline = true


@BindingAdapter("clipRadius", "clipTopLeft", "clipTopRight", "clipBottomLeft", "clipBottomRight", requireAll = false)
fun View.bindClipCorners(radius: Float?, topLeft: Float?, topRight: Float?, bottomLeft: Float?, bottomRight: Float?) 
    this.outlineProvider = RoundedCornersOutlineProvider(radius, topLeft, topRight, bottomLeft, bottomRight)
    this.clipToOutline = true

Clipping in xml

<androidx.constraintlayout.widget.ConstraintLayout
    clipCircle="@@bool/const_true"
    ...

<ImageView
    clipBottomLeft="@@dimen/green_tab_corner_radius"
    clipBottomRight="@@dimen/green_tab_corner_radius"
    ...

【讨论】:

以上是关于Android:如何仅剪切顶部圆角的主要内容,如果未能解决你的问题,请参考以下文章

Android - 如何创建具有圆角和平铺图像背景的视图?

Android:菜单中的圆角

选择最佳 Android 布局

android 自定义progressbar怎么让右侧也是圆角

如何在android中创建形状三角形

如何剪切面具上的列表视图?