Android 3种坐标系、View在各坐标系下获取自身坐标的方法、View的滑动和scroll方法

注:理解View获取自身坐标或者说位置的各种方法重点在于考虑该方法获取到的坐标是相对于3种坐标系中的哪种。

Android 3种坐标系


Android 3种坐标系都是以右、下方向为直角坐标系x、y轴的正方向。此文介绍的都是二维直角坐标系,不考虑 z 轴(z 轴以屏幕正上方的方向为正方向)。

一般来说,Android中存在三类坐标:

屏幕坐标:以屏幕的左上角为原点,水平向右为x轴正方向,竖直向下为y轴正方向。
布局坐标:以view的左上角为原点,水平向右为x轴正方向,竖直向下为y轴正方向。之所以称之为布局坐标是因为view的左上角的位置是在view的layout过程中确定的。
视图坐标(绘制坐标):视图坐标是view的draw过程中绘制内容时参考的坐标。存在于一个抽象的画布上。
在理解视图坐标上,很重要的一点是:draw过程中,我们的画布是无限大的,画布上有一个直角坐标系,称之为视图坐标。你可以以视图坐标为基准在画布上随便画点啥。View的layout过程中的四个参数(l,t,r,b)确定了一个矩形框。矩形框在屏幕上且在屏幕上的位置一般是不会改变的,矩形框的左上角就是当前View的布局坐标。我们拿一个和这个矩形框一样大小的另一个矩形框放到画布上,其左上角与视图坐标重合,画布上的矩形框在画布上框住的内容就会绘制到屏幕上对应的矩形框。当调用scrollto或scollby方法时,画布不动,画布上的矩形框移动。这个过程中,视图坐标在画布上的位置始终不变,布局坐标在屏幕上的位置始终不变。

打个比方:我们有一张小的白纸,对应于手机屏幕,白纸上画了一个矩形,对应于手机上View layout过程确定的矩形框,其左上角对应于当前View的布局坐标。我们在地上放了一张超大的白纸,对应于上面的说的画布,超大白纸上画了一个直角坐标系,相当于视图坐标。在超大白纸上方放了一个超大的纸板(不透明),纸板上有个矩形的洞,矩形的左上角与超大白纸的坐标对齐。这个洞就对应于上面所说的画布上的矩形框。当调用scrollto或者scrollby方法时,移动纸板。透过矩形框看见的内容,画到小的白纸上的矩形框中,相当于显示在了屏幕上。这个过程中,小的白纸上的布局坐标没有变,大的白纸上的视图坐标也没有变。

每个View都有自己的画布和画布上的视图坐标。画布独立于屏幕,和屏幕没有直接关系。子View上显示的内容会覆盖父View上显示的内容。

注:视图坐标和布局坐标的概念引用自Android内核剖析,除了这本书之外,博主还未见到其他地方有类似的定义。虽然这个坐标系的分类没有被广泛的传播,但是不可否认的是,这个坐标体系很好,很清晰。这应该是scrollTo和scrollBy为什么是“反的”的最好理论解释了。
以上内容来源:Android View理论基础之坐标系

View在各坐标系下获取自身坐标的方法


  1. 方法获得的值以布局坐标为基准的:

    • View的getRight()、getTop()、getLeft()、getBottom()、getX()、getY()都是以该View的ParentView即父布局的左上角为坐标原点的;
    • View的getLocalVisibleRect()以自身在屏幕上的可视区域的左上角为坐标原点获取该可视区域的Rect;
    • View的getLocationInWindow()以该View所在的Window的左上角为坐标原点;
    • View的getScrollX()、getScrollY()以View的左上角为坐标原点;
    • MotionEvent的getX()、getY()以该触摸点所在的View的左上角为坐标原点的;
  2. 方法获得的值以屏幕坐标为基准的:

    • View的getGlobalVisibleRect()以屏幕坐标为基准获取可视区域的Rect;
    • View的getLocationOnScreen();
    • MotionEvent的getRawX()、getRawY();

View的滑动


View的滑动方法:
1. 通过改变View的(x, y),即getX()、getY()得到的值。getX() = getLeft()+getTranslationX(),getY() = getTop()+getTranslationY()。因此改变(x, y):
一是改变View的getLeft()等的值。方法:重写View的onTouchEvent方法并在其中利用layout(l, t, r, b)、offsetLeftAndRight(int offset)配合offsetTopAndBottom(int offset)、改变View的LayoutParams中的margin(间接改变l、t、r、b)这3种方法来达到滑动View的目的。
二是改变getTranslationX()、getTranslationY()的值。方法:动画。
2. 通过移动View的父容器ParentView的内容。方法:重写ParentView的onTouchEvent方法并在其中利用scroll方法来滑动整个ParentView的内容,从而达到滑动View的目的。
View滑动的具体实现参考:实现View滑动的七种方法
这里主要讲View的各种scroll方法。

View的scroll方法


滑动方法的调用者通常是ViewGroup如ScrollView。如:scrollView.setScrollX(100)。
滑动方法滑动的是View的内容而不是View本身。

  • View的getScrollX()、getScrollY()获取的是View内容的当前滑动位置偏移量,具体就是getScrollX()获得的是View左边缘和View内容左边缘在水平方向上的距离,getScrollY()获得的则是View上边缘和View内容上边缘在垂直方向上的距离。

  • View的scrollTo()方法、scrollBy()方法。
    scrollTo(int x, int y)中的x、y是以该View的左上角为坐标原点的,该方法的作用是将该View的左上角滑动到(x,y)的位置上。(注:这里的View通常是ViewGroup如ScrollView,而(x,y)是在ViewGroup的整个内容尚未发生任何滑动前ViewGroup中某个View以该ViewGroup的左上角为坐标原点下的坐标。**该ViewGroup调用scrollTo(x,y)后,再调用该ViewGroup的getScrollX()、getScrollY()结果分别等于上面的x、y。**x、y为负数时表示内容沿x、y轴正方向移动,为正数时则相反。)
    scrollBy(int dx, int dy)中的dx、dy是指在x、y方向上分别上滑动dx、dy的距离。

  • View的setScrollX(int value)、setScrollY(int value) 分别相当于scrollTo(value, 0)、scrollTo(0, value)。

例子:ScrollView sv 中含有唯一的一个LinearLayout,而LinearLayout中含有TextView tv,将 tv 移动到ScrollView 的顶部,操作如下
int top = tv.getTop();
sv.scrollTo(0, top);
注:此例子中 top 是从 tv 到 LinearLayout顶部的距离,由于ScrollView 只能有一个子布局,因此 top 也是 tv 到 ScrollView顶部的距离,若LinearLayout设有MarginTop 则还要加上MarginTop 的值。

ScrollView 通过getHeight()得到的是其在屏幕上矩形区域的高度。
ScrollView 的内容的高度不仅仅只是屏幕上看到的矩形区域的高度,其内容的高度的获取可以通过如下方法:
int height = mScrollView.getChildAt(0).getBottom();
若ScrollView 唯一的子布局设有 MarginBottom 则还要加上该值。
例子可参考:android之ScrollView滑动到指定的View

View的scroll方法可参考:Android坐标

若是上面View获取坐标的方法不明白可参考:Android应用坐标系统全面详解
结合:Android手机界面组成

展开阅读全文

没有更多推荐了,返回首页