View体系(一):View位置参数
在 Android 整个体系中,View 在其中扮演着不可或缺的角色。
前言
我对 Android 中的 View 理解是:View 在字面上面理解指的是视图,或者更加确切来说,它指的是控件,只不过这个控件的功能比较更广泛,没有像 Button
控件那样细化罢了。我为什么这么理解呢?因为在 View 体系中,例如 Button
、TextView
…它们都是继承自 View
,如 Android 5.0 源码中的:
public class Button extends TextView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
在 Android 中,除了 View,还有 ViewGroup,ViewGroup 在字面上面的理解是:它指的是控件组。基于上面的 View 的解析,ViewGroup 便很好理解:ViewGroup 是多个控件的集合。也就是说,ViewGroup 它可以包含多个 View 控件,也是就可以解析下面的控件为什么可以包含多个子 View 了:
public class LinearLayout extends ViewGroup
public class RelativeLayout extends ViewGroup
public class FrameLayout extends ViewGroup
当然,ViewGroup 也是继承自 View:
public abstract class ViewGroup extends View implements ViewParent, ViewManager
1.0 View 的位置参数
在准备深入理解 View 体系的时候,需了理解 View 的一些细节,如 x
、y
、translationX
、translationY
等。在理解 View 的位置参数的时候,我们需要先弄清楚 View 中的 left
、right
、top
、bottom
所代表的含义是什么:
/**
* 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;
则上面四个 left
、right
、top
、bottom
值通过上面的字面的理解,则可以通过作图表示为:
在 View 的源码中,存在着 left
、right
、top
、bottom
的获取接口,如下 Android 5.0 源码中:
...
/**
* 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 开发者应该熟知的。
弄清楚 left
、right
、top
、bottom
四个值的意义,那么 X
、Y
值的含义相对简单很多,如下源码:
/**
* 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()
又源码可知,
/**
* 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 的移动而相对变化的。
理解了 left
、right
、top
、bottom
这四个参数的由来,对于 View 的 Width
和 Height
也是比较容易理解的了:
width = mRight - mLeft;
height = mBottom - mTop;
实质上,对于 View 的 Width
和 Height
在上面的解析,在 Android 5.0 的源码中也是如此的。并且 View 还提供了获取 Width
和 Height
的接口: getWidth()
和 getHeight()
有些时候,在自定义 View 的过程中,往往会遇到 rawX
和 rawY
这两个参数,如下面 kotlin 源码:
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而言的。(值得注意的是,
left、
right、
top、
bottom这四个值是相对于该 View 的父 View 的,而
(X,Y)` 是相对与自身的)
2.0 小总结
这一篇文章主要介绍了 View 的几个重要的位置参数。