1 View的坐标体系
首先要知道,在Android中,坐标体系如下:
在android的坐标系中,原点在屏幕左上角,向右x为正,向下y为正。
该坐标原点位于屏幕最左上角的点。一般在AndroidAPP中,我们的布局如下:
这里的原点就位于状态栏的最左上角,因此我们在处理View的坐标时,应该注意这点。
而对于View来说,坐标体系如下:
view提供的方法
getTop:获取到的,是view自身的顶边到其父布局顶边的距离
getLeft:获取到的,是view自身的左边到其父布局左边的距离
getRight:获取到的,是view自身的右边到其父布局左边的距离
getBottom:获取到的,是view自身的底边到其父布局顶边的距离
在android3.0以后又增加x、y、translationX、translationY,都是相对于父容器的坐标。x指view左上角的横坐标,当view发生移动时,x会变化;
translationX指view左上角的横坐标相对于父容器的偏移量,当view发生移动时,translationX会变化。
同理,y与translationY也是如此。因此有以下关系式成立
x = left + translationX
y = top + translationY
一般来说,View在做translation动画时,改变的就是translationX或translationY。例如如下图所示:
2 MotionEvent的坐标体系
对于MotionEvent的坐标体系就比较简单,还是用上面的图来表示
MotionEvent提供四个坐标:x,y,rawX,rawY。其中x,y是该view相对于父容器,rawX,rawY是相对于屏幕原点。
MotionEvent提供的方法
getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离
3 获取View坐标的时机
对于View:
对于View,ViewGroup来说,width、height、top、left等属性值是在Measure与Layout过程完成之后才开始正确赋值的,而Measure与Layout却都晚于onCreate方法执行,所以onCreate中getLeft根本就取不到值!
一般来说,获取View的坐标有以下三个方法
- 监听Draw/Layout事件:ViewTreeObserver
- 将一个runnable添加到Layout队列中:View.post()
- 重写View的onLayout方法
- 重写Activity的onWindowFocusChanged方法,在该方法中获取
这里我推荐1,2,4这两种,即
//获得ViewTreeObserver
ViewTreeObserver observer=view.getViewTreeObserver();
//注册观察者,监听变化
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if(observer.isAlive()){
observer.removeOnDrawListener(this);
}
//获得宽高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
return true;
}
});
view.post(new Runnable() {
@Override
public void run() {
view.getHeight();
}
});
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//此处可以正常获取width、height等
}
对于MotionEvent:
因为MotionEvent提供的获取坐标的方法是在页面完完全全显示在用户眼前且用户点击后才会使用到的方法,所以并不存在获取不到的问题。
一般直接在哦了TouchEvent()或者dispatchTouchEvent()中获取即可。
4 View移动的位置计算
View的移动一共有以下几种方式,总结起来如下:
1.layout
这种方式,直接改变mTop,mLeft,mRight,mBottom的位置,使用的方法如下:
复写View的onLayout()方法
void onLayout (boolean changed, int left, int top, int right, int bottom)
2.offsetLeftAndRight、offsetTopAndBottom
也是直接改变mTop,mLeft,mRight,mBottom的位置
3.修改LayoutParams
可以改变Margin或者本身的mTop,mLeft,mRight,mBottom等参数
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
lp.leftMargin = getLeft() + 200;
lp.topMargin = getTop() + 400;
setLayoutParams(lp);
或
ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) view.getLayoutParams();
view.setLayoutParams(lp);
4.scrollTo与scrollBy
注意,这个移动不会改变mTop,mLeft,mRight,mBottom的位置,改变的是x、y、translationX、translationY。
因为对于Scroll,例如scrollX指的是view在滑动过程中,view的左边缘和view内容的左边缘在水平方向的距离(注意与translationX 的区别,translationX 指的是view本身的移动,scrollX是view的内容移动),也就是说调用了view的所以所以scrollTo或scrollBy方法,view本身不会移动,只会移动view的内容。
对于scrollTo、scrollBy需要注意的有两个问题
问题1:
移动计算值 = 最开始点坐标 - 最后移动到的坐标
原因是因为最终会调用这个方法
—— invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
其中l,t,r,b为原来坐标点,scrollX,scrollY为目标坐标点,只有当目标坐标点值是负数时,移动到的位置才为正数!
例如scrollTo ,我们要从(0,0)移动到(200,400)这个点,根据上面的公式可知为负值
问题2
为什么需要加上 ((View)getParent())
TextView本身是View,scrollTo、scrollBy移动的都是View的Content,如果不加的话,使用的效果则是TextView的文字位置变化,而TextView本身不会变化。
如果在ViewGroup中使用scrollTo、scrollBy,则移动的是ViewGroup中的View.我们这里需要让TextView移动,则需要先 ((View)getParent()),然后再((View)getParent()).scrollTo…
一句话总结:就是对于scrollTo,scrooBy一般都是让ViewGroup移动,而不是View滚动。
5.属性动画
改变的是具体的属性,可以为mTop,mLeft,mRight,mBottom,translationX、translationY等。但是需要按照属性动画的规则来。
下面是一个ObjectAnimator
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(this, "translationX", 200),
ObjectAnimator.ofFloat(this,"translationY", 400)
);
set.start();
translationX,translationY。
6.位移动画
位移动画改变的是translationX,translationY,例如下面的例子。
TranslateAnimation anim = new TranslateAnimation(0,200,0,400);
anim.setFillAfter(true);
startAnimation(anim);
注意点
属性动画是真实改变View的位置的,虽然属性动画、位移动画的getLeft等没有改变,但是属性动画的getX、getY是改变了的,位移动画的getX、getY仍未改变!
关于位移动画的补充点:
我们经常用这样的需求,要求一个popupwindow从屏幕底部弹出或者从屏幕顶部弹出。
这里的位移设置同样还是如此(原点在屏幕左上角,向右x为正,向下y为正)
这篇博文可以参考学习下,下面这张神图也是来自这篇博文(Android动画之translate(位移动画))
http://www.cnblogs.com/bavariama/archive/2013/01/29/2881225.html
我们首先要认识到布局坐标如下:
所以如果要从屏幕底弹出,就是Y位置从100%p到0%p。
关于View的坐标就暂时分析道这里吧,后续再继续补充