事件拦截案例

package cn.com;


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


public class MyScrollView extends ViewGroup{


    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        initView();
    }
    
    public MyScrollView(Context context) {
        super(context);
        initView();
    }
    
    private MyScroller myScroller;
//    private Scroller myScroller;
    
    /**
     * 初始化
     */
    private void initView() {


        myScroller = new MyScroller(getContext());
//        myScroller = new Scroller(getContext());
        
            detector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
                
                @Override
                // 当一个手指抬起时,回调该方法
                public boolean onSingleTapUp(MotionEvent e) {
                    return false;
                }
                
                @Override
                // 当有手指按下时,回调该方法
                public void onShowPress(MotionEvent e) {
                }
                
                @Override
                /**
                 * 当手指在屏幕滑动时,回调该方法
                 * @param e1 是down事件
                 * @param e2 是最近的一个move事件
                 * @param distanceX 二个相临事件之间X方向的距离
                 * @param distanceY 二个相临事件之间Y方向的距离
                 */
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                        float distanceY) {
                    
//                    System.out.println("distanceX::"+distanceX);
                    /**
                     * 让当前的view发生一些偏移
                     * 参数一 是x方向偏移的距离
                     * 参数二 是y方向偏移的距离
                     */
                    scrollBy((int)distanceX,0);
                    
                    /**
                     * 将当前的view的 scroll Position 定位在某个点上
                     * 参数一 是X坐标点
                     * 参数二 是Y坐标点
                     */
//                    scrollTo(mScrollX + x, mScrollY + y);
                    return false;
                }
                
                @Override
                public void onLongPress(MotionEvent e) {
                    
                }
                
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                        float velocityY) {
                    return false;
                }
                
                @Override
                public boolean onDown(MotionEvent e) {
                    return false;
                }
            });
        
    }


    /**
     * 手势解析的工具类
     */
    private GestureDetector detector;
    
    @Override
    /**
     * 分发事件
     * 系统默认的事件分发的规则,一般情况无需修改此方法
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }
    
    /**
     * onInterceptTouchEvent 方法中,down事件的X坐标
     */
    private int onInterDownX;
    /**
     * onInterceptTouchEvent 方法中,down事件的Y坐标
     */
    private int onInterDownY;
    @Override
    /**
     * 中断事件的传递
     * 默认返回 false 意思是,不中断,
     * 返回 true 意思是,中断事件传递,由自己消费
     */
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean result = false;
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            onInterDownX = (int) event.getX();
            onInterDownY = (int) event.getY();
            
            // 解决testView 跳跃的BUG
            detector.onTouchEvent(event);
            downX = (int) event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            
            // 水平方向滑动的距离 = (move事件的X坐标 - down 事件的X坐标)的绝对值
            int disX = (int) Math.abs(event.getX()-onInterDownX);
            int disY = (int) Math.abs(event.getY()-onInterDownY);
            
            // 如果水平方向,大于竖直方向,则中断事件
            if(disX> disY && disX>15){ // disX>15 是为过虑手指的抖动
                result = true;
            }
            
            break;
        case MotionEvent.ACTION_UP:
            
            break;
        }
        return result;
    }
    
    
    /**
     * DOWN 事件时的X坐标
     */
    private int downX;
    
    /**
     * 显示在屏幕的子view的下标
     */
    private int currIndex;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        super.onTouchEvent(event)
        /**
         * 当点中listView水平滑动时,down事件,MyScrollView没有中断,按默认传递给了listView,前几个MOVE事件,由于不超
         * 过15个像素同样也传递给了listView,当移动的距离超过15个像素时,MyScrollView中断了事件,事件交由自己来处理,
         * 即,执行当前的onTouchEvent 方法,而此时,onTouchEvent方法收到的第一个事件,是MOVE事件
         */
        System.out.println("MyScrollView.onTouchEvent()"+event.getAction());
        
        detector.onTouchEvent(event);
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: // 0
            
            downX = (int) event.getX();
            
            break;
        case MotionEvent.ACTION_MOVE: // 2
            
            break;
        case MotionEvent.ACTION_UP: // 1
            
            // upX - downX > getWidth()/2  切换至上一页
            // 否则保持不变
            int upX = (int) event.getX();
            int tempIndex = currIndex;
            
            // downX - upX > getWidth()/2   切换至下一页
            if((downX - upX )>getWidth()/2){
                tempIndex++;
            }else if((upX - downX)>getWidth()/2){
                tempIndex--;
            }
            
            moveToDest(tempIndex);
            break;
        }
        return true;
    }
    


    /**
     * 显示指定下标 的页面
     * @param tempIndex
     */
    public  void moveToDest(int tempIndex) {
        if(tempIndex<0){
            tempIndex = 0;
        }
        if(tempIndex>getChildCount()-1){
            tempIndex = getChildCount()-1;
        }
        currIndex = tempIndex;
        
        // 触发页面改变的监听
        if(onPageChangedListener!=null){
            onPageChangedListener.onPagedSelect(currIndex);
        }
        
        // 将下标为currIndex 的子View显示在屏幕中
//        scrollTo(getWidth()*currIndex, 0);
        
        int distanceX = getWidth()*currIndex - getScrollX();// 距离 = 终点坐标 - 当前坐标
        myScroller.startScroll(getScrollX(), 0, distanceX, 0);
        
//        handler.sendEmptyMessage(FLUSH);
        
        // 刷新页面,会导致一系列的方法被调用,其中就会调用到computeScroll 方法
        invalidate();
    }


    @Override
    /**
     * 当刷新页面的时候,如果需要改变 mSCrollX和mScrollY的值,可以在此方法中,做出修改。
     */
    public void computeScroll() {
        super.computeScroll();
        
        // 计算当前的偏移量
        if(myScroller.computeScrollOffset()){
            int currX = myScroller.getCurrX();
//            System.out.println("currX::"+currX);
            
            //  让页面移动到指定的位置
            scrollTo(currX, 0);
            // 再次刷新,刷新页面,会导致一系列的方法被调用,其中就会调用到computeScroll 方法
            invalidate();
        }
    }
    
//    private final int FLUSH = 999;
//    
//    private Handler handler = new Handler(){
//        public void handleMessage(android.os.Message msg) {
//            switch (msg.what) {
//            case FLUSH:
//                    // 计算当前的偏移量
//                    if(myScroller.computeScrollOffset()){
//                        int currX = myScroller.getCurrX();
//                        System.out.println("currX::"+currX);
//                        
//                        //  让页面移动到指定的位置
//                        scrollTo(currX, 0);
//                        // 再次刷新
//                        handler.sendEmptyMessage(FLUSH);
//                    }
//                
//                
//                break;
//            }
//        };
//    };
    
    
    @Override
    /**
     * 当系统测量view的大小的时候,调用,我们在此方法中的任务就是,计算自身的大小,调用setMeasuredDimension 方法设置大小
     * 如果当前view是一个布局,那么,还应在此方法中,测试,所有的子view的大小。
     */
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        
        System.out.println("widthSize::"+widthSize);
        System.out.println("widthMeasureSpec::"+widthMeasureSpec);
        
        
        
        // 测量,testView 的大小
        //getChildAt(2).measure(widthMeasureSpec, heightMeasureSpec);
        
        // 为所有的子view测量大小
        for(int i=0;i<getChildCount();i++){
            View view = getChildAt(i);
            view.measure(widthMeasureSpec, heightMeasureSpec);
        }
        
//        getMeasuredWidth(); // 获得view的测量值的宽度
//        getMinimumHeight(); // 获得view的测量值的高度
        // 测量的值,在 onMeasure方法执行完以后,就有了测量大小
        
//        getWidth(); // view的真实的宽度
//        getHeight(); // view的真实的高度
        // 真实的大小,在 onLayout 方法执行完了以后,才有
        // 系统的布局,在指定view的大小时,会参考 view的测量值的大小,根据测量的值来设置真实的大小
    }
    
    
    @Override
    /**
     * 当父view为我们指定好位置以后,调用此方法 ,
     * 如果当前view也是一个viewGroup,那么,应在此方法 中,为当前view的子view指定位置
     *  changed 是说你的位置是否有发生改变
     *  l t r b  是当前view 在父view 坐标系中的位置
     */
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 为子view指定位置
//        View child0 = getChildAt(0);
//        // 为下标为0的子view,指定位置
//        child0.layout(50, 30, 150, 200);
//        
//        View child1 = getChildAt(1);
//        // 为下标为1的子view,指定位置
//        child1.layout(100, 230, 500, 600);
        
        for(int i=0;i<getChildCount();i++){
            View child = getChildAt(i);
            // 让第一个子view填充满整个屏幕,其他的子view,依次向右平移一个宽度
            child.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());
        }
    }
    
    
    private IOnPageChangedListener onPageChangedListener;
    
    public void setOnPageChangedListener(IOnPageChangedListener onPageChangedListener){
        this.onPageChangedListener = onPageChangedListener;
    }


    /**
     * 当页面选择发生改变时的回调接口
     * @author leo
     *
     */
    public interface IOnPageChangedListener{
        
        /**
         * 选择的页面发生改变时,回调此方法
         * @param currIndex 新的页面的下标
         */
        void onPagedSelect(int currIndex);
        
    }
}

package cn.com;

import android.content.Context;
import android.os.SystemClock;

/**
* 计算位移偏移量
*/
public class MyScroller {


    private int startX;
    private int startY;
    private int distanceX;
    private int distanceY;


    public MyScroller(Context ctx){
        
    }
    
    /**
     * 开始移动
     * @param startX 开始点的X坐标
     * @param startY 开始点的Y坐标
     * @param distanceX X方向移动的距离
     * @param distanceY Y方向移动的距离
     */
    public void startScroll(int startX,int startY,int distanceX,int distanceY){
        this.startX = startX;
        this.startY = startY;
        this.distanceX = distanceX;
        this.distanceY = distanceY;
        
        startTime = SystemClock.uptimeMillis();// 手机从开机到现在的时间,不包含深度休眠的时间
        
        isFinish = false;
    }
    
    private long startTime;
    
    private int totalTime = 200;
    
    private boolean isFinish;
    private int currX;
    private int currY;
    
    /**
     * 计算当前的偏移量,
     * @return
     * 返回true 是指还在运行
     */
    public boolean computeScrollOffset(){
        if(isFinish){
            return false;
        }
        
        // 获得动画运行的时间
        long passTime = SystemClock.uptimeMillis() - startTime;


        if(passTime<totalTime){ // 还在动动
            
            currX = (int) (startX + distanceX * passTime /totalTime);
            currY = (int) (startY + distanceY * passTime /totalTime);
            
            
        }else{
            // 运动已经结束了
            setCurrX(startX + distanceX);
            setCurrY(startY + distanceY);
            
            isFinish = true;
        }
        return true;
    }


    public int getCurrX() {
        return currX;
    }


    public void setCurrX(int currX) {
        this.currX = currX;
    }


    public int getCurrY() {
        return currY;
    }

    public void setCurrY(int currY) {
        this.currY = currY;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值