0%

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

1. 使用 scrollTo()/scrollBy() 实现滑动

  • scrollTo()scrollBy() 方法实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 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(); 老版源码
    invalidate(true); // 通知 View 进行重绘
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 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);
    }
  • scrollTo() 方法实现了基于所传递参数的绝对滑动scrollBy() 方法实现了基于当前位置的相对滑动

    • 滑动过程中,View 内部的两个属性 mScrollXmScrollY 可以分别通过方法 getScrollX()getScrollY() 得到
    • 滑动过程中,mScrollX 的值总是等于 View 左边缘View 内容左边缘在水平方向的距离;mScrollY 的值总是等于 View 上边缘View 内容上边缘在竖直方向的距离
  • mScrollXmScrollY 的单位为像素

    • 当 View 左边缘在 View 内容左边缘的右边时,mScrollX 为正值,反之为负值。即,内容往左移动 mScrollX 则为正
    • 当 View 上边缘在 View 内容上边缘的下边时,mScrollY 为正值,反之为负值。即,内容往上移动 mScrollY 则为正
    • 方法 scrollTo() 参数与滑动方向相反的原因可以通过源码查到:invalidate(true) -> draw(canvas) -> onDrawScrollBars(canvas) -> invalidate(left, top, right, bottom) -> 倒数第 5 行:tmpr.set(l-scrollX, t-scrollY, r-scrollX, b-scrollY);
  • scrollTo()scrollBy() 只能改变 View 内容的位置而不能改变 View 在布局中的位置

  • mScrollXmScrollY 的变换规律示意图

`mScrollX` 和 `mScrollY` 的变换规律示意图

2. 使用动画实现滑动

  • 通过动画能够让一个 View 进行平移,平移就是一种滑动

    • 使用动画来移动 View,主要是操作 View 的 translationXtranslationY 属性
    • 既可以采用传统的 View 动画,也可以采用属性动画(为了兼容 3.0 以下版本,需要采用开源动画库 nineoldandroids
  • Demo:

    • View 动画:在 100 ms 内将一个 View 从原始位置向右下角移动 100 个像素

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <?xml version="1.0" encoding="utf-8"?>
      <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:fillAfter="true"
      android:zAdjustment="normal" >

      <translate
      android:duration="100"
      android:fromXDelta="0"
      android:fromYDelta="0"
      android:interpolator="@android:anim/linear_interpolator"
      android:toXDelta="100"
      android:toYDelta="100" />

      </set>
      • View 动画是对 View 的影像做操作,它并不能真正的改变 View 的位置参数,包括宽高
      • 如果希望动画后的状态得以保留必须将 fillAfter 属性设置为 true,否则动画完成后其动画结果会消失会瞬间恢复到动画前的状态
    • 属性动画:将一个 View 在 100 ms 内从原始位置向右平移 100 像素

      1
      ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
      • 属性动画不存在 View 动画的 fillAfter 问题
      • 尽管如此,在 Android 3.0 以下的手机上通过 nineoldandroids 来实现的属性动画本质上仍然是 View 动画

3. 改变布局参数实现滑动

  • View 的布局参数即 LayoutParams,改变 LayoutParams 实现 View 滑动是一种很灵活的方法

  • Demo: 重新设置一个 View 的 LayoutParams

    1
    2
    3
    4
    MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
    params.width += 100;
    params.leftMargin += 100;
    mButton.requestLayout(); // 或者 mButton.setLayoutParams(params);
  • 还可以在目标 View 的周围放置空 View,通过改变空 View 的大小使目标 View 滑动(不推荐)

4. 三种 View 滑动方式的对比

  • 简单总结

    • scrollTo()/scrollBy(): 操作简单,适合对 View 内容的滑动
    • 动画:操作简单,适用于没有交互的 View 和实现复杂的动画效果
    • 改变布局参数:操作稍微复杂但灵活,适用于有交互的 View
  • Demo: 实现自定义 View 跟随手指全屏滑动的效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getRawX();
    int y = (int) event.getRawY();
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    break;
    case MotionEvent.ACTION_MOVE:
    int deltaX = x - mLastX;
    int deltaY = y - mLastY;
    Log.d(TAG, "move, deltaX: " + deltaX + " deltaY: " + deltaY);
    int translationX = (int) ViewHelper.getTranslationX(this) + deltaX;
    int translationY = (int) ViewHelper.getTranslationY(this) + deltaY;
    ViewHelper.setTranslationX(this, translationX);
    ViewHelper.setTranslationY(this, translationY);
    break;
    case MotionEvent.ACTION_UP:
    break;
    default:
    break;
    }
    mLastX = x;
    mLastY = y;
    return true;
    }
-------------------- 本文结束感谢您的阅读 --------------------