简介
在开发过程中,规划常常会有一些比较炫酷的主意,比方两边不相同巨细的圆角啦,乃至四角的radius
各不相同,对于这种情况咱们该怎么完成呢?
布景圆角
Shape
对于一般的布景,咱们能够直接运用shape
,这种办法天生支持设置四角不同的radius
,比方:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#8358FF" />
<corners
android:bottomLeftRadius="10dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="30dp"
android:topRightRadius="40dp" />
</shape>
小贴士:shape
在代码层的完成为GradientDrawable
,能够直接在代码层构建圆角布景,顺便引荐一下我写的库:ShapeLayout,能够便利的完成shape
布景,离别xml
内容圆角
许多情况下,设置布景的四边不同圆角并不能满足咱们,大多数情况下,咱们需求连着里边的内容一同切圆角,这儿咱们需求先纠正一下网上的一个过错写法
有人发文说,能够经过outline.setConvexPath
办法,完成四角不同radius
,如下:
outline?.setConvexPath(
Path().apply {
addRoundRect(
0f, 0f, width.toFloat(), height.toFloat(),
floatArrayOf(
topLeftRadius,
topLeftRadius,
topRightRadius,
topRightRadius,
bottomRightRadius,
bottomRightRadius,
bottomLeftRadius,
bottomLeftRadius
),
Path.Direction.CCW
)
}
)
经过实测,这样写是不可的,精确的来说,在大部分体系上是不可的(MIUI上能够,我不知道是该夸它兼容性太好了仍是该吐槽它啥,我的测验机用的小米,这导致我在最终的测验阶段才发现这个问题)
指出过错办法后,让咱们来看看正确解法有哪些
CardView
提到切内容圆角,咱们自然而然会去想到CardView
,其实CardView
的圆角也是经过Outline
完成的
有人可能要问了,CardView
不是只支持四角相同radius
吗?别急,且看我灵机一动想出来的奇特嵌套大法
奇特嵌套大法
已然一个CardView
只能设一个radius
,那我多用几个CardView
嵌套是否能解决问题呢?
举个最简单的比如,比方说规划想要上半部分为12dp
的圆角,下半部分没有圆角,咱们需求一个辅佐View
,让他的顶部和父布局的底部对齐,然后设置成圆角巨细的高度或许margin
,接着运用CardView
,让它的底部对齐这个辅佐View
的底部,再设置一个圆角巨细的padding
,这样,由于CardView
超出了父布局的鸿沟,所以底部的圆角不会显示出来,再由于咱们设置了恰好的padding
,所以CardView
里边的内容也能完好展现,可谓完美,实例如下:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Space
android:id="@+id/guideline"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
上面的比如没有嵌套,因为另一边没有圆角,那么假如咱们需求上半部分为12dp
的圆角,下半部分为6dp
的圆角,咱们能够这样操作
手法和上面的比如相同,不过咱们在最外层再嵌套一个CardView
,并且将其圆角设为较小的那个圆角巨细6dp
,将里边的CardView
的圆角设置成较大的那个圆角巨细12dp
,详细完成如下:
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
本质上就是大圆角套小圆角,大圆角的裁切规模更大,会覆盖小圆角裁切的规模,从视觉上看就完成了两边的不同圆角
那么假如咱们想进一步完成三边不同圆角或许四边不同圆角呢?原理和上面是相同的,只不过嵌套和占位会变得愈加复杂,记住一个原则,小圆角在外,大圆角在内即可,我直接把详细完成贴在下面,各位自取即可:
- 三边不同圆角(左下6dp,左上12dp,右上24dp)
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Space
android:id="@+id/guideline"
android:layout_width="6dp"
android:layout_height="match_parent"
app:layout_constraintStart_toEndOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:contentPaddingRight="6dp"
app:layout_constraintEnd_toEndOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toStartOf="parent" />
<Space
android:id="@+id/guideline4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:contentPaddingBottom="24dp"
app:contentPaddingLeft="24dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline3">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
- 四边不同圆角(左下6dp,左上12dp,右上24dp,右下48dp)
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Space
android:id="@+id/guideline"
android:layout_width="6dp"
android:layout_height="match_parent"
app:layout_constraintStart_toEndOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="6dp"
app:cardElevation="0dp"
app:contentPaddingRight="6dp"
app:layout_constraintEnd_toEndOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:contentPaddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toStartOf="parent" />
<Space
android:id="@+id/guideline4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:contentPaddingBottom="24dp"
app:contentPaddingLeft="24dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline3">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Space
android:id="@+id/guideline5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="48dp"
app:layout_constraintEnd_toStartOf="parent" />
<Space
android:id="@+id/guideline6"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="48dp"
app:layout_constraintBottom_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="48dp"
app:cardElevation="0dp"
app:contentPaddingLeft="48dp"
app:contentPaddingTop="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="@+id/guideline6">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:adjustViewBounds="true"
android:background="#8358FF" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
自定义ImageView
由于大部分裁切内容的需求,其中的内容都是图片,所以咱们也能够直接对图片进行裁切,此时咱们就能够自定义ImageView
来将图片裁剪出不同巨细的圆角
clipPath
先说这个办法的缺陷,那就是无法运用抗锯齿,这一点缺陷注定了它无法被正式运用,但咱们仍是来看看他是如何完成的
首先,咱们需求重写ImageView
的onSizeChanged
办法,为咱们的Path
确认道路
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
path.reset()
//这儿的radii就是咱们自定义的四边圆角巨细的数组(size为8,从左上顺时针到左下)
path.addRoundRect(0f, 0f, w.toFloat(), h.toFloat(), radii, Path.Direction.CW)
}
接着咱们重写onDraw
办法
override fun onDraw(canvas: Canvas) {
canvas.clipPath(path)
super.onDraw(rawBitmapCanvas)
}
网上有的教程说要设置PaintFlagsDrawFilter
,但实际上就算为这个PaintFlagsDrawFilter
设置了Paint.ANTI_ALIAS_FLAG
抗锯齿属性也没用,抗锯齿只在运用了Paint
的情况下才能够收效
PorterDuff
已然clipPath
无法运用抗锯齿,那咱们能够换一条道路曲线救国,那就是运用PorterDuff
当然,这种办法也有它的缺陷,那就是不能运用硬件加速,但比较无法运用抗锯齿而言,这点缺陷也就不算什么了
首先,咱们要在结构办法中禁用硬件加速
init {
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
然后重写onSizeChanged
办法,在这个办法中,咱们需求确认Path
,结构出相应巨细的Bitmap
和Canvas
,这俩是用来获取原始无圆角的Bitmap
的
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
path.reset()
path.addRoundRect(0f, 0f, w.toFloat(), h.toFloat(), radii, Path.Direction.CW)
rawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
rawBitmapCanvas = Canvas(rawBitmap!!)
}
接着咱们重写onDraw
办法
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
override fun onDraw(canvas: Canvas) {
val rawBitmap = rawBitmap ?: return
val rawBitmapCanvas = rawBitmapCanvas ?: return
super.onDraw(rawBitmapCanvas)
canvas.drawPath(path, paint)
paint.xfermode = xfermode
canvas.drawBitmap(rawBitmap, 0f, 0f, paint)
paint.xfermode = null
}
这儿,咱们调用父类的onDraw
办法,获取到原始无圆角的Bitmap
,然后制作Path
,再经过PorterDuff
的叠加作用制作咱们刚刚得到的原始Bitmap
,由于PorterDuff.Mode.SRC_IN
的作用是取两层制作交集,显示上层,所以咱们最终便获得了一个带圆角的图片
截图问题
假如想要将View
截图成Bitmap
,在Android 8.0
及以上体系中咱们能够运用PixelCopy
,此时运用CardView
或Outline
裁切的圆角不会有任何问题,而在Android 8.0
以下的体系中,一般咱们是构建一个带Bitmap
的Canvas
,然后对要截图的View
调用draw
办法达成截图作用,而在这种情况下,运用CardView
或Outline
裁切的圆角便会呈现无效的情况(截图出来的Bitmap
中,圆角没了),这种情况的呈现似乎也和硬件加速有关,针对这种问题,我个人的主意是准备两套布局,8.0
以上运用CardView
或Outline
,截图运用PixelCopy
,8.0
以下运用PorterDuff
计划直接裁切图片,最大程度防止功能损耗
总结
以上就是我本人目前对Android
完成不同巨细的圆角的一些主意和遇到的问题,至于CardView
嵌套会不会带来什么功能问题,我目前并没有做验证,各位小伙伴有什么更好的解决计划,欢迎在评论区指出,大家一同集思广益