赞
踩
不知道多长时间没有登录发文章了,正好今天不太忙进来看一眼,然后看到好多人都已经开始自己公众号了,看看自己的工作年限也不短了,可是感觉好久没有干正事了,还是老老实实打开文档开始写吧。
随着UI越来越美观,原本直来直去的布局样式越来越不受喜欢,圆角样式,这个最简单的优化方案在越来越多的场合下被应用到。而具体怎么样才能展示出圆角的效果,相比大家都有N多中方案,我这里就列举一些自己常用的方案吧。当然,还有一些第三方的框架支持,这里就不列举了,主要是我个人感觉为了一个简单的效果,集成一个库不太划算,尤其是库中很大一部分功能都用不到的时候。而且第三方的框架贡献者实力不等,有很多都要导入源码进一步优化,分析太麻烦了,一向懒汉自称的我是不可能这么勤快去列举的,大家有兴趣可以自行研究。
样式参考就已自如房型列表的item为例吧:
首先说明一下,什么叫做方案0,不是因为程序员,计数一定要从0开始的洁癖,单纯是因为这些方案算是比较常用的,大家遇到类似的需求很快就能想到的。这里只是简单介绍一下,给一些新入门的同学看看,在遇到相应的圆角需求的时候,可以最方便的解决问题。
当然,还是同样的,如果一定要找一个第三方框架,一个参数控制去实现也不是不可以,性能各方面我这里也没有去具体评估过,也没办法说一定谁好谁坏。所以对于一切反对意见,我这里的回应都是:“我错了!你说的对!”
圆角的背景,可以实现的效果就是上面图中的下半部分
首先我们需要分析这个部分有什么要求:
可以看到有两个地方有要求,分别是:1.白色背景;2.灰色边框。所以对应的xml只需要满足这两点就可以了。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!--中间的白色实心背景-->
<solid android:color="#FFFFFF" />
<!--灰色边框,色值和宽度-->
<stroke
android:width="0.5dp"
android:color="#999999" />
<!--圆角角度:左下和右下-->
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp" />
</shape>
其中需要注意的是,图中可以看到,需要有圆角的区域只在下半部分,所以这里配置的时候也要只配置下半部,不然图片与背景的交接处就会有相当怪异。
这里可能有人会说,直接绘制一个白色的全圆角背景放到下面,上面盖上一个圆角的图片不就好了吗?你真是一个小机灵鬼,这个方案当然可行。但是性能稍微有点追求的建议了解一些过度绘制,虽然不是说背景上面叠加一张图片就会对性能造成断崖式影响,但是雪崩之下,可没有哪朵雪花是无辜的。
当然,这里为了看得清晰,页面的背景设置成了黑色,圆角也调整到了20dp,正常情况下也会出现问题,但是不会这么明显,能不能发现就看测试团队和UI团队的审查力度了。
现在用的比较多的,或者是我工作这么多年用的比较多的图片框架就是glide,想当初在刚开始用glide的时候还不支持圆角,需要额外添加一堆配置,还要导入第三方的库,现在好了,没有几行代码,基本属于一键配置了。
Glide.with(this).load(R.drawable.bg_scene)
.apply(
RequestOptions
.bitmapTransform(RoundedCorners(dip2px(this, 20f)))
).into(findViewById(R.id.test_img))
其中apply中的内容就是圆角的配置,当然,猜也能猜出来,这里只配置了一个角度,显示的时候底部肯定有问题,应该只配置顶部的两个圆角才对,这里给大家找了个链接:Glide 加载部分圆角图片,作为一个懒汉,没有做测试,哪位如果正好赶上有这个需求可以测试一下,我们直接进入下一个原生方案。
UI样式的大趋势,作为Android的幕后大boss google怎么可能不知道呢,所以官方控件里也有这么一个小朋友可以实现这个功能,那就是CardView,直接上代码:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/item_tv" android:layout_width="200dp" android:layout_height="wrap_content" android:background="#fefefe" android:padding="15dp" tools:ignore="SpUsage" tools:viewBindingIgnore="true"> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/test_img" android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="centerInside" android:src="@drawable/bg_scene" /> <View android:layout_width="match_parent" android:layout_height="80dp" android:background="20dp" /> </LinearLayout> </androidx.cardview.widget.CardView> </FrameLayout>
然后看效果:
可以看到,前面的两个问题,在这里都直接搞定了,背景图也不用设置圆角了,glide也不用设置圆角了,在CardView中添加了app:cardCornerRadius="5dp"后,一切问题都迎刃而解了,是不是特别爽,特别开心!
上面虽然说实现了功能,但是能放到方案0上,就代表还有其他在我个人的能力水平内能看到的更好的方案(具体是不是还是要交给大神们评估),那就先说说上面的这些我所看到的缺点吧。
那么我们就进入到为大家准备的其他方案。
这个方案就是通过declare-styleable配置圆角角度,然后在执行onDraw的时候,通过裁剪Carvas实现圆角效果,代码如下:
<declare-styleable name="XXXView">
<!-- 通用圆角配置 -->
<attr format="dimension" name="radius"/>
<!-- 右上圆角 -->
<attr format="dimension" name="rightTopRadius"/>
<!-- 左上圆角 -->
<attr format="dimension" name="leftTopRadius"/>
<!-- 右下圆角 -->
<attr format="dimension" name="rightBottomRadius"/>
<!-- 左下圆角 -->
<attr format="dimension" name="leftBottomRadius"/>
</declare-styleable>
自定义View代码
class XXXView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : AppCompatImageView(context, attrs, defStyleAttr) { private val mPath = Path() private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG) /** * 左上角 圆角大小 */ private var leftTopRadius: Float = 0f /** * 右上角 圆角大小 */ private var rightTopRadius: Float = 0f /** * 左下角 圆角大小 */ private var leftBottomRadius: Float = 0f /** * 右下角 圆角大小 */ private var rightBottomRadius: Float = 0f /** * 全部圆角 */ private var radius: Float = 0f private val clipRectF = RectF() init { val obtainStyledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.XXXView) radius= obtainStyledAttributes.getDimension(R.styleable.XXXView_radius, radius) if (radius != 0f) { //不知道为啥 不能 leftTopRadius=rightTopRadius=rightBottomRadius=leftBottomRadius = radius leftTopRadius = radius rightTopRadius = radius rightBottomRadius = radius leftBottomRadius = radius } else { leftTopRadius = obtainStyledAttributes.getDimension(R.styleable.XXXView_leftTopRadius, leftTopRadius) rightTopRadius = obtainStyledAttributes.getDimension(R.styleable.XXXView_rightTopRadius, rightTopRadius) leftBottomRadius = obtainStyledAttributes.getDimension(R.styleable.XXXView_leftBottomRadius, leftBottomRadius) rightBottomRadius = obtainStyledAttributes.getDimension(R.styleable.XXXView_rightBottomRadius, rightBottomRadius) } obtainStyledAttributes.recycle() } override fun onDraw(canvas: Canvas?) { //1. //dx,dy 成对出现,控制上右下左,四个位置圆角 val array = floatArrayOf(leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius, rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius); clipRectF.set(0f, 0f, width.toFloat(), height.toFloat()) mPath.addRoundRect(clipRectF, array, Path.Direction.CW) //2. //3. canvas?.clipPath(mPath) //4. super.onDraw(canvas) } }
自定义ViewGroup圆角(onDraw替换为dispatchDraw)
override fun dispatchDraw(canvas: Canvas) {
//1.
//dx,dy 成对出现,控制上右下左,四个位置圆角
val array = floatArrayOf(leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius, rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius);
mPath.addRoundRect(RectF(0f, 0f, width.toFloat(), height.toFloat()), array, Path.Direction.CW)
//2.
//3.
canvas.clipPath(mPath)
//4.
super.dispatchDraw(canvas)
}
注:
1. canvas裁剪需要放置在super之前
2. 频繁刷新UI,会导致频繁在onDraw执行canvas裁剪,出现黑色闪屏
其实我较多的时候使用的还是方案一,直到一次的需求需要频繁刷新UI,才发现了黑色闪屏的问题,查找其他方案的时候,才知道的ViewOutlineProvider,参考链接:Material Design :ViewOutlineProvider
demo代码如下:
<declare-styleable name="XXXView">
<!-- 圆角角度 -->
<attr format="dimension" name="radius"/>
</declare-styleable>
圆角View
class XXXView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : FrameLayout(context, attrs) { private var radius = 0f init { val obtainStyledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.XXXView) radius = obtainStyledAttributes.getDimension(R.styleable.XXXView_radius, radius) obtainStyledAttributes.recycle() clipToOutline = true outlineProvider = object : ViewOutlineProvider() { override fun getOutline(view: View?, outline: Outline?) { view?.let { outline?.setRoundRect( 0 + paddingLeft, 0 + paddingTop, it.width - paddingRight, it.height - paddingBottom, radius ) } } } } // 业务代码逻辑 // ... }
布局:
<com.example.demo.widget.radius.RadiusView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp" android:padding="15dp" app:radius="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!--图片自己网上随便找一张就可以--> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/test_img" android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="centerInside" android:src="@drawable/bg_scene" /> <View android:layout_width="match_parent" android:layout_height="80dp" android:background="@android:color/holo_purple" /> </LinearLayout> </com.example.demo.widget.radius.RadiusView>
效果如图:
注:
1. 代码中圆角的位置是会收到padding影响的,如果不想收到影响,去掉setRoundRect中的四个padding参数即可,具体依据具体需求调整
2. 由于ViewOutlineProvider api的限制,暂时能配置的都是四个角统一角度,如果想要分别配置,该方案不适用
可以看到,上面的所有方案在特定的情况下都会有所限制,所以在相应的场景下,产品经历还是要求我们实现对应的圆角效果,我们又不能让他闭嘴的时候,只能再想其他方案。
这里的建议就是切图覆盖,也就是让UI切一个颜色与背景色相同的圆角边框,覆盖到需要圆角展示的View上方,假装实现了一个圆角的效果。
圆角图片:
绘制布局:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/item_tv" android:layout_width="200dp" android:layout_height="200dp" tools:viewBindingIgnore="true"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/test_img" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:scaleType="centerCrop" android:src="@drawable/bg_scene" /> <View android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/mask" /> </FrameLayout>
圆角效果:
优点:基本可以兼容产品的所有圆角样式需求
缺点:需要多绘制一层View,不够优雅
上面列举的就是我现阶段想到的所有圆角效果的实现方案,在工作场景中,其实大多数情况也都够用了,当然,大家如果有其他的方案,也欢迎分享一下,大家共同进步。
这里也说过了,是没有列举第三方的方案的,都是一些原生的实现方案,虽然说用第三方的方案很爽,但是大多数情况下,还是建议自己学习一下具体的实现,较少一些不必要的引用。
另外,关于最后一条要说几句,其实这个方案是一次看网课有学生提问,老师提到的方案,结果是学生感觉这种方案是在忽悠他,最终不欢而散。其实说实话,这个方案,我个人也不是那么建议使用的,只是有很多情况我们并没有办法很快的实现产品或者UI想要的一些效果,不得不使用一些折中的方案,也算是工作中的一种妥协和无奈。
这里不是建议大家一点不用,但是最好应付过关后,在空闲时间思考一下,还有没有其他好的解决方案丰富自己。
好了,祝大家新年快乐,开年第一天的摸鱼……还有什么可以做的呢,容我想想,哈哈哈!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。