View的事件体系

主要记录学习《Android开发艺术探索》

1.View的位置参数

left,top,right,bottom;
表示View的原始信息,view在移动过程不会发生变化。
x,y,translationX,translationY;
x,y表示View左上角顶点坐标,translationX,translationY表示View向x轴,y轴移动的距离。若View的位置未发生移动translationX,translationY都为0;
x=left+translationX;
y=top+translationY;
以上大小都是View相对于父控件的坐标。Android坐标系原点在左上角,x轴水平向右为正,y轴竖直向下为正。

2.MotionEvent和TouchSlop

2.1 MotionEvent

当手指接触屏幕后会产生一系列的事件。典型的有以下几种
1.ACTION_DOWN 手指刚接触屏幕
2.ACTION_MOVE 手指在屏幕上移动
3.ACTION_UP 手指离开屏幕
MotionEvent 可通过event.getX(); event.getY();获取相对父控件坐标。
event.getRawX(); event.getRawY();获取相对屏幕的绝对坐标。

2.2 TouchSlop

系统所能识别出被认为是滑动的最小距离,用于过滤一些微小的滑动
可通过如下方式获取到
ViewConfiguration.get(getContext()).getScaledTouchSlop();

3.VelocityTracker,GestureDetector和Scroller

3.1 VelocityTracker

手在屏幕的滑动速度

        VelocityTracker velocityTracker=VelocityTracker.obtain();

        velocityTracker.addMovement(event);

        //设置时间间隔为1000毫秒
        velocityTracker.computeCurrentVelocity(1000);
        //表示水平方向1000毫秒内�从左往右滑过的像素点(向右滑为正,左滑为负)
        int xVelocity= (int) velocityTracker.getXVelocity();
      //表示竖直方向1000毫秒内�从上往下滑过的像素点(向下滑为正,上滑为负)
        int yVelocity= (int) velocityTracker.getYVelocity();
        velocityTracker.clear();
        velocityTracker.recycle();
3.2 GestureDetector 和ScaleGestureDetector

手势检测和缩放手势检测

 GestureDetector gestureDetector=new GestureDetector(getContext(),new GestureDetector.OnGestureListener() {
          //手指触碰一瞬间触发
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }
          //手指触碰未松开或者拖动
            @Override
            public void onShowPress(MotionEvent e) {

            }
          //单击
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }
          //拖动
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }
          //长按
            @Override
            public void onLongPress(MotionEvent e) {

            }
          //快速滑动
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return false;
            }
        });
        gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
          //严格单击行为(不可能为双击中的某一次)
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                return false;
            }
          //双击行为
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                return false;
            }
          //双击行为,但是ACTION_DOWN  ACTION_MOVE ACTION_UP 都会触发
            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                return false;
            }
        });
        //解决长按后无法拖动的现象
        gestureDetector.setIsLongpressEnabled(false);
        boolean consume = gestureDetector.onTouchEvent(event);
        return consume;

         ScaleGestureDetector g=new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                return false;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                return false;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {

            }
        });
        boolean consume = g.onTouchEvent(event);
        return consume;

3.3 Scroller

可以实现弹性滑动,但和View的computeScroll搭配使用典型代码如下
通过调用View的smoothScrollTo方法即可实现弹性滑动

private Scroller mscoller;
mscoller=new Scroller(getContext());

 public void smoothScrollTo(int destX,int destY)
    {
        int scrollX=getScrollX();
        int scorllY=getScrollY();
        int deltax=destX-scrollX;
        int deltay=destY-scorllY;

        mscoller.startScroll(scrollX,scorllY,deltax,deltay,3000);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mscoller.computeScrollOffset())
        {
            scrollTo(mscoller.getCurrX(),mscoller.getCurrY());
            postInvalidate();
        }
    }

4.View的滑动

4.1使用scrollTo和scrollBy

通过改变View的mScrollX,mScrollY参数来改变View内容的位置
若View内容在初始位置的的左边 mScrollX为正,反正为负。
若View的内容在初始位置的上面 mScrollY为正,反正为负。
可通过getScrollX();getScrollY();来获取mScrollX,mScrollY。

 public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }

  public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

4.2使用动画

View动画只能改变View的影像无法改变View的位置参数,无法在新位置接收点击事件,有严重的缺陷。
属性动画可以改变View的位置参数translationX,translationY。

4.3改变布局参数
   ViewGroup.MarginLayoutParams params=(ViewGroup.MarginLayoutParams)view.getLayoutParams();
        params.width+=100;
        params.leftMargin+=100;
        view.requestLayout();
        //或者
        view.setLayoutParams(params);

5.弹性滑动

5.1Scoller

工作原理:通过startScroll()把要滑动的位置信息和时间等传入Scoller,在调用 invalidate()方法进行重绘,View的重绘过程会调用自身的computeScroll()方法,
这个方法在View中是空实现,我们要重写这个方法,若时间还没有到(computeScrollOffset()),就调用View的scollTo方法,通过scoller的getCurrX(),getCurrY()获取到按时间流逝百分比计算出来的mCurrX,mCurrY,然后在调用postInvalidate()方法继续重绘。

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }

   public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
                
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);
                
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // Pin to mMinY <= mCurrY <= mMaxY
                mCurrY = Math.min(mCurrY, mMaxY);
                mCurrY = Math.max(mCurrY, mMinY);

                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }
5.2通过动画

动画本身就是一种渐进的过程,它天然就有弹性效果.

ObjectAnimator.ofFloat(view,"translationX",0,100).setDuration(1000).start();

可用动画特性来实现动画无法实现一些特殊效果

        final int startX=0;
        final int deltaX=100;
        final int startY=0;
        final int deltaY=100;
        ValueAnimator animator=ValueAnimator.ofInt(0,1).setDuration(1000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction=animation.getAnimatedFraction();
                view.scrollTo(startX+(int)(fraction * deltaX),startY+(int)(fraction * deltaY));
            }
        });
        animator.start();
5.3使用延时策略

你可能感兴趣的:(View的事件体系)