@946898963
2019-12-31T16:48:06.000000Z
字数 5968
阅读 930
Android绘图
从类的角度来说,View就是Android中的一个类,而且是所有视图控件的基类。
从视图显示的角度来说,View就是一个显示控件,这个控件可以显示一些视图,同时可以对用户的点击,触摸等事件作出响应。View可以是一个控件,也可以是多个控件组成的一组控件,也就是ViewGroup,ViewGroup也继承自View,其内部可以包含View。
在Android中,默认的View的形状都是矩形。也就是说,只要确定了View的左上顶点和右下顶点的坐标,我们就可以确定了View的位置。这两个顶点分别对应了View的四个属性:
- top: 左上角纵坐标
- left: 左上角横坐标
- bottom: 右下角纵坐标
- right: 右下角横坐标
值得注意的是,这里所说的坐标都是相对坐标,这些坐标都是相对于父容器来说的。在Android系统中,屏幕的左上角作为,整个坐标系的原点,x轴和y轴的正向分别为右和下,大部分的Android的系统都是按照这个坐标系来进行显示的。
上图中,涉及到了以下方法:
- view获取自身宽高:getHeight(),getWidth()
- view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()
- MotionEvent获取坐标:getX(),getY(),getRawX(),getRawY()
View获取View的自身宽高不用多说,从方法命名上就可以看出来。
对于View本身是放置在一个ViewGroup中,先不管ViewGroup的大小,我们只要关心View在ViewGroup的位置就好了。前面我们已经提到了View在ViewGroup的位置是由View的4个位置属性决定的,它们之间的关系是这样的
- getTop():获取到的是view自身的顶边到其父布局顶边的距离,即view的top属性
- getLeft():获取到的是view自身的左边到其父布局左边的距离,即view的left属性
- getRight():获取到的是view自身的右边到其父布局左边的距离,即view的right属性
- getBottom():获取到的是view自身的底边到其父布局顶边的距离,即view的Bottom属性
从图中,我们可以清楚地得到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这四个参数。
验证代码:
tv_test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_test, "translationX", 0, 100).setDuration(100);
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.d("zhangyan", "Left:" + tv_test.getLeft());
Log.d("zhangyan", "Right:" + tv_test.getRight());
Log.d("zhangyan", "Top:" + tv_test.getTop());
Log.d("zhangyan", "Bottom:" + tv_test.getBottom());
Log.d("zhangyan", "translationX:" + tv_test.getTranslationX());
Log.d("zhangyan", "translationY:" + tv_test.getTranslationY());
Log.d("zhangyan", "X:" + tv_test.getX());
Log.d("zhangyan", "Y:" + tv_test.getY());
Log.d("zhangyan", ":::::::::::::::::" );
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d("zhangyan", "Left:" + tv_test.getLeft());
Log.d("zhangyan", "Right:" + tv_test.getRight());
Log.d("zhangyan", "Top:" + tv_test.getTop());
Log.d("zhangyan", "Bottom:" + tv_test.getBottom());
Log.d("zhangyan", "translationX:" + tv_test.getTranslationX());
Log.d("zhangyan", "translationY:" + tv_test.getTranslationY());
Log.d("zhangyan", "X:" + tv_test.getX());
Log.d("zhangyan", "Y:" + tv_test.getY());
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
objectAnimator.start();
}
});
对于MotionEvent这四个方法,也是获取View的坐标,其所对应的的参考点不一样,它们的用途也不一样,具体该如何使用呢?在紧随其后的Android自定义View之MotionEvent会了解其使用情况。
- getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
- getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
- getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
- getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离
前面获取到的View的位置坐标都是相对坐标(相对于父容器),而在MotionEvent所提供的四个方法中,可以获取到是点击事件相对坐标。有时候需要获取View在整个屏幕的位置,这个时候,又该如何呢?在View中定义了这样的四个方法:
- getLocationInWindow(int[])
- getLocationOnScreen(int[])
int[] position = new int[2];
textview.getLocationInWindow(position);
这个方法是将view的左上角坐标存入数组中.此坐标是相对当前activity而言.
1. 若是普通activity,则y坐标为可见的状态栏高度+可见的标题栏高度+view左上角到标题栏底部的距离.
2. 若是对话框式的activity,则y坐标为可见的标题栏高度+view到标题栏底部的距离.
可见的意思是:在隐藏了状态栏/标题栏的情况下,它们的高度以0计算.此时是无视状态栏的有无的.
Android中Activity界面:
int[] position = new int[2];
textview.getLocationOnScreen(position);
这个方法跟上面的差不多,也是将view的左上角坐标存入数组中.但此坐标是相对整个屏幕而言.
y坐标为view左上角到屏幕顶部的距离.
关于区别如果还是不懂的话,建议阅读:android中getLocationInWindow 和 getLocationOnScreen的区别
需要注意的以上方法在OnCreate方法中调用,都会返回0,这是因为View还未加载完毕.建议在onWindowFocusChanged方法中进行获取,有些情况下onWindowFocusChanged不好用的时候(比如ActivityGroup),可以这样写:
mTextView.post(new Runnable() {
@Override
public void run() {
//获取位置信息
}
});
这样在View加载完毕之后会执行获取位置的方法.
手机触摸屏幕产生的一系列事件中,典型的事件类型有如下三种:
- ACTION_DOWN 手指刚接触到屏幕
- ACTION_MOVE 手指在屏幕上移动
- ACTION_UP 手指从屏幕离开
手指触摸屏幕会产生一些列的点击事件,典型的是如下两种情况:
- 点击屏幕后离开松开,事件序列为DOWN->UP
- 点击屏幕滑动一会再松开,事件序列为DOWN->MOVE->...->MOVE->UP
如何获取点击位置的坐标,参考前面的纪录。
TouchSloup是系统所能识别出的被认为是滑动的最小距离,这是一个常量,与设备有关,可通过以下方法获得:
ViewConfiguration.get(getContext()).getScaledTouchSloup().
速度追踪,用于追踪手指在滑动过程中的速度,包括水平放向速度和竖直方向速度。 使用方法:
1.在View的onTouchEvent方法中追踪当前单击事件的速度
VelocityRracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
2.计算速度,获得水平速度和竖直速度
velocityTracker.computeCurrentVelocity(1000);
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();
注意,获取速度之前必须先计算速度,即调用computeCurrentVelocity方法,这里指的速度是指一段时间内手指滑过的像素数,1000指的是1000毫秒,得到的是1000毫秒内滑过的像素数。速度可正可负:
速度 = (终点位置 - 起点位置) / 时间段
最后,当不需要使用的时候,需要调用clear()方法重置并回收内存:
velocityTracker.clear();
velocityTracker.recycle();
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。 使用方法:
1.创建一个GestureDetector对象并实现OnGestureListener接口,根据需要,也可实现OnDoubleTapListener接口从而监听双击行为:
GestureDetector mGestureDetector = new GestureDetector(this);
//解决长按屏幕后无法拖动的现象
mGestureDetector.setIsLongpressEnabled(false);
2.在目标View的OnTouchEvent方法中添加以下实现:
boolean consume = mGestureDetector.onTouchEvent(event);
return consume;
3.实现OnGestureListener和OnDoubleTapListener接口中的方法,其中常用的方法有:onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)和onDoubleTap(双击)。 建议:如果只是监听滑动相关的,可以自己在onTouchEvent中实现,如果要监听双击这种行为,那么就使用GestureDetector。
弹性滑动对象,用于实现View的弹性滑动。其本身无法让View他行滑动,需要和View的computeScroll方法配合使用才能完成这个功能。 使用方法:
Scroller scroller = new Scroller(mContext);
//缓慢移动到指定位置
private void smoothScrollTo(int destX,int destY){
int scrollX = getScrollX();
int delta = destX - scrollX;
//1000ms内滑向destX,效果就是慢慢滑动
mScroller.startScroll(scrollX,0,delta,0,1000);
invalidate();
}
@Override
public void computeScroll(){
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX,mScroller.getCurrY());
postInvalidate();
}
}
参考资料:
《Android第一行代码》
Android自定义View之View的位置参数
android中getLocationInWindow和getLocationOnScreen的区别