自定义控件
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的侧拉菜单效果的更多相关文章

  1. Android仿QQ ios dialog,仿QQ退出向上菜单

    Android仿QQ ios dialog,仿QQ退出向上菜单 EasyDialog两种模式 仿QQ退出向上菜单,自己定义向上菜单              github地址:https://gith ...

  2. 使用DrawerLayout实现QQ5.0侧拉菜单效果

    在上一篇文章中,我们介绍了怎么使用DrawerLayout来实现一个简单的侧拉菜单(使用DrawerLayout实现侧拉菜单),也就是我们常说的抽屉效果,GitHub上类似效果的实现方式非常多,实现出 ...

  3. Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单

    最近看到有人用Dialog来实现QQ的仿ActionSheet的自定义菜单,对于自己没实现过的一些控件,看着也想实现一下.于是动手了一下,发现也不难,和大家分享一下. 本文原创,转载请注明出处:htt ...

  4. 仿qq最新侧滑菜单

    为了后续对这个项目进行优化,比如透明度动画.背景图的位移动画,以及性能上的优化. 我把这个项目上传到github上面,请大家随时关注. github地址https://github.com/sungu ...

  5. 仿QQ空间长图效果简易版--母亲节感恩

    手机网站 母亲节最火的两件事 1.NBA 杜兰特在获MVP催泪致辞献给母亲:她才是真的MVP. 2.QQ空间长图 ------------------------------------------- ...

  6. Android自定义View实现仿QQ实现运动步数效果

    效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...

  7. android桌面悬浮窗仿QQ手机管家加速效果

    主要还是用到了WindowManager对桌面悬浮进行管理. 需要一个火箭的悬浮窗 一个发射台悬浮窗  ,判断火箭是否放到了发射台,如果放上了,则使用AsyTask 慢慢将火箭的图片往上移.结束后., ...

  8. 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

    重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...

  9. android开发学习 ------- 仿QQ侧滑效果的实现

    需要做一个仿QQ侧滑删除的一个效果: 一开始是毫无头绪,百度找思路,找到  https://blog.csdn.net/xiaxiazaizai01/article/details/53036994  ...

随机推荐

  1. Asp.Net MVC页面静态化功能实现二:用递归算法来实现

    上一篇提到采用IHttpModule来实现当用户访问网站的时候,通过重新定义Response.Filter来实现将返回给客户端的html代码保存,以便用户下一次访问是直接访问静态页面. Asp.Net ...

  2. javascript保留两位

    原文:javascript保留两位 //保留两位小数 //功能:将浮点数四舍五入,取小数点后2位 function toDecimal(x) { var f = parseFloat(x); if ( ...

  3. 免费的编程中文书籍索引 from github

    免费的编程中文书籍索引 免费的编程中文书籍索引,欢迎投稿. 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版. stackoverflow 上的程序员应该阅读的非编程类书籍有哪些 ...

  4. AjaxPro实现无刷新更新数据

    使用AjaxPro实现无刷新更新数据 需求 在一个页面动态无刷新的更新后台得到的数据.要想无刷新的更新数据,需要使用Javascript能够获取后台返回的数据,然后通过第三方Javascript库(J ...

  5. mysql主从同步配置(windows环境)

    mysql主从同步配置(mysql5.5,windows环境)   A主机(作为主服务器)环境:windows8.mysql5.5 ip:192.168.1.100(自己填) B主机(作为从服务器,由 ...

  6. mvc上传头像加剪裁功能

    asp.net mvc上传头像加剪裁功能 正好项目用到上传+剪裁功能,发上来便于以后使用. 我不能告诉你们其实是从博客园扒的前台代码,哈哈. 前端是jquery+fineuploader+jquery ...

  7. EF 关系描述

    网络上常常看到有ef 1对1 1对多等关系的描述,按照我的理解,其根本就是为了呈现出一个视图,我最近设计了一个ef关系,请大家看一看. 需求描述 在gps车辆信息管理中,有个开户需求,其根本就是三种关 ...

  8. JavaScript实例技巧精选(14)—动态变化背景颜色

    >>点击这里下载完整html源码<< 这是截图: 网页背景颜色随时间变化,核心代码如下: <SCRIPT LANGUAGE="JavaScript"& ...

  9. RDLC(Reportview)报表直接打印,支持所有浏览器,客户可在linux下浏览使用

    最近在做一个打印清单的,但是rdlc报表自带的工具栏中的打印按钮只有在ie内核下的浏览器才可以使用(其他的就会 隐藏),这导致了使用火狐和谷歌浏览器还有使用linux系统的客户打印成了问题,于是就自己 ...

  10. Undefined class constant 'MYSQL_ATTR_USE_BUFFERED_QUERY'

    The error that occurs is:Fatal error: Undefined class constant 'MYSQL_ATTR_USE_BUFFERED_QUERY' in /w ...