展開式(可收縮)菜單彈出效果簡單實現 - 主要是通過AnimatorSet聯動實現
屬性動畫簡單使用可參考:Android 屬性動畫(Animator)簡單使用
可以分爲3種ObjectAnimator動畫:
位移動畫(translationX/translationY)
透明度動畫(alpha)
旋轉動畫(rotation)
先上效果:
開始貼代碼
#1 xml佈局:(FloatingActionButton換成其他View都可以,這兒只是示例)
<?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=".MainActivity">
//...TODO anything
<FrameLayout android:id="@+id/main_layout_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_on_black_50"
android:clickable="true"
android:visibility="gone" />
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/main_fab_transfer"
style="@style/GreenFabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_transfer"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/main_fab_return"
style="@style/GreenFabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_return"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/main_fab_minus"
style="@style/GreenFabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_minus"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/main_fab_plus"
style="@style/GreenFabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:src="@drawable/ic_plus"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin" />
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/main_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:visibility="visible"
android:layout_margin="@dimen/fab_margin"
app:fabCustomSize="60dp"
app:tint="@color/colorWhite"
app:maxImageSize="30dp"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
cc: 因爲要從一個菜單按鈕彈出,所以其他子菜單按鈕需要放置在統一位置即可,最好將菜單按鈕放在最上層;
#2 動畫代碼:
位移動畫(translationX/translationY):)
ObjectAnimator.ofFloat(
menuView,
"translationX",
0F,
x)
ObjectAnimator.ofFloat(
menuView,
"translationY",
0F,
y)
//x,y 可以理解爲 移動終點座標點至起始點的距離
透明度動畫(alpha) :)
ObjectAnimator.ofFloat(menuView, "alpha", 0F, 1F)
旋轉動畫(rotation) :)
ObjectAnimator.ofFloat(mainView, "rotation", 0F, 90F) //旋轉90度
#3 Activity/Fragment 最終實現:)
class MainActivity : AppCompatActivity() {
private var menuViews = mutableListOf<FloatingActionButton>()
private var isMenuOpen = false
companion object {
private const val DISTANCE: Int = 70
private const val ANIMATION_ALPHA: Long = 600
private const val ANIMATION_TRANSLATION: Long = 600
private const val ANIMATION_ROTATION: Long = 800
}
//每個子菜單彈出後的點座標,即水平方向張開
private val childMenuPoints by lazy {
mutableListOf(
PointF(DISTANCE.toDp() * -1, 0F),
PointF(DISTANCE.toDp() * -2, 0F),
PointF(DISTANCE.toDp() * -3, 0F),
PointF(DISTANCE.toDp() * -4, 0F)
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
menuViews.add(main_fab_minus)
menuViews.add(main_fab_plus)
menuViews.add(main_fab_return)
menuViews.add(main_fab_transfer)
main_layout_bg.setOnClickListener {
showCloseAnim()
}
main_fab.setOnClickListener {
if (!isMenuOpen) {
showOpenAnim()
} else {
showCloseAnim()
}
}
}
private fun showOpenAnim() {
main_fab.isEnabled = false
main_layout_bg.show()
//for循環來開始小圖標的出現動畫
for (i in menuViews.indices) {
menuViews[i].show()
val set = AnimatorSet()
with(set) {
playTogether(
ObjectAnimator.ofFloat(menuViews[i], "translationX", 0F, childMenuPoints[i].x),
ObjectAnimator.ofFloat(menuViews[i], "translationY", 0F, childMenuPoints[i].y),
ObjectAnimator.ofFloat(menuViews[i], "alpha", 0F, 1F).setDuration(ANIMATION_ALPHA)
)
interpolator = BounceInterpolator()//添加回彈動畫
duration = ANIMATION_TRANSLATION
start()
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
//菜單狀態置開啓
isMenuOpen = true
main_fab.isEnabled = true
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
}
}
//轉動圖標本身90°
val rotate: ObjectAnimator = ObjectAnimator.ofFloat(main_fab, "rotation", 0F, 90F)
.setDuration(ANIMATION_ROTATION)
rotate.interpolator = BounceInterpolator()//添加回彈動畫
rotate.start()
}
private fun showCloseAnim() {
main_fab.isEnabled = false
main_layout_bg.hide()
//for循環來開始小圖標的出現動畫
for (i in menuViews.indices) {
val set = AnimatorSet()
with(set){
playTogether(
ObjectAnimator.ofFloat(menuViews[i], "translationX", childMenuPoints[i].x, 0F),
ObjectAnimator.ofFloat(menuViews[i], "translationY", childMenuPoints[i].y, 0F),
ObjectAnimator.ofFloat(menuViews[i], "alpha", 1f, 0f).setDuration(ANIMATION_ALPHA)
)
interpolator = BounceInterpolator() //添加回彈動畫
duration = ANIMATION_TRANSLATION
start()
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
menuViews[i].hide()
//菜單狀態置關閉
isMenuOpen = false
main_fab.isEnabled = true
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
}
}
//轉動標本身90°
val rotate: ObjectAnimator = ObjectAnimator.ofFloat(main_fab, "rotation", 0F, 90F)
.setDuration(ANIMATION_ROTATION)
rotate.interpolator = BounceInterpolator()
rotate.start()
}
}
cc: 以上代碼爲水平展開式(可收縮)菜單彈出效果,易於簡單說明;爆炸式展開的現實其實邏輯是一致的,只需更改子菜單點座標即可;
源碼附上
Git地址: ExpandableMenuDemo