美文网首页
View的事件体系

View的事件体系

作者: changchengfeng | 来源:发表于2016-12-22 13:15 被阅读31次

    主要记录学习《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的事件体系

          本文链接:https://www.haomeiwen.com/subject/mvmcvttx.html