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文件定义方法。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <!--平移-->
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100"
        android:toYDelta="100" />
    <!--旋转-->
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="10%p"
        android:pivotY="10%p"
        android:toDegrees="360" />
    <!--透明渐变-->
    <alpha
        android:duration="3000"
        android:fromAlpha="0"
        android:toAlpha="1" />
    <!--缩放-->
    <scale
        android:duration="3000"
        android:fromXScale="0"
        android:fromYScale="0"
        android:pivotX="10%p"
        android:pivotY="10%p"
        android:toXScale="2"
        android:toYScale="2" />
</set>  

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

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

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开发艺术探索》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容