最近在学习自定义View,总是被View的显示的位置搞的一头雾水。对于一个View的位置,我比较迷惑:
View是Android中所有控件的基类,不管是Button或者TextView,还是LinearLayout或者ViewGroup,都继承于View。所以说,View是用户界面最基本的控件,用于创建交互式UI组件。
ViewGroup的字面意思是一组的View,其是View的子类,它是保存其他视图(或者其他ViewGroup),并定义其布局属性的容器。
在Android设计中,ViewGroup也继承了View,意味着View本身不仅是一个控件,其还是多个控件组成的一组控件。通过这些关系也就形成了我们所熟悉的View树形结构。
在Android中,默认的View的形状都是矩形。也就是说,只要确定了View的左上顶点和右下顶点的坐标,我们就可以确定了View的位置。这两个顶点分别对应了View的四个属性:
值得注意的是,这里所说的坐标都是相对坐标,这些坐标都是相对于父容器来说的。在Android系统中,屏幕的左上角作为,整个坐标系的原点,x轴和y轴的正向分别为右和下,大部分的Android的系统都是按照这个坐标系来进行显示的。
上图中,涉及到了以下方法:
View获取View的自身宽高不用多说,从方法命名上就可以看出来。
对于View本身是放置在一个ViewGroup中,先不管ViewGroup的大小,我们只要关心View在ViewGroup的位置就好了。前面我们已经提到了View在ViewGroup的位置是由View的4个位置属性决定的,它们之间的关系是这样的
从图中,我们可以清楚地得到View的宽高和View自身坐标之间的关系:
width = right - left
= getRight() - getLeft()
heiht = bottom - top
= getBottom() - getTop()
在Android 3.0以后,对View又新添了4个属性:
x:view左上角的横坐标
y:view左上角的纵坐标
translationX:左上角相对于父容器的偏移量
translationY:左上角相对于父容器的偏移量
这几个参数坐标都是相对于父容器的相对坐标,并且translationX和translationY的默认值都是0,view也分别为它们提供了get()和set方法。它们之间的换算关系是:
x = lef + translationX;
y = top + translationY;
值得注意的是,View在平移的过程中,top和Left表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x、y、translationX和translationX这四个参数。
在Android 5.0,对View又添加了一个新属性z,此新属性表示视图的标高,其确定:
View的Z值有两个组成部分:
elevation:静态组件。
定义elevation属性:
1. XML中使用android:elevation属性
2. 代码中设置视图的View.setElevation()
translationZ:用于动画的动态组件。
Z = elevation + translationZ
在Material设计中,Z属性占了很大比重,由Z属性表示的视图的高程确定其阴影的视觉外观:具有较高Z值的视图投射较大,较柔和的阴影。 具有较高Z值的视图会遮挡具有较低Z值的视图; 但是,视图的Z值不会影响视图的大小。
对于MotionEvent这四个方法,也是获取View的坐标,其所对应的的参考点不一样,它们的用途也不一样,具体该如何使用呢?在紧随其后的Android自定义View之MotionEvent会了解其使用情况。
前面获取到的View的位置坐标都是相对坐标(相对于父容器),而在MotionEvent所提供的四个方法中,可以获取到是点击事件相对坐标。有时候需要获取View在整个屏幕的位置,这个时候,又该如何呢?在View中定义了这样的四个方法:
int[] position = new int[2];
textview.getLocationInWindow(position);
这个方法是将view的左上角坐标存入数组中.此坐标是相对当前activity而言.
可见的意思是:在隐藏了状态栏/标题栏的情况下,它们的高度以0计算.
此时是无视状态栏的有无的.
int[] position = new int[2];
textview.getLocationOnScreen(position);
这个方法跟上面的差不多,也是将view的左上角坐标存入数组中.但此坐标是相对整个屏幕而言.
y坐标为view左上角到屏幕顶部的距离.
Rect viewRect = new Rect();
textview.getGlobalVisibleRect(viewRect);
这个方法是构建一个Rect用来”套”这个view.此Rect的坐标是相对当前activity而言.
此时是无视状态栏的有无的.
Rect globeRect = new Rect();
button.getLocalVisibleRect(globeRect);
这个方法获得的Rect的top和left都是0,也就是说,仅仅能通过这个Rect得到View的宽度和高度.
以上方法在OnCreate方法中调用,都会返回0,这是因为View还未加载完毕.
建议在onWindowFocusChanged方法中进行获取,有些情况下onWindowFocusChanged不好用的时候(比如ActivityGroup),可以这样写:
mTextView.post(new Runnable() {
@Override
public void run() {
Rect viewRect = new Rect();
mTextView.getGlobalVisibleRect(viewRect);
mTreeScrollView.setRect(viewRect);
}
});
这样在View加载完毕之后会执行获取位置的方法.