待看
- juejin.cn/post/684490…
- juejin.cn/post/686031…
- juejin.cn/post/684490…
- juejin.cn/post/684490…
- juejin.cn/post/684490…
- juejin.cn/post/684490…
- juejin.cn/post/685457…
- juejin.cn/post/722069…
- juejin.cn/post/722142…
- 官方 MotionLayoutDemo
1. MotionLayout简单使用
1. 示例
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Case1Activity"
app:layoutDescription="@xml/case1_scene"
app:showPaths="true"
>
<View
android:id="@+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
case1_scene.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@+id/view">
//Layout描述位置
<Layout
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_marginStart="96dp"
android:layout_marginTop="96dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Transform
android:rotationX="30"
android:rotationY="30" />
<CustomAttribute
app:attributeName="backgroundColor"
app:customColorValue="@color/color1" />
//PropertySet描述可见性,透明度
<PropertySet android:alpha="0.3"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@+id/view">
<Layout
android:layout_width="192dp"
android:layout_height="192dp"
android:layout_marginStart="0dp"
android:layout_marginTop="0dp"
android:layout_marginEnd="192dp"
android:layout_marginBottom="192dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Transform
android:rotationX="70"
android:rotationY="70" />
<CustomAttribute
app:attributeName="backgroundColor"
app:customColorValue="@color/color2" />
<PropertySet android:alpha="0.8"/>
</Constraint>
</ConstraintSet>
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start"
app:duration="4000"
app:motionInterpolator="@string/material_motion_easing_accelerated">
<OnClick
app:clickAction="toggle"
app:targetId="@+id/view" />
<KeyFrameSet>
<KeyPosition
app:curveFit="spline"
app:framePosition="30"
app:keyPositionType="pathRelative"
app:motionTarget="@+id/view"
app:percentHeight="0.6"
app:percentWidth="0.6"
app:percentX="0.50"
app:percentY="0.50"></KeyPosition>
<KeyAttribute
android:alpha="0.6"
android:scaleX="1.2"
android:scaleY="1.2"
app:framePosition="30"
app:motionTarget="@+id/view">
<CustomAttribute
app:attributeName="backgroundColor"
app:customColorValue="@color/color4" />
</KeyAttribute>
</KeyFrameSet>
</Transition>
</MotionScene>
2. MotionScene大致结构
<MotionScene ***>
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@+id/view">
<Layout *** />
<Transform *** />
<CustomAttribute
app:attributeName="backgroundColor"
app:customColorValue="@color/color1" />
<PropertySet android:alpha="0.3"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
***
</ConstraintSet>
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start">
<OnClick
app:clickAction="toggle"
app:targetId="@+id/view" />
<KeyFrameSet>
<KeyPosition
app:framePosition="30"
app:motionTarget="@+id/view"
*** />
<KeyAttribute
app:framePosition="30"
app:motionTarget="@+id/view"
*** />
<CustomAttribute
app:attributeName="backgroundColor"
app:customColorValue="@color/color4" />
</KeyAttribute>
</KeyFrameSet>
</Transition>
</MotionScene>
- 最外层是MotionScene
- 内部是 start ConstraintSet + end ConstraintSet + Transition
- ConstraintSet内部包含多个Constraint, 每个Constraint关联MotionLayout中的1个View
- Constraint内部包含 Layout, Transform, CustomAttribute, PropertySet
- Layout描述该View的位置
- Transform描述该View的旋转,位移,缩放,海拔等
- PropertySet描述该View的可见性,透明度
- CustomAttribute描述其他属性
- Transition内部包含点击逻辑,关键帧集合
- KeyFrameSet内部包含多个KeyPosition和KeyAttribute
- KeyPosition描述该关键帧时候对应View的位置
- KeyAttribute描述该关键帧时候对应View的非位置属性
3. MotionLayout配合AppbarLayout
- 自定义MotionLayout
open class CustomMotionLayout1 @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener {
override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {
val currProgress: Float = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!!
progress = currProgress
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
(parent as? AppBarLayout)?.addOnOffsetChangedListener(this)
}
}
- Activitiy布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Case2Activity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<motionlayout.p1.CustomMotionLayout1
android:layout_width="match_parent"
android:layout_height="300dp"
android:minHeight="100dp"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
app:layoutDescription="@xml/case2_scene"
>
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="300dp"
android:src="@mipmap/img1"
android:scaleType="center"
/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="11111111"
android:textColor="@color/color1"
android:textSize="32sp"
android:transformPivotX="0dp"
android:transformPivotY="0dp"
/>
</motionlayout.p1.CustomMotionLayout1>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/str_long1" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
- MotionScene配置
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Constraint
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rotation="-90"
app:layout_constraintBottom_toBottomOf="@id/img"
app:layout_constraintStart_toStartOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Constraint
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rotation="0"
app:layout_constraintBottom_toBottomOf="@id/img"
app:layout_constraintStart_toStartOf="parent" />
</ConstraintSet>
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start"
app:duration="2000"
app:motionInterpolator="@string/material_motion_easing_accelerated" />
</MotionScene>
- 起止图像
4. MotionLayout配合ViewPager
- 自定义MotionLayout
open class CustomMotionLayout2 @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
val currProgress: Float = (position + positionOffset) / 2
progress = currProgress
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val viewGroup = (parent as? ViewGroup)!!
for (i in 0 until viewGroup.childCount) {
val childView: View = viewGroup.getChildAt(i)
if (childView is ViewPager) {
childView.addOnPageChangeListener(this)
break
}
}
}
override fun onPageSelected(position: Int) {
}
override fun onPageScrollStateChanged(state: Int) {
}
}
- Activity布局文件 及 MotionScene文件
- Activity代码
private fun initViews() {
vp = findViewById(R.id.vp)
val pages = mutableListOf<View>()
val page1: TextView = TextView(this)
page1.textSize = 48F
page1.layoutParams = ViewPager.LayoutParams()
page1.text = "111"
pages.add(page1)
val page2: TextView = TextView(this)
page2.textSize = 48F
page2.layoutParams = ViewPager.LayoutParams()
page2.text = "222"
pages.add(page2)
val page3: TextView = TextView(this)
page3.textSize = 48F
page3.layoutParams = ViewPager.LayoutParams()
page3.text = "333"
pages.add(page3)
val adapter = MyPagerAdapter(pages)
vp.adapter = adapter
}
class MyPagerAdapter : PagerAdapter {
private var pages: List<View> = mutableListOf()
constructor(pages: List<View>) : super() {
this.pages = pages
}
override fun getCount(): Int {
return this.pages.size
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object`
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val page: View = this.pages.get(position)
container.addView(page)
return page
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View?)
}
}
- 运行截图
5. KeyTrigger
- KeyTrigger位于KeyFrameSet中,可以根据动画进度触发关联View的特定事件
- 相当于对特定View的动画进度进行监听,在指定进度时,调用该View特定方法
特定View的类
open class KeyTriggerImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
fun show() {
visibility = VISIBLE
}
fun hide() {
visibility = INVISIBLE
}
}
xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
***>
<MotionLayout ***>
<motionlayout.p1.KeyTriggerImageView
android:id="@+id/img"
***/>
</MotionLayout>
</LinearLayout>
MotionScene
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start"
app:duration="10000"
>
<KeyFrameSet>
<KeyTrigger
app:framePosition="0"
app:motionTarget="@+id/img"
app:onCross="show" />
<KeyTrigger
app:framePosition="25"
app:motionTarget="@+id/img"
app:onCross="hide" />
<KeyTrigger
app:framePosition="50"
app:motionTarget="@+id/img"
app:onCross="show" />
<KeyTrigger
app:framePosition="75"
app:motionTarget="@+id/img"
app:onCross="hide" />
<KeyTrigger
app:framePosition="100"
app:motionTarget="@+id/img"
app:onCross="show" />
</KeyFrameSet>
</Transition>
6. OnSwipe
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start">
<OnSwipe
app:dragDirection="dragDown"
app:touchRegionId="@+id/img" />
</Transition>
- touchRegionId 指的是哪一个控件响应触摸事件
- dragDirection 拖拽的方向