前言
前两天写了一篇文章是关于Android中绘制圆角图片,发布之后有两位大佬发表了评论(谢谢大佬!!!),给介绍了OutLine和ShapeableImageView,后面发现ShapeableImageView也是通过OutLine实现了部分东西,这个还没详细看,后面补充。
简介
本文的重点是Android的布局凹陷的实现。相信大家有看到过类似这样的界面
中间有一个按钮,然后下面的bottomBar刚好凹陷下去一部分,这样看起来很舒服!!! 我这里只是为了展示,微调参数过后效果会更好一些。 这是通过material.shape包下的MaterialShapeDrawable实现的。
解析
开始之前要先了解MaterialShapeDrawable内部其实是通过ShapeAppearanceModel来实现对边和角的形状描述。
ShapeAppearanceModel
/**
* This class models the edges and corners of a shape, which are used by {@link
* MaterialShapeDrawable} to generate and render the shape for a view's background.
* 此类为形状边缘和角的模型,MaterialShapeDrawable使用这个模型来生成并渲染指定视图的背景形状
*/
另外还有一个ShapePathModel也是一样的功能,目前已被废弃。
此类中拥有四个角CornerTreatment、四条边EdgeTreatment,这样就能够完整的描述整个view的背景形状。
需要使用其内置的Builder来构建这个对象。
CornerTreatment、EdgeTreatment
这两个类的实现及其相似。这里就看以下EdgeTreatment的内部方法,毕竟我们要通过它来描述一段凹陷的路径。
EdgeTreatment
我们只需要关注一个方法(这个类除了三个方法外别的什么也没有),最终通过这个方法拿到这条边的path,我们想要什么形状都能画出来哦。
public void getEdgePath(
float length, float center, float interpolation, @NonNull ShapePath shapePath) {
shapePath.lineTo(length, 0);
}
- length 边的长度
- center 当前位置到边的中心的距离(我有打印log发现在位置发生移动之后center的值会发生变化,但是有点怪,我没看明白,由于我把初始化写在了函数开头,验证了center == length/2)
- interpolation 这个参数我并没有用上,下面给出注释
he interpolation of the edge treatment. Ranges between 0 (none) and 1 (fully) interpolated. Custom edge treatments can implement interpolation to support shape transition between two arbitrary states. Typically, a value of 0 indicates that the custom edge treatment is not rendered (i.e. that it is a straight line), and a value of 1 indicates that the treatment is fully rendered. Animation between these two values can "heal" or "reveal" an edge treatment. 插值边缘处理的插值。插值介于0(无)和1 (完全)之间的范围。自定义边缘处理可实现插值以支持两个任意状态之间的形状*过渡。通常,值为0表示未渲染自定义边缘处理(即,它是一条直线),值为1 表示已完全渲染处理。这两个值之间的动画可以“修复”或“显示”边缘处理。
- shapePath 我们对这条边的描述就直接写入到这个path就可以了
MaterialShapeDrawable
MaterialShapeDrawable 继承于Drawable
所以它可以直接被设置到任何view上。
它有一个构造方法就是直接接收一个ShapeAppearanceModel对象
/**
* @param shapeAppearanceModel the {@link ShapeAppearanceModel} containing the path that will be
* rendered in this drawable.
*/
public MaterialShapeDrawable(@NonNull ShapeAppearanceModel shapeAppearanceModel) {}
当然也可以通过后面直接设置一个model对象
@Override
public void setShapeAppearanceModel(@NonNull ShapeAppearanceModel shapeAppearanceModel) {}
实践
思路
- 创建一个
ShapeAppearanceModel对象,给它设置topEdge的path, - 用这个对象生成一个
MaterialShapeDrawable对象 - 把drawable设置给view的background
代码
val bar = findViewById<LinearLayout>(R.id.bar)
val shapePathModel = ShapeAppearanceModel
.Builder()
.setTopEdge(object: EdgeTreatment(){
override fun getEdgePath(
length: Float,
center: Float,
interpolation: Float,
shapePath: ShapePath
) {
//凹陷半径
val r = length/10 // == center
val halfLine = length/2-r
//值越大转角处就越顺滑,但是必须小于等于r,这个大小可以多试试
val offset = 30f
//第一部分,直线,画到凹陷开始的地方
shapePath.lineTo(halfLine-offset,0f)
//凹陷开始的地方的转角,不处理的话看起来很尖锐
shapePath.quadToPoint(halfLine,0f,halfLine+offset,offset)
//凹陷部分
shapePath.quadToPoint(center,r,halfLine+2*r-offset,offset)
//凹陷结束的地方的转角
shapePath.quadToPoint(halfLine+2*r,0f,halfLine+2*r+offset,0f)
//最后一部分的直线,画到底
shapePath.lineTo(length,0f)
}
}).build()
val drawable = MaterialShapeDrawable(shapePathModel)
bar.background = drawable
//至此结束
结语
学无止境!!!
感谢各位的分享,又让我好好学了一波👍!
加油冲!!!
有错误的话请大家及时告知,我会马上修改的,也欢迎大家给建议,谢谢啦!