当前位置: 代码迷 >> Android >> Android中View的弹性滑动——Android开发艺术探索札记
  详细解决方案

Android中View的弹性滑动——Android开发艺术探索札记

热度:81   发布时间:2016-04-24 11:41:23.0
Android中View的弹性滑动——Android开发艺术探索笔记

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50732132

介绍

弹性滑动也就是渐进式滑动,实现弹性滑动的方法有很多,但是他们都有一个共同的思想:将一次大的滑动分成若干次小的滑动并在一段时间内完成。本文主要介绍三种弹性滑动方式,Scroller、动画和Handler。

本文中的“滑动”是指View内容的滑动而非View本身位置的改变。

示例

点击屏幕任意地方,手指与屏幕接触时,触发ACTION_DOWN屏幕中的文字会向上滑动400px,手指离开屏幕时触发ACTION_UP文字下滑400px。

示例

基础知识补充

  • View的宽高和坐标关系:width = right - left,height = top - bottom。
  • View在平移过程中,top和left表示的是原始左上角的位置信息,其值不会改变,发生改变的是x、y、translationX、translationY这四个参数。
  • x是View左上角的坐标,translation是view移动后相对于父容器的偏移量,所以有x = left + translationX。y的原理相同。
  • getX/getY返回的是相对于当前View左上角的x和y坐标,而getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。

使用Scroller

private void smoothScrollTo(int destX, int destY) {        int scrollX = getScrollX();        int detlaX = destX - scrollX;        int scrollY = getScrollY();        int detlaY = destY - scrollY;        Log.d(TAG, "smoothScrollTo:scrollY, detlaY= " + scrollY+" " + detlaY);        mScroller.startScroll(scrollX, scrollY, detlaX, detlaY, 1000);        invalidate();    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        }    }

上面是使用Scroller实现弹性滑动的一个典型方法。

实现原理为:Scroller本身无法让View弹性滑动,它需要和View的computeScroll方法配合使用才能共同完成这个功能。在startScroll()方法下调用了invalidate(),这使得View重绘,View重绘的时候会在draw方法中调用computeScroll(),在此方法中调用scrollTo滑向指定位置。之后再通过postInvalidate()进行二次重绘,如此重复直到滑动结束。

下面是computeScrollOffset()这个函数的源码。从中可以看出判断滑动是否结束是以timePassed为标准的。

/**     * Call this when you want to know the new location.  If it returns true,     * the animation is not yet finished.     */     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:            ...            ...                break;            }        }        else {            mCurrX = mFinalX;            mCurrY = mFinalY;            mFinished = true;        }        return true;    }

使用动画

    private static final int FRAME_COUNT = 30;    final int startX = 0;    final int deltaX = 100;    ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);    animator.addUpdateListener(new AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animator) {            float fraction = animator.getAnimatedFraction();            mButton1.scrollTo(startX + (int) (deltaX * fraction), 0);        }    });    animator.start();

上述代码中,动画本质上没有作用于任何对象上,只是在1000ms内完成了整个动画过程。这个方法scrollTo(startX + (int) (deltaX * fraction), 0)使得动画每次更新时view都滑动一点。

使用Handler

    private static final int MESSAGE_SCROLL_TO = 1;    private static final int DELAYED_TIME = 33; private Handler mHandler = new Handler() {        public void handleMessage(Message msg) {            switch (msg.what) {            case MESSAGE_SCROLL_TO: {                mCount++;                if (mCount <= FRAME_COUNT) {                    float fraction = mCount / (float) FRAME_COUNT;                    int scrollX = (int) (fraction * 100);                    mButton1.scrollTo(scrollX, 0);                    mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO, DELAYED_TIME);                }                break;            }            default:                break;            }        };    };

在Handler的handleMessage中调用View的scrollTo方法滑动一些距离,紧接着再向Handler发送一个Delay的消息再次滑动。关键方法mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO, DELAYED_TIME)。

示例动画效果的源码

示例中的动画效果是在一个TextView进行的,滑动的是TextView中的文字。源码如下。

/** * Created by Spark on 2/22/2016 21:12. */public class ElasticText extends TextView {    private static final String TAG = "ElasticText";    private Scroller mScroller;    private VelocityTracker mVelocityTracker;    public ElasticText(Context context) {        super(context);        init();    }    public ElasticText(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public ElasticText(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        mScroller = new Scroller(getContext());        mVelocityTracker = VelocityTracker.obtain();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN: {                Log.d(TAG, "onTouchEvent: ActionDown");                mScroller.startScroll(getScrollX(), getScrollY(), 0, 400, 600);                invalidate();                break;            }            case MotionEvent.ACTION_MOVE: {                break;            }            case MotionEvent.ACTION_UP: {                Log.d(TAG, "onTouchEvent: ACTION_UP");                mScroller.startScroll(getScrollX(), getScrollY(), 0, -400, 600);                invalidate();                break;            }            default:                break;        }        return true;    }    private void smoothScrollTo(int destX, int destY) {        int scrollX = getScrollX();        int detlaX = destX - scrollX;        int scrollY = getScrollY();        int detlaY = destY - scrollY;        mScroller.startScroll(scrollX, scrollY, detlaX, detlaY, 1000);        invalidate();    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        }    }}

欢迎转载,转载请注明出处http://blog.csdn.net/l664675249/article/details/50732132

  相关解决方案