标签:android 5.0 侧滑菜单 按钮过度动画
尊重原创,转载请注明出处:http://blog.csdn.net/a740169405/article/details/50285017

前言:

前阵子看到Android 5.0 的新的侧滑菜单按钮,其有打开和关闭两种状态,状态之间切换时也会有动画过度。
突发奇想的自己实现了一个。效果实现是引用了NineOldAndroids开源库来实现动画效果,自己只是加了一些基本的逻辑处理。
加上自己实现的一个抽屉效果,并实现按钮和抽屉联动,总体看起来和Android 5.0原生的效果类似。

效果:

实现

视图设计:

按钮继承了一个FrameLayout,并添加三个View对象,用来绘制三条直线。

public class MaterialMenuButton extends FrameLayout

在构造函数中初始化三个View对象,并设置颜色,最后添加到父容器中。

// 初始化三条线
mFirstLineView = new View(context);
mSecondLineView = new View(context);
mThirdLineView = new View(context);
mFirstLineView.setBackgroundColor(mLineColor);
mSecondLineView.setBackgroundColor(mLineColor);
mThirdLineView.setBackgroundColor(mLineColor);
addView(mFirstLineView);
addView(mSecondLineView);
addView(mThirdLineView);

到这里视图已经完成了一半,我们思考一下下面两个问题:
1. 因为是FrameLayout,所以addView之后,三条直线会跌在一起。
2. 其次没有设置线的高度,FrameLayout会使用默认的LayoutParams,其宽高都是包裹内容,导致三个View都显示不出来。

接着我们看看如何设置三条线的视图属性。
思路是监测视图的改变,并在视图绘制完成后设置三条线的视图属性。这里我采用的方式是为父容器添加视图改变回调接口OnGlobalLayoutListener:

// 添加视图绘制监听器
getViewTreeObserver().addOnGlobalLayoutListener(this);

实现相应的回调

@Override
public void onGlobalLayout() {int width = getWidth();int height = getHeight();if (!isInitialization && width != 0 && height != 0) {// 当未初始化信息,并且当前按钮已经绘制完成,能够拿到宽高值if (mWidth != width || mHeight != height) {int mLineMaxWidth = (int) (width * 1.0f / 2.0f);// mLineMinWidth = (int) (mLineMaxWidth * 2.0f / 3.0f);float mCenterX = width / 2.0f;float mCenterY = height / 2.0f;// 三条线的左侧位置float mLineLeft = mCenterX - mLineMaxWidth / 2.0f;// 第一线线的Y轴位置float mFirstLineStartY = mCenterY - Math.min(width, height) / 8.0f;// 第三条线的Y轴位置float mThirdLineStartY = mCenterY + Math.min(width, height) / 8.0f;// 当前按钮的宽高mWidth = width;mHeight = height;// 设置三条线的左上角X,Y轴值ViewHelper.setX(mFirstLineView, mLineLeft);ViewHelper.setY(mFirstLineView, mFirstLineStartY);ViewHelper.setX(mSecondLineView, mLineLeft);ViewHelper.setY(mSecondLineView, mCenterY);ViewHelper.setX(mThirdLineView, mLineLeft);ViewHelper.setY(mThirdLineView, mThirdLineStartY);// 设置三条直线的宽高值ViewGroup.LayoutParams lp;lp = mFirstLineView.getLayoutParams();lp.width = mLineMaxWidth;lp.height = mLineHeight;mFirstLineView.setLayoutParams(lp);lp = mSecondLineView.getLayoutParams();lp.width = mLineMaxWidth;lp.height = mLineHeight;mSecondLineView.setLayoutParams(lp);lp = mThirdLineView.getLayoutParams();lp.width = mLineMaxWidth;lp.height = mLineHeight;mThirdLineView.setLayoutParams(lp);// 记录三条线的各种操作起始值和结束值mFirstLineRotation = new KeyFrameSet(0, 225);mFirstLineXValues = new KeyFrameSet(mLineLeft, mLineLeft - mLineMaxWidth * 0.1f);mFirstLineYValues = new KeyFrameSet(mFirstLineStartY, mThirdLineStartY - 2);mThirdLineXValues = new KeyFrameSet(mLineLeft, mLineLeft - mLineMaxWidth * 0.1f);mThirdLineYValues = new KeyFrameSet(mThirdLineStartY, mFirstLineStartY + 2);mThirdLineRotation = new KeyFrameSet(0, 135);mSecondLineRotation = new KeyFrameSet(0, 180);mFirstOrThirdLineWidth = new KeyFrameSet(mLineMaxWidth, (int) (mLineMaxWidth * 0.7f));}// 标记已经初始化了isInitialization = true;}
}

在回调函数中,isInitialization是判断时候已经初始化按钮的标识,初始化一次后就不会再次初始化了。

好的,到这里视图基本布置好了。接下来要做的就是过度动画了。
动画我使用NineOldAndroids开源动画库来实现。
首先我们要记录动画的开始和结束值,这一步需要放在视图的初始化位置。也就是上面的onGlobalLayout回调方法里。

// 记录三条线的各种操作起始值和结束值
// 第一条线需要旋转225°
mFirstLineRotation = new KeyFrameSet(0, 225);
// 第一条线的X轴移动距离
mFirstLineXValues = new KeyFrameSet(mLineLeft, mLineLeft - mLineMaxWidth * 0.1f);
// 第一条线的Y轴移动距离
mFirstLineYValues = new KeyFrameSet(mFirstLineStartY, mThirdLineStartY - 2);
// 第三条线的X轴移动距离
mThirdLineXValues = new KeyFrameSet(mLineLeft, mLineLeft - mLineMaxWidth * 0.1f);
// 第三条线的Y轴移动距离
mThirdLineYValues = new KeyFrameSet(mThirdLineStartY, mFirstLineStartY + 2);
// 第三条线需要旋转135
mThirdLineRotation = new KeyFrameSet(0, 135);
// 第二条线需要旋转180°
mSecondLineRotation = new KeyFrameSet(0, 180);
// 第一条或者第三条线的宽度改变
mFirstOrThirdLineWidth = new KeyFrameSet(mLineMaxWidth, (int) (mLineMaxWidth * 0.7f));

解释一下,打开的时候:
1. 第一条线需要做三个操作,旋转,向左移动一定距离,宽度缩短一定距离。
2. 第二条线需要旋转180°
3. 第三条线需要做三个操作,旋转,向左移动一定距离,宽度缩短一定距离。
当关闭的时候,其实做的是一个逆向操作。

接下来,我们要做的是,为按钮设置点击事件,并在点击后播放过度动画。
这里我重写了FrameLayout的setOnClickListener方法,为了防止外界设置点击事件与内部的点击事件冲突:

@Override
public void setOnClickListener(OnClickListener l) {// super.setOnClickListener(l);this.mOnClickListener = l;
}

并在构造函数中调用父类的setOnClickListener设置点击事件:

// 添加点击事件
super.setOnClickListener(new MyOnClickListener());

看看在MyOnClickListener里做的事情:

private class MyOnClickListener implements OnClickListener {@Overridepublic void onClick(View v) {playAnimation();// 调用外部设置的点击事件if (mOnClickListener != null) {mOnClickListener.onClick(MaterialMenuButton.this);}}
}

在点击事件里执行了过度动画的播放,以及回调外部设置的点击事件。
看看是如何播放动画的:

/*** 播放动画*/
private void playAnimation() {if (!mIsAutoAnimating) {// 如果外部设置了不需要在点击的时候播放动画return;}if (animator == null) {animator = ObjectAnimator.ofFloat(0, 100);animator.setDuration(800);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 更新视图update(animation.getAnimatedFraction());}});animator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {// 当动画结束的时候,切换当前按钮的打开状态标识mIsOpened = !mIsOpened;}});}// 先判断动画是否正在执行if (animator.isStarted()) {return;}animator.start();
}

动画实在800ms里完成的,并且只是让属性值从0增长到100,这其实相当于一个百分比。
在动画属性值改变的时候调用了update方法,用来根据当前时间的改变百分比来改变三条直线的属性值。
update:

/*** 根据当前动画执行的百分比,更新视图* @param fraction*/
public void update(float fraction) {if (fraction < 0 || fraction > 1) {return;}updateFirstLineRotation(fraction);updateFirstLineY(fraction);updateFirstOrThirdLineWidth(fraction);updateFistLineX(fraction);updateSecondLinRotation(fraction);updateThirdLineRotation(fraction);updateThirdLineX(fraction);updateThirdLineY(fraction);
}

该函数调用了对应的函数用来改变三条指向的属性值。
这些函数其实都是根据当前动画已经完成百分比来设置对应的属性值,有X轴,Y轴,还有旋转角度:

public void updateFistLineX(float fraction) {ViewHelper.setX(mFirstLineView, mFirstLineXValues.getCurrentValue(fraction));
}private void updateThirdLineX(float fraction) {ViewHelper.setX(mThirdLineView, mThirdLineXValues.getCurrentValue(fraction));
}private void updateFirstLineY(float fraction) {ViewHelper.setY(mFirstLineView, mFirstLineYValues.getCurrentValue(fraction));
}private void updateThirdLineY(float fraction) {ViewHelper.setY(mThirdLineView, mThirdLineYValues.getCurrentValue(fraction));
}private void updateFirstLineRotation(float fraction) {ViewHelper.setRotation(mFirstLineView, mFirstLineRotation.getCurrentValue(fraction));
}private void updateSecondLinRotation(float fraction) {ViewHelper.setRotation(mSecondLineView, mSecondLineRotation.getCurrentValue(fraction));
}private void updateThirdLineRotation(float fraction) {ViewHelper.setRotation(mThirdLineView, mThirdLineRotation.getCurrentValue(fraction));
}private void updateFirstOrThirdLineWidth(float fraction) {int value = (int) mFirstOrThirdLineWidth.getCurrentValue(fraction);ViewGroup.LayoutParams lp = mFirstLineView.getLayoutParams();lp.width = value;mFirstLineView.requestLayout();lp = mThirdLineView.getLayoutParams();lp.width = value;mThirdLineView.requestLayout();
}

到这里,差不多按钮的自定义完成了。

接下来我们看看怎么和抽屉布局进行联动。
其实就是根据抽屉的移动百分比回调给按钮,让按钮实时改变线条属性值:

/*** Material按钮与抽屉联动效果Activity*/
public class MaterialMenuActivity extends Activity implements View.OnClickListener {private static final String TAG = MaterialMenuActivity.class.getSimpleName();private GenericDrawerLayout mGenericDrawerLayout;private MaterialMenuButton mMaterialMenuButton;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_menu);mGenericDrawerLayout = (GenericDrawerLayout) findViewById(R.id.genericdrawerlayout);mMaterialMenuButton = (MaterialMenuButton) findViewById(R.id.materialmenubutton);mGenericDrawerLayout.setOpaqueWhenTranslating(true);mGenericDrawerLayout.setMaxOpaque(0.6f);TextView textView = new TextView(this);textView.setBackgroundColor(Color.parseColor("#00A4A6"));textView.setGravity(Gravity.CENTER);textView.setText("GenericDrawerLayout");textView.setTextSize(22);mGenericDrawerLayout.setContentLayout(textView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));// 设置抽屉留白mGenericDrawerLayout.setDrawerEmptySize((int) (getResources().getDisplayMetrics().density * 120 + 0.5f));mMaterialMenuButton.setOnClickListener(this);// 设置不需要自动播放动画,因为抽屉会回调动画的执行mMaterialMenuButton.setAutoAnimating(false);mGenericDrawerLayout.setDrawerCallback(mGenericDrawerCallback);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.materialmenubutton) {mGenericDrawerLayout.switchStatus();}}private GenericDrawerLayout.DrawerCallback mGenericDrawerCallback = new GenericDrawerLayout.DrawerCallbackAdapter() {@Overridepublic void onTranslating(int gravity, float translation, float fraction) {super.onTranslating(gravity, translation, fraction);Log.e(TAG, "fraction = " + fraction);mMaterialMenuButton.update(fraction);}};
}

这里的抽屉是一个从四侧都可以拉出来的一个自定义视图,大家可以阅读:
Android 自定义万能的抽屉布局(侧滑菜单)GenericDrawerLayout

在抽屉的onTranslating回调方法里调用了按钮的update方法,并把当前的百分比传递进去,完成联动的效果。

按钮以及抽屉源码已经上传到gitHub:
https://github.com/a740169405/GenericDrawerLayout
csdn上也上传一份(2015年12月13日上传):
http://download.csdn.net/detail/a740169405/9351925

仿Android 5.0 侧滑菜单按钮动画 以及侧滑菜单联动相关推荐

  1. css3.0动画,CSS3.0实现霓虹灯按钮动画特效的示例代码

    今天给大家分享一个用CSS 3.0实现的霓虹灯按钮动画特效,效果如下: 以下是代码实现,欢迎大家复制粘贴和收藏. CSS 3.0实现霓虹灯按钮动画特效 * { font-family: '微软雅黑', ...

  2. android4.0 菜单,Android 4.0.4系统曝光 增新Power菜单

    [IT168 资讯]尽管Android4.0.3已经推出很久了,但是目前为止,三星Galaxy Nexus运行的仍旧是Android4.0.2的系统更新,与4.0.3相比,4.0.2版本不仅应用界面不 ...

  3. 仿微信6.0的界面按钮切换产生渐变效果

    最近看了一个视频讲的是自定义个view模仿微信的四个菜单按钮切换时的颜色产生渐变的效果,最后实现出来感觉好不错所以我就做了总结希望对你们有用. 先看效果吧,不知道用什么软件来录制gif图你们就凑合看吧 ...

  4. 按钮 交互_SwiftUI中的微交互—菜单按钮动画

    按钮 交互 Microinteractions have become increasingly important in a world with a dizzying number of digi ...

  5. android+3.0新加的动画,Android动画片

    使用Android两年多了,工作中的动画也动能应付,自认为Android中的动画自己也能用个八九不离十,结果我在学习[Periscope点赞效果](http://www.jianshu.com/p/0 ...

  6. html 悬浮菜单按钮,HTML右侧悬浮菜单

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 模仿的是B站的右侧菜单 代码: 右侧悬浮菜单 *{margin:0;padding: 0;box-sizing: border-box;} div{bor ...

  7. 仿抖音短视频录制按钮动画

    最近找工作,面试遇到了一个问题问抖音的那个录制按钮效果如何实现.说到这里熟悉自定义View的话,很容易就想到这里关键是考Xfermode的了解及使用. 话不多说先上图 项目源码 现在就让我们来动手实现 ...

  8. Android 10.0横屏旋转开机动画显示不全的解决方案

    1.概述 在10.0的产品进行平板设备开发中,需要横屏显示,所以需要把屏幕方向旋转90,作为横屏显示,但是这样可能会引起一个问题 就是开机动画显示不全,就是虽然画面是横屏显示了 但是只能显示一部分显示 ...

  9. Android 9.0横屏旋转开机动画显示不全的解决方案

    1.前言 在9.0的系统产品rom开发中,在进行平板设备开发中,需要横屏显示,所以需要把屏幕方向旋转90,作为横屏显示,但是这样可能会引起一个问题 就是开机动画显示不全,就是虽然画面是横屏显示了 但是 ...

  10. android studio2.0去除运行按钮旁边闪电标志的方法

    Flie -> settings ->Instant Run 去掉第一个钩 这个是安卓叫做启用即时运行 热交换代码/资源变化 部署(默认启用)的新技术,开启后回产生应用开启时间长和有时后对 ...

最新文章

  1. 【跃迁之路】【495天】程序员高效学习方法论探索系列(实验阶段252-2018.06.15)...
  2. springboot相关书籍文献_国内民国时期文献酸化调研与思考
  3. FPGA的设计艺术(14)使用函数和任务提升逻辑的可重用性
  4. 长方形纸做容积最大的长方体_儿童手工折纸,童年玩具纸扇子怎么折?一起来回忆下经典折法吧...
  5. 微软亚研提出VL-BERT:通用的视觉-语言预训练模型
  6. 江西理工大学c语言程序设计竞赛怎么备考,2015年江西理工大学C语言程序设计竞赛(高级组)...
  7. 【Python】自定义排序函数 - 示例
  8. 红旗linux添加usb无线网卡,在Ubuntu 8.10中安装无线网卡RTL8187SE驱动
  9. python学习第22天
  10. html5是未来,开始用吧!
  11. mel滤波器组频率响应曲线_非常好的滤波器知识总结,值得一看!
  12. [转]Ubuntu 常用快捷键10个
  13. Nuget如何自动下载依赖DLL引用
  14. spring认证的一些核心类
  15. 【Leetcode】数学题(Python)
  16. 安卓模拟定位mockLocation,闪退?
  17. 教你itunes电脑版怎么下载
  18. 支付宝第三方在线支付接口详解
  19. 用chrome 观看youtube视频显示中文字幕
  20. 送一首诗给心急的、望子成龙的父母们——《牵一只蜗牛去散步》

热门文章

  1. sublime text3 错误解决
  2. 分组 php库,纯真ip数据库查询的php实现(补充分组查询)
  3. .NET重要技术思考
  4. mysql主从集群搭建;(集群复制数据)
  5. jquery migrate 应用迁移辅助插件
  6. 微信内置浏览器不支持 onclick 如何解决?(原因是因为内面中的内容或者标签大部分是动态生成的)...
  7. C++ 输入输出,IO
  8. centos6.5安装mysql-udf-http日记
  9. GNU make 汇总
  10. Hibernate读书笔记-----事件机制