动画必须有(二):悬浮菜单了解一下!
目录
- 前言
- 效果图
- FloatingActionButton基础
- FloatingActionButton实例
- 最后
前言
悬浮按钮是我非常喜欢的, 可以把最关键的功能放入到悬浮按钮中. 比如日记app里的新建日记, 阅读类app里的喜欢. 稍微处理一下可以将悬浮按钮扩展成悬浮菜单, 来看下实现吧! github直接看源码
效果图
废话不多说, 先看图, 感兴趣再往下看!
FloatingActionButton基础
记得导包.
compile 'com.android.support:design:26.+'
- 搭配Snackbar
官方推荐配合Snackbar来使用, 这都不多说了.
- 显示和隐藏
然后还有就是悬浮按钮的隐藏和显示函数.
Button btHide = (Button) findViewById(R.id.bt_hide); btHide.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fab.hide(); } }); Button btShow = (Button) findViewById(R.id.bt_show); btShow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fab.show(); } });
- 颜色
可以设置点击颜色app:rippleColor, 以及背景颜色app:backgroundTint. 我将背景色改成蓝色, 点击水波纹扩散变为紫色, 效果图如下:
- 位置
当然了, 位置可以随便改, 甚至可以吸附在某个控件之上.
android:layout_gravity="bottom|left"
吸附效果如下, 即使滚动也会保持相对的位置:
app:layout_anchor="@id/toolbar" app:layout_anchorGravity="center|bottom"
FloatingActionButton实例
来看看效果图是如何实现的吧.
- 布局文件
布局文件是个要点, 里面塞进了两个菜单, 你选一个喜欢的用就好. 一个是扇型的, 一个是线型的.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/fl_fan_menu" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginBottom="@dimen/twenty_dp" android:layout_marginEnd="@dimen/twenty_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorAccent" app:elevation="@dimen/zero_dp" app:fabSize="mini" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_left_top" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginBottom="@dimen/twenty_dp" android:layout_marginEnd="@dimen/twenty_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorAccent" app:elevation="@dimen/zero_dp" app:fabSize="mini" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_top" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginBottom="@dimen/twenty_dp" android:layout_marginEnd="@dimen/twenty_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorAccent" app:elevation="@dimen/zero_dp" app:fabSize="mini" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_origin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginBottom="@dimen/twenty_dp" android:layout_marginEnd="@dimen/twenty_dp" android:src="@drawable/ic_add" app:backgroundTint="@color/colorPrimary" app:fabSize="normal" /> </FrameLayout> <RelativeLayout android:id="@+id/rl_line_menu" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/rl_menu_content" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"> <LinearLayout android:id="@+id/ll_fun1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="@dimen/hundred_dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_toLeftOf="@+id/fab_mini1" android:layout_weight="1" android:gravity="right" android:paddingRight="@dimen/eight_dp" android:text="1" android:textColor="@android:color/white" android:textSize="@dimen/sixteen_sp" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_mini1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/twenty_six_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorPrimary" app:fabSize="mini" /> </LinearLayout> <LinearLayout android:id="@+id/ll_fun2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/ll_fun1" android:layout_marginBottom="@dimen/twenty_dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_toLeftOf="@+id/fab_mini2" android:layout_weight="1" android:gravity="right" android:paddingRight="@dimen/eight_dp" android:text="2" android:textColor="@android:color/white" android:textSize="@dimen/sixteen_sp" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_mini2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/twenty_six_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorPrimary" app:fabSize="mini" /> </LinearLayout> <LinearLayout android:id="@+id/ll_fun3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/ll_fun2" android:layout_marginBottom="@dimen/twenty_dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_toLeftOf="@+id/fab_mini2" android:layout_weight="1" android:gravity="right" android:paddingRight="@dimen/eight_dp" android:text="3" android:textColor="@android:color/white" android:textSize="@dimen/sixteen_sp" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_mini3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/twenty_six_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorPrimary" app:fabSize="mini" /> </LinearLayout> <LinearLayout android:id="@+id/ll_fun4" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/ll_fun3" android:layout_marginBottom="@dimen/twenty_dp" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_toLeftOf="@+id/fab_mini2" android:layout_weight="1" android:gravity="right" android:paddingRight="@dimen/eight_dp" android:text="4" android:textColor="@android:color/white" android:textSize="@dimen/sixteen_sp" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_mini4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/twenty_six_dp" android:src="@mipmap/ic_launcher" app:backgroundTint="@color/colorPrimary" app:fabSize="mini" /> </LinearLayout> </RelativeLayout> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="@dimen/twenty_dp" android:layout_marginEnd="@dimen/twenty_dp" android:backgroundTint="@color/colorAccent" android:src="@drawable/ic_add" app:fabSize="normal" app:rippleColor="@color/colorPrimaryDark" /> </RelativeLayout> <Button android:id="@+id/bt_switch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="@string/switch_menu" /> </RelativeLayout>
- normal与mini
悬浮按钮有两种尺寸, normal和mini. 在xml中加入app:fabSize="mini"
就变成mini尺寸的了. 所以在设置动画和位置的时候不是将按钮全部放置在同一位置, 需要修正位置. 修正距离就是(normal-mini)/2, 应该很好理解.
// 计算偏移值 int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); mFabOrigin.measure(w, h); mFabLeft.measure(w, h); mOffset = (mFabOrigin.getMeasuredHeight() - mFabLeft.getMeasuredHeight()) / 2; // 修正位置 FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) mFabLeft.getLayoutParams(); lParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset); mFabLeft.setLayoutParams(lParams); FrameLayout.LayoutParams ltParams = (FrameLayout.LayoutParams) mFabLeftTop.getLayoutParams(); ltParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset); mFabLeftTop.setLayoutParams(ltParams); FrameLayout.LayoutParams tParams = (FrameLayout.LayoutParams) mFabTop.getLayoutParams(); tParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset); mFabTop.setLayoutParams(tParams);
- 关于动画
只要位置算对了, 动画不是特别难, 当然想要像google或者apple的动画那样舒适还是很难的. 没看第一篇的可以回头看看.
/** * 显示扇型菜单 */ private void showFanMenu() { // 标识符设置是 mFanMenuOpen = true; // 按钮1向左移动 int x = (int) mFabOrigin.getX(); int y = (int) mFabOrigin.getY(); ValueAnimator va1 = ValueAnimator.ofInt(x, x - ConstantUtil.FAN_OFFSET); va1.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int l = (int) animation.getAnimatedValue(); int t = (int) mFabLeft.getY(); int r = mFabLeft.getWidth() + l; int b = mFabLeft.getHeight() + t; mFabLeft.layout(l, t, r, b); } }); // 按钮2向左上移动 ValueAnimator va2x = ValueAnimator.ofInt(x, x - (int) (ConstantUtil.FAN_OFFSET / Math.pow(2, 1.0 / 2))); ValueAnimator va2y = ValueAnimator.ofInt(y, y - (int) (ConstantUtil.FAN_OFFSET / Math.pow(2, 1.0 / 2))); va2x.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int l = (int) animation.getAnimatedValue(); int t = (int) mFabLeftTop.getY(); int r = mFabLeftTop.getWidth() + l; int b = mFabLeftTop.getHeight() + t; mFabLeftTop.layout(l, t, r, b); } }); va2y.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int l = (int) mFabLeftTop.getX(); int t = (int) animation.getAnimatedValue(); int r = mFabLeftTop.getWidth() + l; int b = mFabLeftTop.getHeight() + t; mFabLeftTop.layout(l, t, r, b); } }); // 按钮3向上移动 ValueAnimator va3 = ValueAnimator.ofInt(y, y - ConstantUtil.FAN_OFFSET); va3.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int l = (int) mFabTop.getX(); int t = (int) animation.getAnimatedValue(); int r = mFabTop.getWidth() + l; int b = mFabTop.getHeight() + t; mFabTop.layout(l, t, r, b); } }); // 开始动画 va1.start(); va2x.start(); va2y.start(); va3.start(); }
- 切换图标
然后就是在不同状态切换悬浮按钮的图标, 使用setImageResource方法即可.
mFabAdd.setImageResource(mLineMenuOpen ? R.drawable.ic_add : R.drawable.ic_close);
最后
我本人还是很喜欢google的material design的, 这个悬浮按钮也非常实用. 喜欢记得点赞或者关注我哦, 有意见或者建议评论区见~
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Architecture -- Lifecycle
1). 简介 生命周期感知组件执行操作以响应另一个组件(例如活动和片段)的生命周期状态的更改。 这些组件可帮助您生成更易于组织且通常更轻量级的代码,这些代码更易于维护。 一种常见的模式是在活动和片段的生命周期方法中实现依赖组件的操作。 但是,这种模式导致代码组织不良以及错误的增加。 通过使用生命周期感知组件,您可以将依赖组件的代码移出生命周期方法并移入组件本身。 2). 依赖 // ViewModel and LiveData implementation "android.arch.lifecycle:extensions:$lifecycle_version" // alternatively - just ViewModel implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // use -ktx for Kotlin // alternatively - just LiveData implementation "android.arch.lifecycle:livedata:$lifecy...
- 下一篇
android自定义相对复杂dialog
之前写了两篇关于Dialog的算是文章吧(2333),分别是: 1、 Android 系统原生dialog使用 2、 Android dialog Activity 使用 下面开始第三篇:相对复杂的自定义dialog。 一、转圈加载 dialog 见过大佬自己自定义的的 dialog ,也就是自己画出来的,额,楼主比较渣、楼主搞不了、我比较怂(一看就是有自知之明的好楼主)。但是呢,我还不服输,于是楼主用了偷懒的方法,废话不说,开干: 1、画个布局 即 layout.xml 文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools" android:background="@drawable/shape_wait_dialog" android:gravi...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS6,CentOS7官方镜像安装Oracle11G
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker安装Oracle12C,快速搭建Oracle学习环境