Compose对MotionLayout的支持

87 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

Screen Shot 2022-11-27 at 8.31.48 AM.png 使用过view中的MotionLayout的同学都知道MotionLayout能轻松的让你做出一些复杂的动画,想使用compose的同学肯定也很疑惑我可不可以在compose里面也使用motionlayout呢,答案是可以的。

在Compose中需要依赖的library是:

implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha04"

下面先看一个简单的例子:

hah.gif

上面是一个简单的动画例子,点击按钮之后开始做Motion动画,其实就是简单的将图标从左移动到右边,下面看看demo的代码:

var enableMotion by remember {
                    mutableStateOf(false)
}
val buttonAnimationProgress by animateFloatAsState(
    // specifying target value on below line.
    targetValue = if (enableMotion) 1f else 0f,

    // on below line we are specifying
    // animation specific duration's 1 sec
    animationSpec = tween(1000)
)
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
    val motionScene = remember {
        resources.openRawResource(R.raw.motion_scene).readBytes().decodeToString()
    }
   Column(modifier = Modifier.fillMaxSize().padding(top = 50.dp)) {
       MotionLayout(motionScene = MotionScene(
           content = motionScene
       ), progress = buttonAnimationProgress) {
           Box(modifier = Modifier.layoutId("box").background(Color.Blue)){
               Icon(modifier = Modifier.layoutId("icon").background(Color.Red),imageVector = Icons.Default.Home, contentDescription = null)
           }
       }
       Button(onClick = { enableMotion = !enableMotion }) {
           Text(text = "do motion",color= Color.White)
       }
   }

}

我们先看MotionLayout的使用,这个里面有两个变量,一个是motionScene一个是progress,motionScene主要定义和动画相关的信息,progress定义了一个移动的动画属性。MotionLayout大致分为两种创建方式一种就是我们上面使用的,看看定义:

inline fun MotionLayout(
    motionScene: MotionScene,
    progress: Float,
    debug: EnumSet<MotionLayoutDebugFlags> = EnumSet.of(MotionLayoutDebugFlags.NONE),
    modifier: Modifier = Modifier,
    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
    crossinline content: @Composable (MotionLayoutScope.() -> Unit),
) 

MotionScene可以是一个json文件,下面看看例子中json文件的写法:

{
  ConstraintSets : {
    start : {
      box : {
        width : 90,
        height : 90,
        start : ['parent','start'],
        end : ['parent','end'],
        top : ['parent','top'],
        bottom : ['parent','bottom']
      },
      icon : {
        width : 90,
        height : 90,
        start : ['box','start',7],
        top : ['box','top',16],
        custom : {
          corner : 50
        }
      }
    },
    end : {
      box : {
        width : 90,
        height : 90,
        start : ['parent','start',300],
        end : ['parent','end'],
        top : ['parent','top'],
        bottom : ['parent','bottom']
      },
      icon : {
        width : 90,
        height : 90,
        top : ['box','top',16],
        left : ['box','left'],
        right : ['box','right'],
        custom : {
          corner : 25
        }
      },
    }
  },
  Transitions : {
    default : {
      from : 'start',
      end : 'end',
      pathMotionArc : 'startHorizontal',
      duration : 900,
      onSwipe : {
        anchor : 'box',
        maxVelocity : 4.2,
        maxAccel : 3,
        direction : 'start',
        side : 'end',
        mode : 'velocity'
      },
      KeyFrames : {
        KeyAttributes : [
          {
            target : ['icon'],
            frames : [0,100]
          }
        ]
      }
    }
  }
}

大家可以看到,其实就是定义了一个ConstraintSets,定义了它开始的状态以及结束的状态,当然还有一些transitions的属性,对于每一个控件还可以自定义属性。 另外一种创建的方式如下:

inline fun MotionLayout(
    start: ConstraintSet,
    end: ConstraintSet,
    transition: androidx.constraintlayout.compose.Transition? = null,
    progress: Float,
    debug: EnumSet<MotionLayoutDebugFlags> = EnumSet.of(MotionLayoutDebugFlags.NONE),
    modifier: Modifier = Modifier,
    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
    crossinline content: @Composable MotionLayoutScope.() -> Unit
)

感觉和上面的差不多,只是把start和end以及transition几个参数分开了。如果大家使用过view中的MotionLayout那么属于compose中的基本也就很快了,其实原理上基本差不多。