Android: 如何只剪掉顶部的圆角
Android: how to clip only top rounded corners
我正在创建一个内部有 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>
剪辑得很好。
那么,如果我只想让顶部变角,我该如何剪裁它。
我已经通过创建自定义 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);
这是@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
}
我从@Jankers 和@Dragan 那里获取了解决方案并对其进行了压缩,并添加了数据绑定以便可以从 xml.
完成
NOTE: Clipping with outline does not support corners to be different
sizes!
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}"
...
我正在创建一个内部有 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>
剪辑得很好。
那么,如果我只想让顶部变角,我该如何剪裁它。
我已经通过创建自定义 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);
这是@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
}
我从@Jankers 和@Dragan 那里获取了解决方案并对其进行了压缩,并添加了数据绑定以便可以从 xml.
完成NOTE: Clipping with outline does not support corners to be different sizes!
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}"
...