自定义View:侧滑菜单动画实现

侧滑菜单貌似从QQ5.0后就火了起来,道长感觉有些莫名其妙,但是想想产品经理那匮乏的想象力就知道原因了,大家都懂的~~~
这里道长先用动画实现一遍侧滑菜单,然后再用SlideMenu实现一遍。道长废话就不说了,直接飞起……

一、布局

1.主界面布局

这里道长重写了LinearLayout类用来拦截点击事件。

public class MyLinearLayout extends LinearLayout {
    public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyLinearLayout(Context context) {
        super(context);
    }

    private SlideMenu slideMenu;

    public void setSlideMenu(SlideMenu slideMenu) {
        this.slideMenu = slideMenu;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 如果slideMenu处于关闭,则拦截并消费掉
        if (slideMenu.getDragState() == SlideMenu.DragState.Close) {
            return true;// 拦截
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 如果slideMenu处于关闭,则消费掉
        if (slideMenu.getDragState() == SlideMenu.DragState.Close) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                // 则让SlideMenu关闭掉
                slideMenu.close();
            }
            return true;// 消费掉
        }
        return super.onTouchEvent(event);
    }
}


主界面的布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<com.yushan.slidingmenu.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dl_left"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include android:id="@+id/main_title"
        layout="@layout/layout_title"/>

    <!--主布局-->
    <TextView
        android:id="@+id/tv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        android:gravity="center"
        android:text="主界面"
        android:textColor="#333"
        android:textSize="20sp" />

</com.yushan.slidingmenu.MyLinearLayout>

2.侧滑菜单布局

注意:这里设置侧滑菜单的宽度即为侧滑菜单布局的宽度

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="250dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:background="#fff"
    android:orientation="vertical">

    <ListView
        android:id="@+id/lv_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none">

    </ListView>
</LinearLayout>

3.总布局

<?xml version="1.0" encoding="utf-8"?>
<com.yushan.slidingmenu.SlideMenu xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/slideMenu"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        android:id="@+id/dl_secondary"
        layout="@layout/layout_secondary" />

    <include
        android:id="@+id/dl_main"
        layout="@layout/layout_main" />

</com.yushan.slidingmenu.SlideMenu>

二、SlideMenu实现

  • 重写构造方法
    public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public SlideMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SlideMenu(Context context) {
        super(context);
        init();
    }

    private void init() {
        // 浮点类型的计算器
        floatEvaluator = new FloatEvaluator();
        viewDragHelper = ViewDragHelper.create(this, callback);
    }
  • 获取子控件并测量宽高
    /**
     * 方法功能:获取子控件
     */
    protected void onFinishInflate() {
        super.onFinishInflate();
        // 简单的异常处理
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("SlideMenu only support 2 children!");
        }
        menuView = getChildAt(0);
        mainView = getChildAt(1);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        measureChild(menuView, widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 一般是在当前的view执行完onMeasure方法之后调用,所以在该方法中肯定可以获取到宽高
     *
     * @param w:
     * @param h:
     * @param oldw:
     * @param oldh:
     */
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
//        dragRange = (int) (getMeasuredWidth() * 0.8f);
        dragRange = menuView.getMeasuredWidth();
    }
  • 重写Callback类中的方法
    这里面备注的比较详细了,道长就不一一细讲了。
    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        /**
         * 方法功能:捕获所有子控件
         * @param child:
         * @param pointerId:
         * @return :
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mainView;
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return dragRange;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child == mainView) {
                if (left < 0) {
                    left = 0;//限制左边
                }
                if (left > dragRange) {
                    left = dragRange;//限制右边
                }
            }
            return left;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            if (changedView == menuView) {
                //让menuView固定住,不要动
                menuView.layout(0, 0, menuView.getMeasuredWidth(), menuView.getMeasuredHeight());
                //同时让mainView伴随移动
                int newLeft = mainView.getLeft() + dx;
                //对newLeft进行限制
                if (newLeft < 0) newLeft = 0;//限制左边
                if (newLeft > dragRange) newLeft = dragRange;//限制右边
                mainView.layout(newLeft, 0, newLeft + mainView.getMeasuredWidth(), mainView.getMeasuredHeight());
            }
            //1.计算移动的百分比
            float fraction = mainView.getLeft() * 1f / dragRange;
            //2.根据移动的百分比去执行伴随动画
            executeAnim(fraction);
            //3.回调监听器的方法
            if (fraction == 1f && mState != DragState.Open) {
                //说明是打开,应该回调onOpen
                mState = DragState.Open;
                if (listener != null) {
                    listener.onOpen();
                }
            } else if (fraction == 0f && mState != DragState.Close) {
                //说明是关闭,应该回调onClose
                mState = DragState.Close;
                if (listener != null) {
                    listener.onClose();
                }
            }
            //回调onDragging
            if (listener != null) {
                listener.onDragging(fraction);
            }
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (mainView.getLeft() < dragRange / 2) {
                //在左半边,
                close();
            } else {
                //在右半边
                open();
            }
            //提高滑动的敏感度
            if (xvel > 200) {
                open();
            } else if (xvel < -200) {
                close();
            }
        }
    };
  • 关闭/打开动画
    /**
     * 方法功能:关闭动画
     */
    public void close() {
        mState = DragState.Close;
        viewDragHelper.smoothSlideViewTo(mainView, 0, mainView.getTop());
        ViewCompat.postInvalidateOnAnimation(SlideMenu.this);//刷新整个ViewGroup
    }

    /**
     * 方法功能:打开动画
     */
    public void open() {
        mState = DragState.Open;
        viewDragHelper.smoothSlideViewTo(mainView, dragRange, mainView.getTop());
        ViewCompat.postInvalidateOnAnimation(SlideMenu.this);//刷新整个ViewGroup
    }
  • 滑动过程中的动画
    这个看自己具体的需求自己选择……
    private void executeAnim(float fraction) {
        //fraction:0 - 1
        //先缩放mainView
//      float scaleVaule = 0.8f+(1-fraction)*0.2f;//1-0.8f
//        ViewHelper.setScaleX(mainView, floatEvaluator.evaluate(fraction, 1f, 0.8f));
//        ViewHelper.setScaleY(mainView, floatEvaluator.evaluate(fraction, 1f, 0.8f));
        //平移menuView
        ViewHelper.setTranslationX(menuView, floatEvaluator.evaluate(fraction, -menuView.getMeasuredWidth() / 2, 0));
        //缩放menuView
//        ViewHelper.setScaleX(menuView, floatEvaluator.evaluate(fraction, 1f, 0.5f));
//        ViewHelper.setScaleY(menuView, floatEvaluator.evaluate(fraction, 0.5f, 1f));
        //透明menuView
//        ViewHelper.setAlpha(menuView, floatEvaluator.evaluate(fraction, 0.3f, 1f));
        //给背景图片添加颜色的遮罩效果
//        getBackground().setColorFilter((Integer) ColorUtil.evaluateColor(fraction, Color.BLACK, Color.TRANSPARENT),
//                PorterDuff.Mode.SRC_OVER);
//      getBackground().setColorFilter((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.YELLOW),Mode.SRC_OVER);
    }
  • 添加拖拽回调监听方法
    private OnSlideStateChangeListener listener;

    public void setOnSlideStateChangeListener(OnSlideStateChangeListener listener) {
        this.listener = listener;
    }

    /**
     * 拖拽状态改变的监听器
     *
     * @author Administrator
     */
    public interface OnSlideStateChangeListener {
        void onOpen();

        void onClose();

        void onDragging(float fraction);
    }
  • 添加数据,实现回调方法
 private void initData(){
        menuData = new ArrayList<>();
        for (int i = 0; i < 50; i++){
            String menuStr = "第"+ (i + 1) +"个条目";
            menuData.add(menuStr);
        }

        adapter = new MenuListAdapter(this,menuData);
        lv_menu.setAdapter(adapter);
    }

    private void initView(){
        dl_main = (MyLinearLayout) findViewById(R.id.dl_main);
        slideMenu = (SlideMenu) findViewById(R.id.slideMenu);

        dl_main.setSlideMenu(slideMenu);
        slideMenu.setOnSlideStateChangeListener(new SlideMenu.OnSlideStateChangeListener() {
            @Override
            public void onOpen() {

            }

            @Override
            public void onClose() {
                // 借助NineOldAndroid的VIewPropertyAnimator
                ViewPropertyAnimator.animate(dl_main)
                        .translationX(5)
                        .setInterpolator(new CycleInterpolator(4))
                        .setDuration(500)
                        .start();
            }

            @Override
            public void onDragging(float fraction) {
                ViewHelper.setAlpha(dl_main, 1 - fraction/2);
            }
        });

        lv_menu = (ListView)findViewById(R.id.lv_menu);
        lv_menu.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                String str = menuData.get(position);
                Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();
            }
        });
    }

到这里就完了,希望这篇博客可以给你提供一些帮助。干巴巴的,连效果图都没有,如果有需要可以自己去github上下载Demo。

源码下载

SlidingMenuAnim


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值