阅读 2066

Android 实现布局凹陷(MaterialShapeDrawable)

前言

前两天写了一篇文章是关于Android中绘制圆角图片,发布之后有两位大佬发表了评论(谢谢大佬!!!),给介绍了OutLineShapeableImageView,后面发现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);
  }
复制代码
  1. length 边的长度
  2. center 当前位置到边的中心的距离(我有打印log发现在位置发生移动之后center的值会发生变化,但是有点怪,我没看明白,由于我把初始化写在了函数开头,验证了center == length/2)
  3. 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 表示已完全渲染处理。这两个值之间的动画可以“修复”或“显示”边缘处理。

  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) {}
复制代码

实践

思路

  1. 创建一个ShapeAppearanceModel对象,给它设置topEdge的path,
  2. 用这个对象生成一个MaterialShapeDrawable对象
  3. 把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
//至此结束
复制代码

结语

学无止境!!!

感谢各位的分享,又让我好好学了一波👍!

加油冲!!!

有错误的话请大家及时告知,我会马上修改的,也欢迎大家给建议,谢谢啦!

文章分类
Android
文章标签