Android实现View滑动的几种方式

软件编程 Android 分类:[default] 更新日期: 2015-07-21
Android中的View类是所有UI控件的基类Base class,也就是说我们平时所有到的各种UI控件,比如Button、ImagView等等都继承自View类。这篇文章主要为大家详细介绍了Android实现View滑动的几种方式,需要的朋友可以参考下

什么是View?实现View滑动的方式有哪些?
1. 关于View我们需要知道的

(1)什么是View?

    Android中的View类是所有UI控件的基类(Base class),也就是说我们平时所有到的各种UI控件,比如Button、ImagView等等都继承自View类。LinearLayout、FrameLayout等布局管理器的直接父类是ViewGroup,而ViewGroup也有View类派生。总的来说,View是对UI控件的抽象,它代表了屏幕上的一个矩形区域。通过继承View,并重写相应方法,我们就能够实现具有各种外观及行为的UI控件。Button等控件我们之所以能够直接拿来即用,是因为Google已经帮我们完成了继承View并重写方法的工作。

(2)View的位置

    View在屏幕上的位置由它的以下四个参数所决定:

top:View的左上角的纵坐标,对应着View类中的成员变量mTop,可由getTop方法获得;
left:View的左上角的横坐标,对应着View类中的成员变量mLeft,可由getLeft方法获得;
bottom:View的右下角的纵坐标,对应着View类中的成员变量mBottom,可由getBottom方法获得;
right:View的右下角的横坐标,对应着View类中的成员变量mRight,可由getRight方法获得。
    注意,以上的坐标都是相对于父View来说的,也就是说,坐标都是相对坐标,因为子View的布局是由父View来完成的。如下图所示:

 Android实现View滑动的几种方式

     有了这四个参数,计算View的宽高就很容易了:width = right - left;height = bottom - top。关于View还有两个参数需要我们注意:translationX代表View平移的水平距离,translationY代表View平移的竖直距离;x、y分别为View的左上角的横纵坐标。View若经过了平移,改变的是它的x、y(代表当前View的左上角位置),它的四个位置参数代表了View的原始位置信息,是始终不变的。View在平移的过程中始终满足如下关系:

    x = left + translationX; y = top + translationY。 

2. 实现View滑动的几种方式
    我们在使用View的过程中,经常需要实现View的滑动效果。比如ListView、跟随手指而移动的自定义View等等,前者的滑动效果是SDK为我们提供的,而对于我们自定义View的滑动效果就需要我们自己来实现。下面我们来详细介绍以下实现View滑动的几种方式。

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

    实现滑动的最朴素直接的方式就是使用View类自带的scrollTo/scrollBy方法了。scrollBy方法是滑动指定的位移量,而scrollTo方法是滑动到指定位置。这两个方法的源码如下:

/** 
 * 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; 
   onScrollChanged(mScrollX, mScrollY, oldX, oldY); 
   if (!awakenScrollBars()) { 
    invalidate(); 
   } 
  } 
} 

/** 
 * 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); 
}

    通过以上代码的33~35行我们可以看到,scrollBy方法内部也是调用了scrollTo方法来实现。以上源码中我们注意到了mScrollX和mScrollY成员变量,前者是View的左边缘减去View的内容的左边缘,后者是View的上边缘减去View的内容的上边缘。示意图如下:

Android实现View滑动的几种方式

    上图中,黑色边框代表View在屏幕上对应的矩形区域,蓝色边框代表View的内容。在上图中,我们调用scrollTo/scrollBy把View向右滚动了一定距离。实际上,调用scrollBy/scrollTo方法只能实现View的内容的滚动,而View的四个位置参数是保持不变的。想一下我们平常使用ListView时,滚动的就是ListView的内容,而ListView本身在屏幕上的位置是不变的。上图中,黑色左边(即View的左边缘)减去蓝色左边(即View的内容的左边缘)即可得到mScrollX。由此我们还可以知道,向右滚动时mScrollX负的,向左滚动时mScrollX是正的。同理我们可以知道,向下滚动时,mScrollY是负的,向上滚动时,mScrollY是正的。

    经过以上的分析,我们了解到使用scrollTo/scrollBy方法实现View的滑动是很简单直接的,那么简单的背后有什么代价呢?代价就是滑动不是“弹性的”,弹性滑动指的是View的滑动应该是一个先加速再逐渐减速到停止的过程,这样看起来很平滑,不会很突兀。scrollTo/scrollBy方法实现的滑动看起来就会很突兀,这样的用户体验很不好。在解决这个问题之前,我们先来看看实现View滑动的其他方法。

 (2)使用动画来实现View的滑动

    使用动画来实现View的滑动主要通过改变View的translationX和translationY参数来实现,使用动画的好处在于滑动效果是平滑的。上面我们提到过,View的x、y参数决定View的当前位置,通过改变translationX和translationY,我们就可以改变View的当前位置。我们可以使用属性动画或者补间动画来实现View的平移。

    首先,我们先来看一下如何使用补间动画来实现View的平移。补间动画资源定义如下(anim.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:andro
 android:fillAfter="true">

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

</set>

    然后在onCreat方法中调用startAnimation方法即可。使用补间动画实现View的滑动有一个缺陷,那就是移动的知识View的“影像”,这意味着其实View并未真正的移动,只是我们看起来它移动了而已。拿Button来举例,假若我们通过补间动画移动了一个Button,我们会发现,在Button的原来位置点击屏幕会出发点击事件,而在移动后的Button上点击不会触发点击事件。

    接下来,我们看看如何用属性动画来实现View的平移。使用属性动画实现View的平移更加简单,只需要以下一条语句:

ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start(); 

    以上代码即实现了使用属性动画把targetView在100ms内向右平移100px。使用属性动画的限制在于真正的属性动画只可以在Android 3.0+使用(一些第三方库实现的兼容低版本的属性动画不是真正的属性动画),优点就是它可以真正的移动View而不是仅仅移动View的影像。

   经过以上的描述,使用属性动画实现View的滑动看起来是个不错的选择,而且一些View的复杂的滑动效果只有通过动画才能比较方便的实现。 

(3)通过改变布局参数来实现View的滑动

    通过改变布局参数来实现View的滑动的思想很简单:比如向右移动一个View,只需要把它的marginLeft参数增大,向其它方向移动同理,只需改变相应的margin参数。还有一种比较拐弯抹角的方法是在要移动的View的旁边预先放一个View(初始宽高设为0)。然后比如我们要向右移动View,只需把预先放置的那个View的宽度增大,这样就把View“挤”到右边了。代码示例如下:

MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
params.leftMargin += 100;
mButton.requestLayout();

    以上代码即实现了把mButton向右滑动100px。通过改变布局参数来实现的滑动效果也不是平滑的。 

(4)使用Scroller来实现弹性滑动

    上面我们提到了使用scrollTo/scrollBy方法实现View的滑动效果不是平滑的,好消息是我们可以使用Scroller方法来辅助实现View的弹性滑动。使用Scroller实现弹性滑动的惯用代码如下:

Scroller scroller = new Scroller(mContext);

private void smoothScrollTo(int dstX, int dstY) {
 int scrollX = getScrollX();
 int delta = dstX - scrollX;
 scroller.startScroll(scrollX, 0, delta, 0, 1000);
 invalidate();
}

@Override
public void computeScroll() {
 if (scroller.computeScrollOffset()) {
  scrollTo(scroller.getCurrX(), scroller.getCurY());
  postInvalidate();
 }
}

 

    我们来看一下以上的代码。第4行中,我们获取到View的mScrollX参数并存到scrollX变量中。然后在第5行计算要滑动的位移量。第6行调用了startScroll方法,我们来看看startScroll方法的源码:

public void startScroll(int startX, int startY, int dx, int dy, int duration) { 
 mMode = SCROLL_MODE; 
 mFinished = false; 
 mDuration = duration; 
 mStartTime = AnimationUtils.currentAnimationTimeMillis(); 
 mStartX = startX; 
 mStartY = startY; 
 mFinalX = startX + dx; 
 mFinalY = startY + dy; 
 mDeltaX = dx; 
 mDeltaY = dy; 
 mDurationReciprocal = 1.0f / (float) mDuration; 
 
 mViscousFluidScale = 8.0f; 
 
 mViscousFluidNormalize = 1.0f; 
 mViscousFluidNormalize = 1.0f / viscousFluid(1.0f); 
}
 

    从以上的源码我们可以看到,startScroll方法中并没有进行实际的滚动操作,而是把startX、startY、deltaX、deltaY等参数都保存了下来。那么究竟怎么实现View的滑动的呢?我们先回到Scroller惯用代码。我们看到第7行调用了invalidate方法,这个方法会请求重绘View,这会导致View的draw的方法被调用,draw的方法内部会调用computeScroll方法。我们来看看第13行,调用了scrollTo方法,并传入mScroller.getCurrX()和mScroller.getCurrY()方法作为参数。那么获取到的这两个参数是什么呢?这两个参数是在第12行调用的computeScrollOffset方法中设置的,我们来看看这个方法中设置这两个参数的相关代码:

public boolean computeScrollOffset() {
 ...
 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.rounc(y * mDeltaY);
    break;
  ...
  }
 }
 return true;
}

     以上代码中第8行和第9行设置的mCurrX和mCurrY即为以上scrollTo的两个参数,表示本次滑动的目标位置。computeScrollOffset方法返回true表示滑动过程还未结束,否则表示结束。

    通过以上的分析,我们大概了解了Scroller实现弹性滑动的原理:invaldate方法会导致View的draw方法被调用,而draw会调用computeScroll方法,因此重写了computeScroll方法,而computeScrollOffset方法会根据时间的流逝动态的计算出很小的一段时间应该滑动多少距离。也就是把一次滑动拆分成无数次小距离滑动从而实现“弹性滑动”。

本文提到的所有三种滑动方式的完整demo

以上就是本文的全部内容,希望对大家学习Android软件编程有所帮助。


> 本站内容系网友提交或本网编辑转载,其目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请及时与本网联系,我们将在第一时间删除内容!

相关文章
  • Flex输出文件到本地的两种方法
    flex中输出文件到本地有两种方法分别是File和FielRefreence,下面的实例代码,大家可以看看在flex中输出文件到本地目前我用到两种方法,分别是File和FielRefreence 例子: var exportString:String = "这就是一个测试" 1.File输出 代码如下:var ff:File = File ...
  • 解读ASP.NET5&MVC6系列教程1:ASP.NET5简介
    这篇文章主要介绍ASP.NET 5简介以及对各个版本号进行解释,ASP.NET 5中新的变化,需要的朋友可以参考下.ASP.NET 5简介 ASP.NET 5是一个跨时代的改写,所有的功能和模块都进行了独立拆分,做到了彻底解耦.为了这些改写,微软也是蛮 拼的,几乎把.NET Framwrok全部改写了一遍,形成了一个.NET Core的东西. 在.NET C ...
  • HTML5实战与剖析之触摸事件(touchstart、touchmove和touchend)
    本文主要介绍HTML5实战与剖析之触摸事件,介绍的比较详细,需要的朋友可以参考下. HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主要是触摸事件:touchstart.touchmove和touchend. ...
  • Swift中内置的集合类型学习笔记
    Swift中内置的集合类型学习笔记
    Swift中自带数组.set.字典三大集合类型,这里将学习过程中的基础的Swift中内置的集合类型学习笔记进行整理,需要的朋友可以参考下一.引言 Swift中提供了3种集合类型,Array数据类型,Set集合类型,Dictionary字典类型.Array用于存放一组有序的数据,数据角标从0开始一次递增:Set用于存放一组无序的数据,数据不可以重复:Dicti ...
  • MongoDB的Master-Slave主从模式配置及主从复制要点解析
    主从复制是数据库运维中一种常见的备份方式,这里我们来看一下MongoDB的Master-Slave主从模式配置及主从复制要点解析,需要的朋友可以参考下主从配置mongodb的master-slave模式配置方式如下1.keyFile生成key_file openssl rand -base64 741 > mongo_key 将mongo_key 分别 ...
  • php项目开发中用到的快速排序算法分析
    这篇文章主要介绍了php项目开发中用到的快速排序算法,结合实例形式详细分析了php快速排序的原理与使用方法,需要的朋友可以参考下本文实例讲述了php项目开发中用到的快速排序算法.分享给大家供大家参考,具体如下: 实际上在,做web开发,比较少遇到使用一些算法之类的,毕竟不是做搜索引擎,也不是写底层(比如写个类似于mysql这样的数据库,里面需要自己实现排序算 ...
猜你喜欢