Android View的事件体系(二)View的滑动

熟练掌握滑动的方法,可以帮助我们实现优秀的自定义控件,一般实现View的滑动有三种方式:

1. 通过View本身提供的scrollTo/scrollBy方法
2. 通过动画给View添加平移效果
3. 通过改变View的LayoutParams使得View重新布局

接下来我们逐个分析这三种方式是如何实现View的滑动效果。

一、使用scrollTo/scrollBy方法

为了实现View的滑动,系统专门为我们提供了scrollTo/scrollBy这两个方法来实现这个功能。首先我们来观察一下这两个方法的具体实现。

    /**
     * Set the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the x position to scroll to
     * @param y 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
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the amount of pixels to scroll by horizontally
     * @param y the amount of pixels to scroll by vertically
     */
    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

我们可以看到scrollBy方法实际上是对scrollTo方法的调用,它实现了基于当前位置的相对滑动;而scrollTo方法实现了对于方法参数的绝对滑动,所以我们只需要重点分析scrollTo方法即可。

首先我们要理解mScrollX、mScrollY两个变量的含义,我们先看一下源码注释给出的定义

    /**
     * The offset, in pixels, by which the content of this view is scrolled
     * horizontally.
     * {@hide}
     */
    @ViewDebug.ExportedProperty(category = "scrolling")
    protected int mScrollX;

    /**
     * The offset, in pixels, by which the content of this view is scrolled
     * vertically.
     * {@hide}
     */
    @ViewDebug.ExportedProperty(category = "scrolling")
    protected int mScrollY;

翻译成中文大致的意思就是

  • mScrollX——View的内容在水平方向滑动的偏移量,以像素为单位,它的值总是等于View左边缘到View内容左边缘的水平距离,并且当View左边缘在View内容左边缘的右边时它的值为正,否则为负。
  • mScrollY——View的内容在竖直方向滑动的偏移量,以像素为单位,它的值总是等于View上边缘到View内容上边缘的竖直距离,并且当View上边缘在View内容上边缘的下边时它的值为正,否则为负。

换句话说,View的内容从右向左滑时mScrollX为正值,从下往上滑时mScrollY为正。

需要注意的是,使用scrollTo/scrollBy方法实现View的滑动,只能将View的内容进行移动,我们并不能移动View本身。下面的图也许能够帮助我们更好地理解这些概念。

灰色阴影表示View的内容

二、使用动画

使用动画来移动View,主要操作的是View的translationX和translationY属性。我们既可以采用传统的View动画(补间动画),也可以使用属性动画。

1、补间动画

视图动画,也叫Tween(补间)动画。可以在一个视图容器内执行一系列简单变换(位置、大小、旋转、透明度)。补间动画可以通过XML文件或者代码定义,推荐使用XML文件定义,下面我们只介绍XML文件定义方法。



    
    
    
    
    
    
    
    
  

上述代码定义了四种补间动画,标签内即可以定义一个动画,也可以定义多个动画,动画效果叠加。

接下来我们详细分析动画的属性含义。

1-公共属性

这是所有补间动画都具有的共同特性,当然每种动画都是有特有的属性的。


Alpha动画属性详解

Rotate动画属性详解

Scale动画属性详解

Translate动画属性详解

需要注意的是,补间动画执行之后并未改变View的真实布局属性,这会给我们带来一个很严重的问题。

假设这样一个场景,我们对某个Button施加动画使其从A位置移动到B位置,并保持动画结束后的状态,此时我们看到的Button是停留在B位置上的。
这个时候我们点击Button,是没有任何点击事件发生的,因为对于系统来说,B位置的Button只是一个影像,真实的Button仍然停留在A位置,所以当我们点击Button原来所处的位置A,会产生点击事件,当然这肯定不是我们期待的结果。

2、属性动画

Android3.0开始引入属性动画,可以帮助我们解决上面的问题,但是对于更低的系统版本来说,问题还是没有得到解决。我们可以使用其他的方法来解决这个问题,《Android开发艺术探索》作者给出了如下参考方案:

我们可以在新位置预先创建一个与目标View一样的替代View,当动画结束之后,我们把目标View隐藏,同时把替代View显示出来,这样就间接地解决了上面提到的问题。

关于动画的更多内容,以后会单独介绍,这里先不做太多延伸了。
我们继续介绍View的滑动方式。

三、改变布局参数

改变布局参数,就是改变LayoutParams。假如我们想把Button向右移动100px,我们只需将LayoutParams里的leftMargin参数增加100px即可。它的用法很简单:

hello = findViewById(R.id.hello);
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) hello.getLayoutParams();
layoutParams.leftMargin += 100;
layoutParams.topMargin += 100;
hello.setLayoutParams(layoutParams);

四、各种滑动方式的对比

  • scrollBy/scrollTo
    View的原生方法,专门作用于View的滑动,使用简单并且不影响View内部元素的单击事件。
    缺点:只能滑动View的内容,不能滑动View本身。
  • 使用动画
    Android3.0以上使用属性动画比较理想,更低的系统下使用补间动画或者属性动画都不能改变View本身的属性。
    在实际使用中,如果动画不需要响应用户的交互,那么使用动画实现滑动效果是比较合适的,否则不合适。
    动画有一个明显的优点就是:一些复杂的滑动效果必须通过动画才能实现。
  • 改变布局参数
    主要适用于一些具有交互性的View,操作稍微复杂一点。

参考

Android 三种动画详解
《Android开发艺术探索》

你可能感兴趣的:(Android View的事件体系(二)View的滑动)