View的滑动

掌握滑动的方法是实现绚丽的自定义控件的基础。实现View的三种滑动方式:
1.通过View自身提供的scrollTo/scrollBy方法。
2.通过动画给View施加水平移动效果。
3.通过改变View的LayouParams使得View重新布局从而实现滑动。


使用scrollTo/scrollBy

Set the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.
Parameters:x the x position to scroll toy the y position to scroll to



    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();

            }

        }

    }


Move the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.

Parameters:x the amount of pixels to scroll by horizontallyy the amount of pixels to scroll by vertically

    public void scrollBy(int x, int y) {

        scrollTo(mScrollX + x, mScrollY + y);

    }

可以看出,scrollBy实际上也是调用了scrollTo方法来实现。不同的是,scrollBy实现了基于当前位置的相对滑动,而scrollTo则实现了基于所传递参数的绝对滑动。这样说两者的区别显得非常的抽象,那到底它们的区别是什么呢?在这之前,先了解一下View内部的两个属性:mScrollX和mScrollY

The offset, in pixels, by which the content of this view is scrolled horizontally.


     @ViewDebug.ExportedProperty(category = "scrolling")

     protected int mScrollX;


The offset, in pixels, by which the content of this view is scrolled vertically.


     @ViewDebug.ExportedProperty(category = "scrolling")

     protected int mScrollY;

mScrollX和mScrollY分别表示的是View内容在X轴和Y轴方向的偏移量,单位是像素。注意这里都是指的View内容而不是View本身的移动。那这个偏移量怎么计算呢?假设有一个View的起始位置是(0,0),这里的(0,0)指的是View的左上角的坐标,现在需要向右偏移100个像素即需要使View的坐标为(100,0),那么偏移量就是(0,0) - (100,0) = -100。这里也可以看出:View从坐往右滑动,mScrollX为负值,反之为正值。同理,View从上往下滑动,mScrollY为负值,反之为正值。

绝对滑动与相对滑动:可以看到,源码scrollTo方法中的参数x和y在代码中直接赋值给了mScrollX和mScrolly,偏移量的值就是以传入的为准。看如下代码:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mButton.scrollTo(-100,-100);
        mButton.scrollTo(-30,-30);
        Log.i(TAG,"偏移量: " + mButton.getScrollX());
    }
});

mButton执行了两次scrollTo方法,那么获取的最后的偏移量是多少呢?没错,就是-30。也就是说,scrollTo方法的参照点始终都是(0,0),而不管之前是否已经滑动过,并且它的滑动结果也是和最后一次调用scrollTo的一致。

再看如下代码:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mButton.scrollTo(-100,-100);
        mButton.scrollBy(-30,-30);
        Log.i(TAG,"偏移量: " + mButton.getScrollX());
    }
});

mButton在执行了一次scrollTo方法后再去执行scrollBy方法,那么获取的偏移量是多少呢。没错就是-130。可以看到再scrollBy方法的源码中,传入的x和y是和之前的偏移量进行了加运算,也就是说,scrollBy的相对滑动参照点是前一次滑动后的点。

那么如果先调用scrollBy方法然后调用scrollTo方法的结果是如何呢?经试验证明,结果还是以最后一次的scrollTo的滑动为准。那么是否能有这样的一个结论:关于View的scrollBy和scrollTo的滑动,如果最后一次滑动动作是通过scrollTo完成的,而不管之前的滑动如何,都是以最后一次scrollTo的结果为最后的滑动结果的呢?

回顾上面分析,需要注意的一点就是这里所说的滑动都是指View的内容的滑动,而View本身是不会移动的。


使用动画

涉及View的滑动的话,其实就是动画中的平移。而动画又有View动画和属性动画,现在都是推荐使用属性动画,两者的区别简单的说就是View动画并不是在真正的移动View而是产生View的影像而已。属性动画需要在3.0以上才支持,不过现在的用户基本上都是3.0以上了,如果需要兼容3.0以下的就需要使用nineoldandroids开源动画库。

属性动画的使用:

// 100ms内向右平移100像素
ObjectAnimator.ofFloat(mButton,"translationX",0,100).setDuration(100).start();

改变布局参数

改变布局参数就是改变LayouParams,例如我们需要将一个Button像右平移100个像素,就只需要将其marginLeft的值增加100,或者先在Button的左边放置一个宽度为0的View,在Button需要平移的时候改变View的宽度即可。

改变marginLeft的方式:

ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mButton.getLayoutParams();
params.leftMargin += 100;
mButton.requestLayout(); // 或者mButton.setLayoutParams(params);

三种方式对比

1. scrollto/scrollBy:操作简单,适合对View内容的滑动;
2. 动画:操作简单,主要使用于没有交互的View和实现复杂的动画效果;
3. 改变布局参数:操作稍微复杂,适用于有交互的View。

接下来实现一个跟随着手滑动的View,我们只需要获取该View在屏幕中的位置,在滑动之后获取与之前坐标的差即是平移的距离。每次滑动之后改变View的位置。


public class MyView extends TextView {

    // 记录上次View的位置
    private int mLastX = 0;
    private int mLastY = 0;

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                // 获取两次滑动之间的位移
                int delaX = rawX - mLastX;
                int delaY = rawY - mLastY;

                int translationX =  (int)ViewHelper.getTranslationX(this) + delaX;
                int translationY = (int)ViewHelper.getTranslationY(this) + delaY;

                ViewHelper.setTranslationX(this,translationX);
                ViewHelper.setTranslationY(this,translationY);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        mLastX = rawX;
        mLastY = rawY;
        return true;
    }
}

你可能感兴趣的:(View相关,移动)