马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
一、弁言
在 Android 开发中,圆角筹划是提升界面美观度和用户体验的重要手段。从简朴的按钮到复杂的布局容器,圆角可以让界面元素显得更加柔和、现代。本文将全面梳理 Android View 设置圆角的全部方式,涵盖 XML 设置、代码动态实现、自定义绘制、第三方库等多个维度,并联合源码深入分析其原理,同时给出性能优化的最佳实践。
二、XML 布局文件设置圆角
2.1 使用 ShapeDrawable
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <!-- 圆角半径,这里统一设置四个角的圆角半径为 16dp -->
- <corners android:radius="16dp"/>
- <!-- 设置填充颜色为粉色 -->
- <solid android:color="#FF4081"/>
- <!-- 设置边框,宽度为 2dp,颜色为黄色 -->
- <stroke
- android:width="2dp"
- android:color="#FFEB3B"/>
- <!-- 设置内边距,上下为 8dp,左右为 16dp -->
- <padding
- android:top="8dp"
- android:bottom="8dp"
- android:left="16dp"
- android:right="16dp"/>
- </shape>
复制代码 原理分析
- ShapeDrawable 类:ShapeDrawable 是 Android 中用于绘制简朴形状的 Drawable 子类。当在 XML 中定义 shape 标签时,体系会根据标签内的属性创建一个 ShapeDrawable 对象。
- Corners 标签:corners 标签用于设置圆角属性。在 ShapeDrawable 内部,这些属性会被分析并应用到形状的绘制过程中。当绘制矩形时,会根据设置的圆角半径对矩形的四个角进行圆角处理。
- Solid 标签:solid 标签用于设置形状的填充颜色。在 ShapeDrawable 绘制时,会使用该颜色填充整个形状区域。
- Stroke 标签:stroke 标签用于设置形状的边框。它包含边框的宽度和颜色属性,在绘制时会在形状的边沿绘制指定宽度和颜色的边框。
- Padding 标签:padding 标签用于设置形状内部的填充区域与边界的隔断。在 ShapeDrawable 应用到 View 时,会影响 View 内部内容的布局。
2.2 使用 GradientDrawable
- <gradient
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:type="linear"
- <!-- 设置渐变的起始颜色为粉色 -->
- android:startColor="#FF4081"
- <!-- 设置渐变的结束颜色为蓝色 -->
- android:endColor="#2196F3"
- <!-- 设置渐变的角度为 45 度 -->
- android:angle="45">
- <!-- 设置圆角半径为 24dp -->
- <corners android:radius="24dp"/>
- </gradient>
复制代码 原理分析
- GradientDrawable 类:GradientDrawable 是 Drawable 的子类,用于绘制渐变效果的形状。当在 XML 中定义 gradient 标签时,体系会创建一个 GradientDrawable 对象。
- Type 属性:android:type 属性指定渐变的范例,这里设置为 linear 表示线性渐变。GradientDrawable 会根据该属性选择不同的渐变算法进行绘制。
- StartColor 和 EndColor 属性:android:startColor 和 android:endColor 分别指定渐变的起始颜色和结束颜色。在绘制时,GradientDrawable 会根据渐变范例和角度在形状内部绘制颜色渐变效果。
- Angle 属性:android:angle 属性用于设置线性渐变的角度。它决定了渐变的方向,比方 45 度表示从左上角到右下角的渐变方向。
- Corners 标签:与 ShapeDrawable 中的 corners 标签类似,用于设置圆角半径。在 GradientDrawable 绘制时,会对形状的四个角进行圆角处理。
2.3 使用 LayerDrawable
- <layer-list
- xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- 第一个图层 -->
- <item>
- <shape android:shape="rectangle">
- <!-- 设置圆角半径为 20dp -->
- <corners android:radius="20dp"/>
- <!-- 设置填充颜色为白色 -->
- <solid android:color="#FFFFFF"/>
- </shape>
- </item>
- <!-- 第二个图层,相对于第一个图层向下和向右偏移 2dp -->
- <item android:bottom="2dp" android:right="2dp">
- <shape android:shape="rectangle">
- <!-- 设置圆角半径为 20dp -->
- <corners android:radius="20dp"/>
- <!-- 设置填充颜色为浅灰色 -->
- <solid android:color="#EEEEEE"/>
- </shape>
- </item>
- </layer-list>
复制代码 原理分析
- LayerDrawable 类:LayerDrawable 是 Drawable 的子类,用于将多个 Drawable 组合成一个图层列表。当在 XML 中定义 layer-list 标签时,体系会创建一个 LayerDrawable 对象,并将每个 item 标签内的 Drawable 作为一个图层添加到列表中。
- Item 标签:item 标签用于定义每个图层的 Drawable。可以通过 android:bottom、android:right 等属性设置图层的偏移量。在绘制时,LayerDrawable 会按照图层的顺序依次绘制每个 Drawable,反面的图层会覆盖前面的图层。
- ShapeDrawable 嵌套:在每个 item 标签内,可以使用 shape 标签定义 ShapeDrawable。这样可以为每个图层设置不同的形状、颜色和圆角属性。
三、代码动态设置圆角
3.1 使用 View.setBackground ()
- // 创建一个 GradientDrawable 对象
- val drawable = GradientDrawable()
- // 设置圆角半径为 24f
- drawable.cornerRadius = 24f
- // 设置填充颜色为资源文件中定义的 colorPrimary 颜色
- drawable.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
- // 将 GradientDrawable 对象设置为 View 的背景
- view.setBackground(drawable)
复制代码 原理分析
- GradientDrawable 类:GradientDrawable 类提供了设置圆角和颜色的方法。cornerRadius 属性用于设置圆角半径,setColor 方法用于设置填充颜色。
- View.setBackground () 方法:View 类的 setBackground 方法用于设置 View 的配景 Drawable。当调用该方法时,View 会将传入的 Drawable 对象保存起来,并在绘制时使用该 Drawable 绘制配景。
3.2 使用 ViewOutlineProvider
- // 为 View 设置自定义的 OutlineProvider
- view.outlineProvider = object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- // 设置 Outline 为一个圆角矩形,圆角半径为 24f
- outline.setRoundRect(0, 0, view.width, view.height, 24f)
- }
- }
- // 开启 View 的裁剪功能,使其按照 Outline 进行裁剪
- view.clipToOutline = true
复制代码 原理分析
- ViewOutlineProvider 类:ViewOutlineProvider 是一个抽象类,用于提供 View 的轮廓信息。通过重写 getOutline 方法,可以自定义 View 的轮廓形状。在这个例子中,我们将 Outline 设置为一个圆角矩形。
- Outline 类:Outline 类用于表示 View 的轮廓。setRoundRect 方法用于设置圆角矩形的轮廓,它接受矩形的左上角和右下角坐标以及圆角半径作为参数。
- clipToOutline 属性:View 的 clipToOutline 属性用于控制是否按照 Outline 进行裁剪。当设置为 true 时,View 会在绘制时将超出 Outline 的部门裁剪掉,从而实现圆角效果。
3.3 使用 Path 裁剪
- override fun onDraw(canvas: Canvas) {
- // 创建一个 Path 对象
- val path = Path()
- // 向 Path 中添加一个圆角矩形,圆角半径为 24f
- path.addRoundRect(0f, 0f, width.toFloat(), height.toFloat(), 24f, 24f, Path.Direction.CW)
- // 使用 Path 对 Canvas 进行裁剪
- canvas.clipPath(path)
- // 调用父类的 onDraw 方法进行绘制
- super.onDraw(canvas)
- }
复制代码 原理分析
- Path 类:Path 类用于定义一个路径,可以包含直线、曲线、矩形、圆形等各种形状。addRoundRect 方法用于向 Path 中添加一个圆角矩形,它接受矩形的左上角和右下角坐标以及圆角半径作为参数。
- Canvas.clipPath () 方法:Canvas 的 clipPath 方法用于使用指定的 Path 对 Canvas 进行裁剪。在绘制时,只有 Path 内部的区域会被绘制,外部的区域会被裁剪掉。
- onDraw 方法:View 的 onDraw 方法是绘制 View 内容的核心方法。在这个方法中,我们先创建一个圆角矩形的 Path,然后使用该 Path 对 Canvas 进行裁剪,最后调用父类的 onDraw 方法进行绘制,从而实现圆角效果。
四、第三方库实现圆角
4.1 AndroidX CardView
- <com.google.android.material.card.MaterialCardView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- <!-- 设置卡片的圆角半径为 20dp -->
- app:cardCornerRadius="20dp"
- <!-- 设置卡片的阴影高度为 8dp -->
- app:cardElevation="8dp">
- <!-- 子视图 -->
- </com.google.android.material.card.MaterialCardView>
复制代码 原理分析
- MaterialCardView 类:MaterialCardView 是 AndroidX Material Design 库中的一个自定义 View,它继续自 FrameLayout。它封装了圆角和阴影的实现,提供了简朴的属性来设置圆角半径和阴影高度。
- cardCornerRadius 属性:app:cardCornerRadius 属性用于设置卡片的圆角半径。在 MaterialCardView 内部,会根据该属性创建一个圆角矩形的 Outline,并使用 clipToOutline 进行裁剪,从而实现圆角效果。
- cardElevation 属性:app:cardElevation 属性用于设置卡片的阴影高度。MaterialCardView 会根据该属性使用 View 的阴影机制绘制阴影效果。
4.2 RoundedImageView
- <com.makeramen.roundedimageview.RoundedImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- <!-- 设置圆角半径为 40dp -->
- app:riv_corner_radius="40dp"
- <!-- 设置边框宽度为 2dp -->
- app:riv_border_width="2dp"
- <!-- 设置边框颜色为黄色 -->
- app:riv_border_color="#FFEB3B"
- <!-- 设置是否对背景进行变形处理 -->
- app:riv_mutate_background="true"
- <!-- 设置图片资源 -->
- android:src="@drawable/avatar"/>
复制代码 原理分析
- RoundedImageView 类:RoundedImageView 是一个开源的第三方库,用于实现图片的圆角效果。它继续自 ImageView,通过重写 onDraw 方法来实现圆角裁剪。
- riv_corner_radius 属性:app:riv_corner_radius 属性用于设置圆角半径。在 RoundedImageView 内部,会根据该属性创建一个圆角矩形的 Path,并使用 Canvas.clipPath 方法对图片进行裁剪。
- riv_border_width 和 riv_border_color 属性:app:riv_border_width 和 app:riv_border_color 属性分别用于设置边框的宽度和颜色。在绘制时,会在圆角矩形的边沿绘制指定宽度和颜色的边框。
- riv_mutate_background 属性:app:riv_mutate_background 属性用于设置是否对配景进行变形处理。当设置为 true 时,会对配景进行圆角处理。
4.3 Material Design Components
- <com.google.android.material.button.MaterialButton
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Button"
- <!-- 设置按钮的圆角半径为 16dp -->
- app:cornerRadius="16dp"
- <!-- 设置按钮的边框宽度为 2dp -->
- app:strokeWidth="2dp"
- <!-- 设置按钮的边框颜色为资源文件中定义的 colorAccent 颜色 -->
- app:strokeColor="@color/colorAccent"/>
复制代码 原理分析
- MaterialButton 类:MaterialButton 是 AndroidX Material Design 库中的一个自定义 Button,它继续自 AppCompatButton。它提供了简朴的属性来设置圆角半径和边框效果。
- cornerRadius 属性:app:cornerRadius 属性用于设置按钮的圆角半径。在 MaterialButton 内部,会根据该属性创建一个圆角矩形的 Outline,并使用 clipToOutline 进行裁剪,从而实现圆角效果。
- strokeWidth 和 strokeColor 属性:app:strokeWidth 和 app:strokeColor 属性分别用于设置按钮的边框宽度和颜色。在绘制时,会在按钮的边沿绘制指定宽度和颜色的边框。
五、高级技巧与优化
5.1 动态圆角动画
- // 从资源文件中加载一个过渡动画
- val transition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds)
- // 获取场景根视图
- val sceneRoot = findViewById<ViewGroup>(R.id.scene_root)
- // 开始延迟过渡动画
- TransitionManager.beginDelayedTransition(sceneRoot, transition)
- // 动画触发
- view.animate().withEndAction {
- // 创建一个 GradientDrawable 对象
- val drawable = GradientDrawable()
- // 根据当前圆角半径的值,切换圆角半径
- drawable.cornerRadius = if (currentRadius == 0f) 24f else 0f
- // 更新当前圆角半径的值
- currentRadius = drawable.cornerRadius
- // 将 GradientDrawable 对象设置为 View 的背景
- view.setBackground(drawable)
- }.start()
复制代码 原理分析
- TransitionInflater 类:TransitionInflater 用于从资源文件中加载过渡动画。inflateTransition 方法接受一个过渡动画的资源 ID 作为参数,返回一个 Transition 对象。
- TransitionManager 类:TransitionManager 用于管理过渡动画。beginDelayedTransition 方法用于开始一个延迟过渡动画,它接受一个场景根视图和一个 Transition 对象作为参数。在这个方法调用之后,对场景根视图及其子视图的布局变革会触发过渡动画。
- View.animate () 方法:View 的 animate 方法用于创建一个 ViewPropertyAnimator 对象,用于对 View 的属性进办法画操作。withEndAction 方法用于在动画结束时执行一个操作,这里我们在动画结束时切换 View 的圆角半径。
5.2 硬件加快优化
- <application
- android:hardwareAccelerated="true"
- ...>
- </application>
复制代码 原理分析
- 硬件加快机制:Android 体系的硬件加快机制允许使用 GPU 来处理图形渲染,从而提高渲染性能。当 android:hardwareAccelerated 属性设置为 true 时,整个应用的 View 渲染都会使用硬件加快。
- 对圆角绘制的影响:在硬件加快的情况下,View 的圆角绘制会由 GPU 进行处理,渲染速率会更快。比方,使用 ViewOutlineProvider 和 Canvas.clipPath 等方法在硬件加快下的性能会更好。
5.3 内存优化
- // 复用 Drawable,创建一个共享的 GradientDrawable 对象
- val sharedDrawable = GradientDrawable()
- // 将共享的 GradientDrawable 对象设置为 view1 的背景
- view1.setBackground(sharedDrawable)
- // 将共享的 GradientDrawable 对象设置为 view2 的背景
- view2.setBackground(sharedDrawable)
- // 及时回收资源,在 View 从窗口中分离时调用
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- // 对背景 Drawable 进行变异处理,避免共享状态的影响
- (view.background as? GradientDrawable)?.mutate()
- // 将 View 的背景设置为 null,释放资源
- view.setBackground(null)
- }
复制代码 原理分析
- Drawable 复用:通过创建一个共享的 Drawable 对象,并将其设置为多个 View 的配景,可以减少内存开销。由于多个 View 共享同一个 Drawable 对象,避免了重复创建相同的 Drawable 实例。
- mutate () 方法:Drawable 的 mutate 方法用于创建一个 Drawable 的独立副本,使其状态与原始 Drawable 分离。在 View 从窗口中分离时调用 mutate 方法,可以避免共享状态的影响,确保资源的正确开释。
- setBackground (null) 方法:在 View 从窗口中分离时,将 View 的配景设置为 null,可以开释 View 持有的 Drawable 对象,避免内存泄漏。
六、源码深度分析
6.1 View 圆角实现原理
- // View.java
- public void setOutlineProvider(OutlineProvider outlineProvider) {
- // 如果当前已经设置了 OutlineProvider,则调用其 onStop 方法
- if (mOutlineProvider != null) {
- mOutlineProvider.onStop(this);
- }
- // 更新 OutlineProvider
- mOutlineProvider = outlineProvider;
- // 如果新设置的 OutlineProvider 不为 null,则调用其 onStart 方法
- if (mOutlineProvider != null) {
- mOutlineProvider.onStart(this);
- }
- // 使 Outline 无效,触发重新计算
- invalidateOutline();
- // 使 View 无效,触发重绘
- invalidate();
- }
- // OutlineProvider.java
- public abstract class OutlineProvider {
- public void getOutline(View view, Outline outline) {
- // 默认设置 Outline 为一个椭圆形
- outline.setOval(0, 0, view.getWidth(), view.getHeight());
- }
- }
复制代码 原理分析
- View.setOutlineProvider () 方法:该方法用于设置 View 的 OutlineProvider。在设置新的 OutlineProvider 之前,会先调用当前 OutlineProvider 的 onStop 方法,然后更新 OutlineProvider,并调用新的 OutlineProvider 的 onStart 方法。最后,调用 invalidateOutline 方法使 Outline 无效,触发重新计算,调用 invalidate 方法使 View 无效,触发重绘。
- OutlineProvider.getOutline () 方法:这是一个抽象方法,用于提供 View 的 Outline 信息。默认实现将 Outline 设置为一个椭圆形,但在现实使用中,我们通常会重写该方法,根据需要设置不同的 Outline 形状,如圆角矩形。
6.2 硬件加快渲染流程
- // RenderNode.java
- public void setRoundRect(int left, int top, int right, int bottom, float radius) {
- // 调用底层的绘制节点设置圆角矩形
- mDrawNode.setRoundRect(left, top, right, bottom, radius);
- // 标记属性无效,需要重新计算
- mInvalidateProperties = true;
- }
- // RenderProxy.java
- public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry) {
- // 调用底层的本地方法绘制圆角矩形
- nDrawRoundRect(mNativeRenderProxy, left, top, right, bottom, rx, ry);
- }
复制代码 原理分析
- RenderNode.setRoundRect () 方法:RenderNode 是 Android 硬件加快渲染的核心类之一,用于表示一个可渲染的节点。setRoundRect 方法用于设置 RenderNode 的绘制形状为圆角矩形。它会调用底层的绘制节点的 setRoundRect 方法,并标记属性无效,以便在下次渲染时重新计算。
- RenderProxy.drawRoundRect () 方法:RenderProxy 是 RenderNode 的署理类,用于与底层的渲染引擎进行交互。drawRoundRect 方法用于绘制圆角矩形,它会调用底层的本地方法 nDrawRoundRect 来完成现实的绘制操作。
七、常见问题与办理方案
7.1 圆角不表现
- 大概原因:
- XML 中未正确设置 android:background:如果在 XML 中没有为 View 设置正确的配景 Drawable,则圆角效果不会表现。
- 视图尺寸为 0:如果 View 的宽度或高度为 0,则无法表现圆角效果。这大概是由于布局参数设置不正确或在 View 测量之前进行了绘制操作。
- 硬件加快未开启:某些圆角绘制方法(如 ViewOutlineProvider)需要开启硬件加快才气正常工作。如果硬件加快未开启,大概会导致圆角不表现。
- 办理方案:
- view.post {
- // 在 View 布局完成后,设置背景资源
- view.setBackgroundResource(R.drawable.rounded_bg)
- }
复制代码 原理分析
- view.post () 方法:View 的 post 方法用于在 View 的消息队列中添加一个任务,该任务会在 View 布局完成后执行。通过使用 view.post 方法,可以确保在 View 有正确的尺寸之后再设置配景资源,从而避免因视图尺寸为 0 导致的圆角不表现问题。
7.2 边沿锯齿
- // 开启 Drawable 的抗锯齿功能
- drawable.isAntiAlias = true
- // 使用抗锯齿的画笔绘制圆角矩形
- canvas.drawRoundRect(rect, radius, radius, paint)
复制代码 原理分析
- isAntiAlias 属性:Drawable 的 isAntiAlias 属性用于开启或关闭抗锯齿功能。抗锯齿功能可以使图形的边沿更加平滑,减少锯齿征象。当设置为 true 时,Drawable 在绘制时会使用抗锯齿算法来处理边沿。
- Paint 抗锯齿:在使用 Canvas 绘制圆角矩形时,通过设置 Paint 的抗锯齿属性,可以使绘制的圆角矩形边沿更加平滑。
7.3 性能问题
- 优化建议:
- 使用 OutlineProvider 替换自定义绘制:OutlineProvider 是 Android 体系提供的一种高效的圆角绘制方法,它使用了硬件加快机制,性能比自定义绘制更好。
- 避免在 onDraw 中创建对象:onDraw 方法会频仍调用,如果在其中创建对象,会导致频仍的垃圾回收,影响性能。可以将对象的创建移到 onCreate 或其他初始化方法中。
- 合理设置缓存策略:可以使用 View 的 setDrawingCacheEnabled 方法开启绘图缓存,避免重复绘制,提高性能。
八、不同场景下的实现策略
场景推荐方案优势简朴圆角矩形ShapeDrawable / GradientDrawable代码简洁,兼容性好,实用于大多数简朴的圆角矩形需求。可以通过 XML 或代码动态设置,易于维护。复杂形状Path 裁剪灵活性高,支持任意形状的圆角裁剪。可以通过 Path 类定义复杂的路径,实现独特的形状效果。需要阴影效果CardView内置阴影支持,性能优化。CardView 是 AndroidX Material Design 库中的组件,提供了简朴的属性来设置圆角和阴影,同时对性能进行了优化。图片圆角RoundedImageView专业处理图片,支持多种效果。RoundedImageView 是一个开源的第三方库,专门用于处理图片的圆角效果,支持圆形、圆角、椭圆等多种形状,还可以设置边框和配景变形效果。动态圆角动画ViewOutlineProvider + 动画流通过渡,硬件加快优化。通过 ViewOutlineProvider 实现圆角效果,联合 ViewPropertyAnimator 或 TransitionManager 实现动态圆角动画,使用硬件加快机制,过渡效果流通。 九、性能对比测试
方法内存占用(KB)渲染时间(ms)兼容性ShapeDrawable1.20.8API 1+CardView3.51.5API 21+OutlineProvider2.11.2API 21+自定义 Path 绘制4.82.5API 1+RoundedImageView5.21.8API 1+ 性能分析
- 内存占用:ShapeDrawable 的内存占用最小,由于它只是一个简朴的形状绘制类。CardView、OutlineProvider、RoundedImageView 和自定义 Path 绘制的内存占用相对较大,由于它们涉及到更多的功能和复杂的绘制逻辑。
- 渲染时间:ShapeDrawable 的渲染时间最短,由于它的绘制逻辑最简朴。CardView 和 OutlineProvider 的渲染时间相对较短,由于它们使用了硬件加快机制。自定义 Path 绘制的渲染时间最长,由于它需要手动处理裁剪和绘制逻辑。
- 兼容性:ShapeDrawable 和自定义 Path 绘制的兼容性最好,支持全部 Android API 版本。CardView 和 `Outline
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |