前些天在谷歌开发者公众号上看到一篇文章——取舍的艺术:Evernote。
其中有说道一个效果,在悬浮按钮上应用快捷指令的设置,点击展开后呈现六种不同的记录笔记的方式,结合菜单展开时的动画效果,让人眼前一亮。
大致思考了一下,可以将这样的菜单分为两部分,一部分是一直显示着的悬浮按钮,另一部分是带有动画效果可以展开消失的菜单部分。
LinearLayout的线性布局显然很适合,可以设置横向或纵向,再配上简单的动画效果,确实可以实现这种展开菜单的效果。
定义一个菜单容器,用于放置展开后需要呈现的菜单列表。最外面是菜单容器,里面是四个悬浮按钮作为菜单项。
<com.sjl.expandmenu.view.ExpandMenu
android:id="@+id/expandMenu4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
app:menuDrawable="@drawable/ic_open_close"
app:menuGravity="TOP">
<android.support.design.widget.FloatingActionButton
android:id="@+id/menu41"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/iv_previous_white" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/menu42"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/iv_play_white" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/menu43"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/iv_next_white" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/menu44"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/iv_stop_white" />
</com.sjl.expandmenu.view.ExpandMenu>
初始化菜单
主要分为几个步骤:
1、将菜单容器中的菜单加入list列表中,之后移除这些菜单(之后的显示和取消都是从list中动态添加删除);
2、初始化要一直显示的悬浮按钮和之后菜单显示的容器;
3、根据菜单要显示的位置,添加悬浮按钮和菜单容器(右边和下边就先添加悬浮按钮,反之先添加菜单容器);
4、添加按钮点击事件;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//布局加载后初始化菜单
initMenuList();
}
/**
* 初始化菜单列表
*/
private void initMenuList() {
menuList = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
//将菜单添加进菜单列表
View childView = getChildAt(i);
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onMenuItemClickListener != null) {
onMenuItemClickListener.onClick(v);
}
}
});
menuList.add(childView);
}
removeAllViews();
//初始化菜单容器
menuLayout = new LinearLayout(getContext());
menuLayout.setOrientation(getOrientation());
//初始化展开按钮
floatingActionButton = new FloatingActionButton(getContext());
if (menuDrawable != null) {
floatingActionButton.setImageDrawable(menuDrawable);
}
if (menuBackground != null) {
floatingActionButton.setBackgroundDrawable(menuBackground);
}
//根据位置添加菜单
if (menuGravity == TOP || menuGravity == LEFT) {
addView(menuLayout);
addView(floatingActionButton);
} else {
addView(floatingActionButton);
addView(menuLayout);
}
//设置菜单容器透明
ViewCompat.setAlpha(menuLayout, 0);
floatingActionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//切换菜单状态
toggle();
}
});
}
菜单状态切换动画
菜单展开需要先将列表中的菜单项添加到布局中,再一一显示菜单。
/**
* 添加菜单项
*/
private void addMenuItems() {
//设置菜单容器不透明
ViewCompat.setAlpha(menuLayout, 1f);
for (View view : menuList) {
menuLayout.addView(view);
}
}
菜单显示动画
/**
* 单个菜单进入动画
*
* @param view
* @param index 第几个显示的菜单,可根据该值设置启动延迟
*/
private void animateInMenuItem(View view, int index) {
//设置开始状态
ViewCompat.setScaleX(view, 0.25f);
ViewCompat.setScaleY(view, 0.25f);
//设置菜单透明是为了在延迟结束前都不可见菜单
ViewCompat.setAlpha(view, 0);
//进入动画
ViewCompat.animate(view)
.setDuration(duration * 4)
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setStartDelay(duration * index)//动画启动延迟
.setInterpolator(new FastOutSlowInInterpolator())
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
super.onAnimationStart(view);
//动画开始时,设置不透明
ViewCompat.setAlpha(view, 1f);
isAnimating = true;
}
@Override
public void onAnimationEnd(View view) {
super.onAnimationEnd(view);
isAnimating = false;
}
})
.start();
}
效果