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来完成的。如下图所示:

有了这四个参数,计算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的内容的上边缘。示意图如下:

上图中,黑色边框代表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:android="http://schemas.android.com/apk/res/android" 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软件编程有所帮助。

时间: 2024-09-13 14:04:12

Android实现View滑动的几种方式的相关文章

Android实现View滑动的几种方式_Android

什么是View?实现View滑动的方式有哪些?1. 关于View我们需要知道的 (1)什么是View?     Android中的View类是所有UI控件的基类(Base class),也就是说我们平时所有到的各种UI控件,比如Button.ImagView等等都继承自View类.LinearLayout.FrameLayout等布局管理器的直接父类是ViewGroup,而ViewGroup也有View类派生.总的来说,View是对UI控件的抽象,它代表了屏幕上的一个矩形区域.通过继承View,

Android实现View滑动的6种方式

本文实例为大家分享了Android实现View滑动的具体方法,供大家参考,具体内容如下 1.View的滑动简介 View的滑动是Android实现自定义控件的基础,同时在开发中我们也难免会遇到View的滑动的处理.其实不管是那种滑动的方式基本思想都是类似的:当触摸事件传到View时,系统记下触摸点的坐标,手指移动时系统记下移动后的触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标. 实现View滑动有很多种方法,这篇文章主要讲解六种滑动的方法,分别是:layout().offsetLeft

Android获取view高度的三种方式_Android

本文为大家分享了Android获取view高度的方法,供大家参考,具体内容如下 getMeasuredHeight()与getHeight的区别 实际上在当屏幕可以包裹内容的时候,他们的值相等, 只有当view超出屏幕后,才能看出他们的区别: getMeasuredHeight()是实际View的大小,与屏幕无关, 而getHeight的大小此时则是屏幕的大小. 当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的大小 具体方法 我们知道在oncr

Android获取view高度的三种方式

本文为大家分享了Android获取view高度的方法,供大家参考,具体内容如下 getMeasuredHeight()与getHeight的区别 实际上在当屏幕可以包裹内容的时候,他们的值相等, 只有当view超出屏幕后,才能看出他们的区别: getMeasuredHeight()是实际View的大小,与屏幕无关, 而getHeight的大小此时则是屏幕的大小. 当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的大小 具体方法 我们知道在oncr

Android中实现滑动的七种方式总结

在Android中想要实现实现滑动有很多方法,这篇博客将提供一些实现滑动的思路,希望可以帮助到有需要的人. 一.Android坐标体系 在讲解滑动之前,我们有必要简单提一下Android的坐标体系,因为滑动的实质就是坐标的不断改变,所以我们先来了解一下Android坐标系和视图坐标系两个概念.直接放上两张图片吧,一目了然. Android坐标系 视图坐标系 从上面的两张图可以看出,Android坐标系的坐标原点位于屏幕的左上角,而视图坐标系的原点位于父视图的左上角,既然提供了两种不同的坐标系,那

Android View移动的3种方式总结_Android

前言 在Android开发中,View一直是Android开发人员的一块心病,一方面想要进阶,一方面又害怕进阶,可以说Android的View是进阶路上的最大绊脚石,因为它涉及的东西太多了,比如本次我们此次要写的View移动,另外还包括View的触摸事件的传递,创建自定义View,这些都是极其重要且不得不面对的难题.但是无论如何,现在不克服的困难将来就会被困难克服. 在此之前,我们还是先了解Android坐标系的定义规则以及View的一些位置参数. Android坐标系 View的位置及大小是由

Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)_Android

本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,notifyDataSetChanged()可以在修改适配器绑定的数组后,不用重新刷新Activity,通知Activity更新ListView.今天的例子就是通过Handler AsyncTask两种方式来动态更新ListView. 布局main.xml: <?xml version="1.0&qu

android中跨进程通讯的4种方式

  转自:http://blog.csdn.net/lyf_007217/article/details/8542359 帖子写的很好.看来一遍,试了一遍,感觉太有意义.必须转过来! android中跨进程通讯的4种方式 由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于android系统中4种应用程序组件:Activity.Content Provider

android绘制圆形图片的两种方式示例

android绘制圆形图片的两种方式 看下效果先 下面有完整的示例代码 使用BitmapShader(着色器) 我们在绘制view 的时候 就是小学上美术课 用水彩笔在本子上画画 使用着色器绘制圆形图片最简单的理解方式 就是把bitmap当做一种颜色 设置给paint ,paint都已经有颜色了 你想让它方了,圆了,扁了 还不是看你心情 canvas调用那个方法咯 实现的大致思路如下: 1. 创建一个类 继承imageView 重写onDraw() 2. 获取到bitmap图片 3. 计算图片的