Android的坐标系统其实是一个比较细也比较杂的东西,想一个小的对比文章,以后忘了可以看看。
首先是MotionEvent的getRawX和getX两个函数。这个还是比较简单的,getRawX取的是相对于屏幕的坐标,屏幕的原点(0,0)在左上角,X轴正方向是向右,Y轴正方向向下。getX是相对于父组件的坐标。
其次是几个View的函数(以Y轴为例)。
getY,getTranslationY,getScrollY,getTop,getHeight,还有一个就是getLocationInWindow。
首先说的是getY和getTranslationY,这两个函数有相同的地方,下面看跟踪进去看下View的getY的函数源码。
public void setY(float y) {
setTranslationY(y - mTop);
}
可以看到getY实际就是调用了getTranslationY,但是不同的是参数是y-mTop,mTop后面再说,看了源码大家应该有点了解了,其实两个函数是差不多的,只是有一个点小不同,不过这点小不同所带来的影响还是挺大的,大家在选用函数的时候要慎重,其实一般做View的移动,用getTranslationY比较多。
getScrollY是一个比较特别的函数,因为它涉及一个值叫mScrollY,简单说,getScrollY一般得到的都是0,除非你调用过scrollTo或scrollBy这两个函数。有几点要注意的是,不论是scrollTo或scrollBy,其实都是对View的内容进行滚动而不是对View本身,你可以做个小实验,一个LinearLayouy背景是黄色,里面放置一个子LinearLayout背景是蓝色,调用scrollTo或scrollBy,移动的永远是蓝色的子LinearLayout。还有就是scrollTo和scrollBy函数的参数和坐标系是“相反的”,比如scrollTo(-100,0),View的内容是向X轴正方向移动的,这个相反打引号是因为并不是真正的相反,具体可以看源码,关于这两个函数的源码分析大家可以看http://www.tuicool.com/articles/uM7ruy,一目了然。
关于getScrollY,我还想多说一句就是,scrollView的滚动,内部就是用scrollTo和scrollBy来进行的,所以可以用getScrollY来获取滚动的Y值,而listView并不是,listView源码的分析大家可以参考郭大侠最新的博客,分析的很透彻,所以使用getScrollY来获取listView滚动的Y值是行不通的,一直都是0,下面我提供一种计算listView滚动值的获取方法给大家参考一下。
public int getScrollY() {
View c = mListView.getChildAt(0);
if (c == null) {
return 0;
}
int firstVisiblePosition = mListView.getFirstVisiblePosition();
int top = c.getTop();
int headerHeight = 0;
if (firstVisiblePosition >= 1) {
headerHeight = mPlaceHolderView.getHeight();
}
return -top + firstVisiblePosition * c.getHeight() + headerHeight;
}
这里的mPlaceHolderView是listView的header,所以要单独考虑。这种方式适用于单一布局的listView。
getTop这个函数也比较特殊,(我讲起来比较虚因为我对它了解的还不是很深。。所以下面关于这个函数的看法,都只是我的一家之言,如果有大神了解的更多更好,希望您能指出我说的错误的地方,谢谢!) 它特殊就特殊在它“很难改变”,为什么这么说呢,因为你不管是调用setY,setTranslationY,scrollTo/scrollBy,getTop都不会改变,那getTop获取的是什么呢
public final int getTop() {
return mTop;
}
就是mTop,这个mTop之前也出现过,其实它代表的就是在View的layout过程中确定的top值,当你看setTop函数的源码,
public final void setTop(int top) {
if (top != mTop) {
final boolean matrixIsIdentity = hasIdentityMatrix();
if (matrixIsIdentity) {
if (mAttachInfo != null) {
int minTop;
int yLoc;
if (top < mTop) {
minTop = top;
yLoc = top - mTop;
} else {
minTop = mTop;
yLoc = 0;
}
invalidate(0, yLoc, mRight - mLeft, mBottom - minTop);
}
} else {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
}
int width = mRight - mLeft;
int oldHeight = mBottom - mTop;
mTop = top;
mRenderNode.setTop(mTop);
sizeChange(width, mBottom - mTop, width, oldHeight);
if (!matrixIsIdentity) {
mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation
invalidate(true);
}
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
invalidateParentIfNeeded();
}
}
}
你会发现它会调用sizeChange这个函数,而不论是setY,setTranslationY还是scrollTo/scrollBy,都是不会调用的,所以我认为,这只是我认为啊,只有setTop/getTop这两个函数是关于一个View的“筋骨”的,也就是layout,其他几个函数都知识draw层面的,我也进行过验证,比如一个LinearLayout一开始getTop是250,height为500,使用其他函数,它的height是不会改变的,而当使用setTop(150)之后,这个LinearLayout的height就编程了600!这也算证实了我的猜测吧。
getHeight,没什么好说的,就是获取View的高度。
最后一个是getLocationInWindow,这个函数呢是这样用的,首先初始化一个数组:
private int[] location = new int[2];
然后
view.getLocationInWindow(childLocation);
之后location[0]就代表这个View相对于屏幕的X坐标,loacation[1]就代表这个View相对于屏幕的Y坐标了。