自定义ViewGroup实现侧滑功能

首先我们必须知道:事件分发是Android中非常重要的机制,是用户与界面交互的基础。与事件分发相关的方法主要有三个:
dispatchTouchEvent();分发
onInterceptTouchEvent:拦截
onTouchEvent();触摸,消费
而事件分发一般会经过三种容器,分别为Activity、ViewGroup、View。onInterceptTouchEvent比较特殊,只有在ViewGroup容器中才存在.

其三个方法所代表的含义为:
分发: dispatchTouchEvent如果返回true,则表示在当前View或者其子View(子子…View)中,找到了处理事件的View;反之,则表示没有寻找到
拦截: onInterceptTouchEvent如果返回true,则表示这个事件由当前View进行处理,不管处理结果如何,都不会再向子View传递这个事件;反之,则表示当前View不主动处理这个事件,除非他的子View返回的事件分发结果为false
触摸消费: onTouchEvent如果返回true,则表示当前View就是事件传递的终点;反之,则表示当前View不是事件传递的终点

在发生触摸消费的时候:触摸事件是一个先自顶向下分发的过程,最早发送在activity,接着activity向下传递到第一个
viewGroup,viewGroup将事件接着往下传递,一直传到除了需要处理这个事件的类。如果一直没有任何控件能够出来这个事件,事件就会往上传递,一直到activity。

代码实现效果图:
这里写图片描述

1,自定义类继承ViewGroup,并在布局文件中引入该自定义类

package com.m520it.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * @author
 * @time
 * @desc
 */
//不利用其坐标的变化实现移动
public class SlideMeun2 extends ViewGroup {

    private View mMeunView;
    private View mContentView;
    private int mMeunHeight;
    private int mMeunWidth;
    private int mContentHeight;
    private int mContentWidth;
    private float mStartX;
    private float mStartY;
    private Scroller mScroller;
    private int mScarollX;

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

    public SlideMeun2(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }

    //测量获取子控件的宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMeunView = getChildAt(0);
        mContentView = getChildAt(1);
        measureChild(mMeunView, widthMeasureSpec, heightMeasureSpec);
        measureChild(mContentView, widthMeasureSpec, heightMeasureSpec);
        mMeunHeight = mMeunView.getMeasuredHeight();
        mMeunWidth = mMeunView.getMeasuredWidth();
        mContentHeight = mContentView.getMeasuredHeight();
        mContentWidth = mContentView.getMeasuredWidth();
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        //布局-->左上右下
        mMeunView.layout(-mMeunWidth, 0, 0, mMeunHeight);
        mContentView.layout(0, 0, mContentWidth, mContentHeight);
    }

    int mInterceptDx = 0;

    //触摸事件的拦截
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int eventAction = ev.getAction();
        switch (eventAction) {
            case MotionEvent.ACTION_DOWN:
                mStartX = ev.getX();
                mStartY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float newX = ev.getX();
                float newY = ev.getY();
                //偏移量
                float dx = newX - mStartX;
                mInterceptDx += dx;
                mStartX = newX;
                mStartY = newY;
                if (Math.abs(mInterceptDx) > 2) {
                    //认为是水平的滑动,开始拦截 返回true就代表拦截
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            //按下
            case MotionEvent.ACTION_DOWN:
                mStartX = event.getX();
                mStartY = event.getY();
                break;
            //移动
            case MotionEvent.ACTION_MOVE:
                float newX = event.getX();
                float newY = event.getY();

                float dx = newX - mStartX;
                //利用scroll来实现其滚动,其参数表示的是偏移量
                //底层是调用scrollTo()
                scrollBy((int) -dx, 0);
                //获取到当前滚动位置的x坐标
                mScarollX = getScrollX();
                if (mScarollX < -mMeunWidth) {
                    //直接回滚到某个地方,其参数表示的是具体的坐标点
                    scrollTo(-mMeunWidth, 0);
                }
                if (mScarollX > 0) {
                    scrollTo(0, 0);
                }
                mStartX = newX;
                mStartY = newY;
                break;
            //抬起
            case MotionEvent.ACTION_UP:
                //同样是获取当前的X点坐标
                mScarollX = getScrollX();
                /* 以提供的起始点和将要滑动的距离开始滚动。滚动会使用缺省值250ms作为持续时间。
                   startX 水平方向滚动的偏移值,以像素为单位。正值表明滚动将向左滚动
                   startY 垂直方向滚动的偏移值,以像素为单位。正值表明滚动将向上滚动
                   dx 水平方向滑动的距离,正值会使滚动向左滚动
                   dy 垂直方向滑动的距离,正值会使滚动向上滚动*/
                if (mScarollX <= -mMeunWidth / 2) {
                    //完全展示
                    mScroller.startScroll(mScarollX, 0, -mMeunWidth - mScarollX, 0);
                } else {
                    //收缩
                    mScroller.startScroll(mScarollX, 0, 0 - mScarollX, 0);
                }
                invalidate();
                break;
        }
        return true;
    }

    //必须覆写此方法,才能实现其滚动(才能让ScrollBAy()和scrollTo()生效)
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            //获取当前滚动的x点位置
            int currX = mScroller.getCurrX();
            //移动到当前的位置
            scrollTo(currX, 0);
            //重新绘制
            invalidate();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值