仿qq的侧拉菜单效果
自定义控件 import android.animation.ArgbEvaluator; import android.animation.FloatEvaluator; import android.content.Context; import android.graphics.Color; import android.graphics.PorterDuff; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.ScaleAnimation; import android.widget.FrameLayout; import android.widget.Scroller; /** * 让SlideMen去继承系统已有的布局,目的是为了让他们帮我们实现onMeasure方法, * 一般的话我们会选择继承FrameLayout,因为FrameLayout最轻量级 */ public class SlideMenu extends FrameLayout { ViewDragHelper dragHelper; private View menu; private View main; int maxLeft; ArgbEvaluator argbEval = new ArgbEvaluator(); FloatEvaluator floatEval = new FloatEvaluator(); public SlideMenu(Context context) { this(context, null); } public SlideMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); dragHelper = ViewDragHelper.create(this,callback); } /** * 该方法是当前view在布局文件中的xml结束标签读取完后执行,此时就知道 * 当前View有几个子View了,但是注意:此时还不能获取子View的宽高,因为 * 还木有测量呢! */ @Override protected void onFinishInflate() { super.onFinishInflate(); menu = getChildAt(0); main = getChildAt(1); } /** * 该方法是onMeasure执行之后执行,因此可获取宽高 * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); maxLeft = (int) (getMeasuredWidth()*0.6f); } // /** // * 用来测量自己和自己的子View的 // * @param widthMeasureSpec // * @param heightMeasureSpec // */ // @Override // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // //只需要测量子View即可 // // for (int i = 0; i < getChildCount(); i++) { // View child = getChildAt(i); // measureChild(child,widthMeasureSpec,heightMeasureSpec); // } // } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //让ViewDragHelper帮助我们判断是否应该拦截 boolean result = dragHelper.shouldInterceptTouchEvent(ev); return result; } @Override public boolean onTouchEvent(MotionEvent event) { //让ViewDragHelper帮我们处理触摸事件 dragHelper.processTouchEvent(event); return true; } ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { /** * 尝试监视View的触摸事件 * @param child 当前触摸的子View * @param pointerId 手指多点触摸时的触摸点的索引 * @return true表示监视 ,false就是忽略不管 */ @Override public boolean tryCaptureView(View child, int pointerId) { return child==main || child==menu; } /** * 看起来是获取View水平的拖拽范围的,然而并不是这样,这是一个鸡肋的方法,目前它的作用是 * 用来判断你是否想强制水平滑动的,如果想强制水平滑动,则返回大于0的任意值 * @param child * @return */ @Override public int getViewHorizontalDragRange(View child) { return 1; } /** * 修正修改View水平方向的位置移动,控制水平移动的 * @param child 当前触摸的子View * @param left ViewDragHelper认为我想让View的left变成的值,它是这样计算好的:child.getLeft+dx * @param dx 本次手指移动的水平距离 * @return 最终返回的值表示我们真正想让child的left变成的值 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //只限制main if(child==main){ left = clampLeft(left); } return left; } /** * 修正修改View垂直方向的位置移动,控制垂直移动的 * @param child 当前触摸的子View * @param top ViewDragHelper认为我想让View的top变成的值,它是这样计算好的:child.getTop+dy * @param dy 本次手指移动的垂直距离 * @return 最终返回的值表示我们真正想让child的top变成的值 */ @Override public int clampViewPositionVertical(View child, int top, int dy) { return 0; } /** * 当View位置改变的时候执行 * @param changedView 当前位置改变的View * @param left 当前VIew改变后最新的left * @param top 当前VIew改变后最新的top * @param dx 本次移动的水平距离 * @param dy 本次移动的垂直距离 */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); // Log.e("tag","left: "+left + " dx: "+dx); //根据dx,让main进行伴随的移动 if(changedView==menu){ //手动让menu固定在原点位置 menu.layout(0,0,menu.getMeasuredWidth(),menu.getBottom()); int newLeft = main.getLeft()+dx; //对newLeft进行限制 newLeft = clampLeft(newLeft); main.layout(newLeft,0,newLeft+main.getMeasuredWidth(), main.getBottom()); } //执行动画 //1.获取main拖动的百分比:0-1f float fraction = main.getLeft()*1f / maxLeft; //2.根据百分比去执行一些列的伴随的动画 execAnim(fraction); //3.回调接口的方法 if(listener!=null){ listener.onSliding(fraction); if(fraction==0f){ listener.onClose(); }else if(fraction==1f){ listener.onOpen(); } } } /** * 手指抬起的时候执行 * @param releasedChild 抬起的那个子View * @param xvel x方向滑动的速度,单位是px/s * @param yvel y方向滑动的速度 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); // Log.e("tag","xvel: "+xvel); if(main.getLeft()>maxLeft/2){ //要open openMenu(); }else { //close closeMenu(); } } }; /** * 执行伴随动画 * @param fraction */ private void execAnim(float fraction) { //fraction:0f - 1f //main执行缩放动画 //scale: 1f - 0.8f //算法: startVal + (endVal-startVal)*fraction float scale = floatEval.evaluate(fraction,1f,0.8f); main.setScaleY(scale); main.setScaleX(scale); //menu执行缩放 menu.setScaleX(floatEval.evaluate(fraction,0.3f,1f)); menu.setScaleY(floatEval.evaluate(fraction,0.3f,1f)); //menu执行平移 menu.setTranslationX(floatEval.evaluate(fraction,-menu.getMeasuredWidth()/2,0)); //立体3D效果 // main.setRotationY(floatEval.evaluate(fraction,0,90)); // menu.setRotationY(floatEval.evaluate(fraction,-90,0)); //给SlideMenu的背景图片添加阴影遮罩效果 if(getBackground()!=null){ int color = (int) argbEval.evaluate(fraction,Color.BLACK,Color.TRANSPARENT); getBackground().setColorFilter(color,PorterDuff.Mode.SRC_OVER); } } /** * 关闭菜单 */ public void closeMenu() { dragHelper.smoothSlideViewTo(main,0,0); ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } /** * 打开菜单 */ public void openMenu() { // scroller.startScroll(); // invalidate(); //ViewDragHelper的写法是这样滴: dragHelper.smoothSlideViewTo(main,maxLeft,0); ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } // Scroller scroller = null; @Override public void computeScroll() { super.computeScroll(); // if(scroller.computeScrollOffset()){ // //如果动画还没有结束 // scrollTo(scroller.getCurrX(),scroller.getCurrY()); // invalidate(); // } if(dragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } } /** * 判断left的值 * @param left * @return */ private int clampLeft(int left) { if(left<0){ left = 0; }else if(left>maxLeft){ left = maxLeft; } return left; } OnSlideListener listener; public void setOnSlideListener(OnSlideListener listener){ this.listener = listener; } //定义回调接口 public interface OnSlideListener{ void onSliding(float fraction); void onOpen(); void onClose(); } }
import android.animation.ObjectAnimator; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.animation.BounceInterpolator; import android.view.animation.CycleInterpolator; import android.view.animation.OvershootInterpolator; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import butterknife.Bind; import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity { @Bind(R.id.menu_listview) ListView menuListview; @Bind(R.id.iv_head) ImageView ivHead; @Bind(R.id.main_listview) ListView mainListview; @Bind(R.id.slidemenu) SlideMenu slidemenu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //填充数据 mainListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1 , Constant.NAMES)); menuListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1 , Constant.sCheeseStrings) { @NonNull @Override public View getView(int position, View convertView, ViewGroup parent) { TextView view = (TextView) super.getView(position, convertView, parent); view.setTextColor(Color.WHITE);//偷梁换柱 return view; } }); //添加滑动监听器 slidemenu.setOnSlideListener(new SlideMenu.OnSlideListener() { @Override public void onSliding(float fraction) { ivHead.setRotation(720*fraction); } @Override public void onOpen() { Toast.makeText(MainActivity.this, "芝麻开门!", Toast.LENGTH_SHORT).show(); } @Override public void onClose() { Toast.makeText(MainActivity.this, "芝麻关门-----!", Toast.LENGTH_SHORT).show(); ViewCompat.animate(ivHead) .translationX(60) // .setInterpolator(new CycleInterpolator(4))//循环执行 // .setInterpolator(new OvershootInterpolator(4))//超过一点再回来 .setInterpolator(new BounceInterpolator())//当当当当~~~~ .setDuration(1000) .start(); } }); } }
仿qq的侧拉菜单效果的更多相关文章
- Android仿QQ ios dialog,仿QQ退出向上菜单
Android仿QQ ios dialog,仿QQ退出向上菜单 EasyDialog两种模式 仿QQ退出向上菜单,自己定义向上菜单 github地址:https://gith ...
- 使用DrawerLayout实现QQ5.0侧拉菜单效果
在上一篇文章中,我们介绍了怎么使用DrawerLayout来实现一个简单的侧拉菜单(使用DrawerLayout实现侧拉菜单),也就是我们常说的抽屉效果,GitHub上类似效果的实现方式非常多,实现出 ...
- Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单
最近看到有人用Dialog来实现QQ的仿ActionSheet的自定义菜单,对于自己没实现过的一些控件,看着也想实现一下.于是动手了一下,发现也不难,和大家分享一下. 本文原创,转载请注明出处:htt ...
- 仿qq最新侧滑菜单
为了后续对这个项目进行优化,比如透明度动画.背景图的位移动画,以及性能上的优化. 我把这个项目上传到github上面,请大家随时关注. github地址https://github.com/sungu ...
- 仿QQ空间长图效果简易版--母亲节感恩
手机网站 母亲节最火的两件事 1.NBA 杜兰特在获MVP催泪致辞献给母亲:她才是真的MVP. 2.QQ空间长图 ------------------------------------------- ...
- Android自定义View实现仿QQ实现运动步数效果
效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...
- android桌面悬浮窗仿QQ手机管家加速效果
主要还是用到了WindowManager对桌面悬浮进行管理. 需要一个火箭的悬浮窗 一个发射台悬浮窗 ,判断火箭是否放到了发射台,如果放上了,则使用AsyTask 慢慢将火箭的图片往上移.结束后., ...
- 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。
重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...
- android开发学习 ------- 仿QQ侧滑效果的实现
需要做一个仿QQ侧滑删除的一个效果: 一开始是毫无头绪,百度找思路,找到 https://blog.csdn.net/xiaxiazaizai01/article/details/53036994 ...
随机推荐
- Java Persistence with MyBatis 3(中国版)
译者的话 前段时间因为工作和学习的须要,我打算深入研究MyBatis框架.于是在网上查找关于MyBatis的教程,发现国内网上关于MyBatis的教程资料少得可怜:除了MyBatis官网上的用户使用手 ...
- android 实现分享功能两种方法
当我想做一个智能的记事本的时候,我就在尝试自己写一组分享功能.后来才知道,原来每个社交软件中都有自己的分享接口. 这就大大减少了我们的代码量了. 第一种方法:特点--简单 package com.ex ...
- Hibernate在自由状态和持久的状态转变
在Hibernate在.一PO术后可能长时间,session过时关闭.此时PO它一直是游离状态的对象,在这种状态下,以被转换成持久战,有几种方法如下: 1.session.saveOrUpdate(o ...
- 一步一步实现基于Task的Promise库(四)无参数的WorkItem
接着上一篇我直接给出代码,现在支持了new Task(), then(), all(), any() 这些不传参的调用方式. (function(){ var isFunction = functio ...
- vector如何进行局部排序
对于vector的全体排序,我们知道sort(vv.begin(),vv.end())来进行的. 但是对于如果是局部排序的话,比如,vector有100个元素,但我只想对10-80之间的数进行排序,如 ...
- 栈和队列简单的STL模板
栈的使用,可以想象成是只有一个出口,最后进来的那个最先出去: #include <stack> 队列:是有两个出口,但是进来了之后只能从前门出去,也就是最先进来的那个最后出去: #incl ...
- 最小生成树算法prim and kruskal
一.最小生成树定义: 从不同顶点出发或搜索次序不同,可得到不同的生成树 生成树的权:对连通网络来说,边附上权,生成树也带权,我们把生成树各边的权值总和称为生成树的权 最小代价生成树:在一个连通网 ...
- git命令与github使用
GitHub 是一个类似 SourceForge 的免费项目管理及分享的服务平台,要想使用 github,用户可以免费注册成为 github 注册用户,之后使用git命令进行操作使用.github 的 ...
- 通过Func 委托理解委托和匿名方法及Lambda 表达式
Func<T, TResult> 委托 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法. 命名空间: System 程序集: mscorlib(在 mscorlib.d ...
- C#线程池用法
C#线程池用法 在C#编程语言中,使用线程池可以并行地处理工作,当强制线程和更新进度条时,会使用内建架构的ThreadPool类,为批处理使用多核结构,这里我们来看在C#编程语言中一些关于来自Syst ...