View体系(一): View位置参数

在 Android 整个体系中,View 在其中扮演着不可或缺的角色。

前言

我对 Android 中的 View 理解是:View 在字面上面理解指的是视图,或者更加确切来说,它指的是控件,只不过这个控件的功能比较更广泛,没有像 Button 控件那样细化罢了。我为什么这么理解呢?因为在 View 体系中,例如 ButtonTextView…它们都是继承自 View,如 Android 5.0 源码中的:

1
public class Button extends TextView
1
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

在 Android 中,除了 View,还有 ViewGroup,ViewGroup 在字面上面的理解是:它指的是控件组。基于上面的 View 的解析,ViewGroup 便很好理解:ViewGroup 是多个控件的集合。也就是说,ViewGroup 它可以包含多个 View 控件,也是就可以解析下面的控件为什么可以包含多个子 View 了:

1
public class LinearLayout extends ViewGroup
1
public class RelativeLayout extends ViewGroup
1
public class FrameLayout extends ViewGroup

当然,ViewGroup 也是继承自 View:

1
public abstract class ViewGroup extends View implements ViewParent, ViewManager

1.0 View 的位置参数

在准备深入理解 View 体系的时候,需了理解 View 的一些细节,如 xytranslationXtranslationY等。在理解 View 的位置参数的时候,我们需要先弄清楚 View 中的 leftrighttopbottom 所代表的含义是什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* The distance in pixels from the left edge of this view's parent
* to the left edge of this view.
* 翻译:mLeft 值的大小是指从 View 的父容器的左边缘到该 View 的左边缘的距离,该值的单位为像素。
*/
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* 翻译:mRight 值的大小是指从 View 的父容器的左边缘到该 View 的右边缘的距离,该值的单位为像素。
*/
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* 翻译:mTop 值的大小是指从 View 的父容器的上边缘到该 View 的上边缘的距离,该值的单位为像素。
*/
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* 翻译:mTop 值的大小是指从 View 的父容器的上边缘到该 View 的下边缘的距离,该值的单位为像素。
*/
protected int mBottom;

则上面四个 leftrighttopbottom 值通过上面的字面的理解,则可以通过作图表示为:

Android 坐标系

在 View 的源码中,存在着 leftrighttopbottom 的获取接口,如下 Android 5.0 源码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
/**
* Top position of this view relative to its parent.
*
* @return The top of this view, in pixels.
*/
public final int getTop() {
return mTop;
}

...

/**
* Bottom position of this view relative to its parent.
*
* @return The bottom of this view, in pixels.
*/
public final int getBottom() {
return mBottom;
}

...

/**
* Left position of this view relative to its parent.
*
* @return The left edge of this view, in pixels.
*/
public final int getLeft() {
return mLeft;
}

...

/**
* Right position of this view relative to its parent.
*
* @return The right edge of this view, in pixels.
*/
public final int getRight() {
return mRight;
}

...

在 Android 的窗口中,存在着一个直角坐标系。这个直角坐标系可以这么理解的,以手机的左上角为原点,左上角水平向右为 X 轴,从左往右为正方向;左上角垂直向下为 Y 轴,从上到下为正方向,如上图的坐标系所示。一般地,这个坐标系是贯穿于 View 体系的,我们 Android 开发者应该熟知的。

弄清楚 leftrighttopbottom 四个值的意义,那么 XY 值的含义相对简单很多,如下源码:

1
2
3
4
5
6
7
8
9
10
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
* {@link #getLeft() left} property.
*
* @return The visual x position of this view, in pixels.
*/
public float getX() {
return mLeft + getTranslationX();
}

方法 getX() 返回的是一个单精度浮点型的值,又上面的源码可知,X 的值是 mLeft + getTranslationX() 的和,mLeft 的值是该 View 的左边缘距父 View 的左边缘的大小,而 getTranslationX() 又源码可知,

1
2
3
4
5
6
7
8
9
10
/**
* The horizontal location of this view relative to its {@link #getLeft() left} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The horizontal position of this view relative to its left position, in pixels.
*/
public float getTranslationX() {
return mRenderNode.getTranslationX();
}

getTranslationX()的方法中,它是通过 mRenderNode.getTranslationX() 进一步的调用,返回对应的 View 的 translationX 值。在 View 中,translationX 值指的是该 View 据其自身的在 X 轴上的偏移量,默认值为 0px; 同理地,translationY 值指的是该 View 据其自身的在 Y 轴上的偏移量,默认值为 0px。通过做图更加清楚表示为:

现在理解了 translationX,而 translationY 也是同样的原理可得,于是有源码可知,mLeft + getTranslationX() 相加的值为 X ,而 Y 则是 mTop + getTranslationY(),于是 (X,Y) 便可以看作是 VIew 的左上角的原点,并且这个 (X,Y) 是随着 View 的移动而相对变化的。

理解了 leftrighttopbottom 这四个参数的由来,对于 View 的 WidthHeight 也是比较容易理解的了:

1
2
width = mRight - mLeft;
height = mBottom - mTop;

实质上,对于 View 的 WidthHeight 在上面的解析,在 Android 5.0 的源码中也是如此的。并且 View 还提供了获取 WidthHeight 的接口: getWidth()getHeight()

有些时候,在自定义 View 的过程中,往往会遇到 rawXrawY 这两个参数,如下面 kotlin 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class CustomSlidView: View {

private var mLastX = 0f
private var mLastY = 0f

constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)


override fun onTouchEvent(event: MotionEvent?): Boolean {

val rawX = event!!.rawX
val rawY = event!!.rawY

when(event!!.action){
MotionEvent.ACTION_MOVE -> {

val deltaX = rawX - mLastX
val deltaY = rawY - mLastY

translationX += deltaX
translationY += deltaY
}
}

mLastX = rawX
mLastY = rawY

return true
}
}

这个 CustomSlidView 实现的是一个可以自由拖动的 View,里面的 (rawX,rawY) 是指手指按下屏幕时的坐标点,这个坐标点是相对于 ``DecorView 而言的。(值得注意的是,leftrighttopbottom这四个值是相对于该 View 的父 View 的,而(X,Y)` 是相对与自身的)

2.0 小总结

这一篇文章主要介绍了 View 的几个重要的位置参数。

本文标题:View体系(一): View位置参数

文章作者:

发布时间:2019年01月31日 - 21:01

最后更新:2021年06月20日 - 19:06

原始链接:https://hndroid.github.io/2019/01/31/View%E4%BD%93%E7%B3%BB%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A-View%E4%BD%8D%E7%BD%AE%E5%8F%82%E6%95%B0/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。