[关闭]
@946898963 2019-12-31T16:48:06.000000Z 字数 5968 阅读 930

View的基础知识

Android绘图


什么是View

从类的角度来说,View就是Android中的一个类,而且是所有视图控件的基类。
从视图显示的角度来说,View就是一个显示控件,这个控件可以显示一些视图,同时可以对用户的点击,触摸等事件作出响应。View可以是一个控件,也可以是多个控件组成的一组控件,也就是ViewGroup,ViewGroup也继承自View,其内部可以包含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的自身宽高不用多说,从方法命名上就可以看出来。

对于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自身坐标之间的关系:

  1. width = right - left = getRight() - getLeft()
  2. heiht = bottom - top = getBottom() - getTop()

Android 3.0新添属性

在Android 3.0以后,对View又新添了4个属性:

  • x:view左上角的横坐标
  • y:view左上角的纵坐标
  • translationX:左上角相对于父容器的偏移量
  • translationY:左上角相对于父容器的偏移量

这几个参数坐标都是相对于父容器的相对坐标,并且translationX和translationY的默认值都是0,view也分别为它们提供了get()和set方法。它们之间的换算关系是:

  1. x = lef + translationX;
  2. y = top + translationY;

值得注意的是,View在平移的过程中,top和Left表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x、y、translationX和translationX这四个参数。
此处输入图片的描述 此处输入图片的描述

验证代码:

  1. tv_test.setOnClickListener(new View.OnClickListener() {
  2. @Override
  3. public void onClick(View v) {
  4. ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_test, "translationX", 0, 100).setDuration(100);
  5. objectAnimator.addListener(new Animator.AnimatorListener() {
  6. @Override
  7. public void onAnimationStart(Animator animation) {
  8. Log.d("zhangyan", "Left:" + tv_test.getLeft());
  9. Log.d("zhangyan", "Right:" + tv_test.getRight());
  10. Log.d("zhangyan", "Top:" + tv_test.getTop());
  11. Log.d("zhangyan", "Bottom:" + tv_test.getBottom());
  12. Log.d("zhangyan", "translationX:" + tv_test.getTranslationX());
  13. Log.d("zhangyan", "translationY:" + tv_test.getTranslationY());
  14. Log.d("zhangyan", "X:" + tv_test.getX());
  15. Log.d("zhangyan", "Y:" + tv_test.getY());
  16. Log.d("zhangyan", ":::::::::::::::::" );
  17. }
  18. @Override
  19. public void onAnimationEnd(Animator animation) {
  20. Log.d("zhangyan", "Left:" + tv_test.getLeft());
  21. Log.d("zhangyan", "Right:" + tv_test.getRight());
  22. Log.d("zhangyan", "Top:" + tv_test.getTop());
  23. Log.d("zhangyan", "Bottom:" + tv_test.getBottom());
  24. Log.d("zhangyan", "translationX:" + tv_test.getTranslationX());
  25. Log.d("zhangyan", "translationY:" + tv_test.getTranslationY());
  26. Log.d("zhangyan", "X:" + tv_test.getX());
  27. Log.d("zhangyan", "Y:" + tv_test.getY());
  28. }
  29. @Override
  30. public void onAnimationCancel(Animator animation) {
  31. }
  32. @Override
  33. public void onAnimationRepeat(Animator animation) {
  34. }
  35. });
  36. objectAnimator.start();
  37. }
  38. });

MotionEvent获取坐标

对于MotionEvent这四个方法,也是获取View的坐标,其所对应的的参考点不一样,它们的用途也不一样,具体该如何使用呢?在紧随其后的Android自定义View之MotionEvent会了解其使用情况。

  • getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
  • getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
  • getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
  • getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离

动态获取View的位置

前面获取到的View的位置坐标都是相对坐标(相对于父容器),而在MotionEvent所提供的四个方法中,可以获取到是点击事件相对坐标。有时候需要获取View在整个屏幕的位置,这个时候,又该如何呢?在View中定义了这样的四个方法:

  • getLocationInWindow(int[])
  • getLocationOnScreen(int[])

getLocationInWindow

  1. int[] position = new int[2];
  2. textview.getLocationInWindow(position);

这个方法是将view的左上角坐标存入数组中.此坐标是相对当前activity而言.
1. 若是普通activity,则y坐标为可见的状态栏高度+可见的标题栏高度+view左上角到标题栏底部的距离.
2. 若是对话框式的activity,则y坐标为可见的标题栏高度+view到标题栏底部的距离.
可见的意思是:在隐藏了状态栏/标题栏的情况下,它们的高度以0计算.此时是无视状态栏的有无的.
Android中Activity界面:
此处输入图片的描述

getLocationOnScreen

  1. int[] position = new int[2];
  2. textview.getLocationOnScreen(position);

这个方法跟上面的差不多,也是将view的左上角坐标存入数组中.但此坐标是相对整个屏幕而言.
y坐标为view左上角到屏幕顶部的距离.

关于区别如果还是不懂的话,建议阅读:android中getLocationInWindow 和 getLocationOnScreen的区别

需要注意的以上方法在OnCreate方法中调用,都会返回0,这是因为View还未加载完毕.建议在onWindowFocusChanged方法中进行获取,有些情况下onWindowFocusChanged不好用的时候(比如ActivityGroup),可以这样写:

  1. mTextView.post(new Runnable() {
  2. @Override
  3. public void run() {
  4. //获取位置信息
  5. }
  6. });

这样在View加载完毕之后会执行获取位置的方法.

MotionEvent和TouchSlop

MotionEvent

手机触摸屏幕产生的一系列事件中,典型的事件类型有如下三种:

  • ACTION_DOWN 手指刚接触到屏幕
  • ACTION_MOVE 手指在屏幕上移动
  • ACTION_UP 手指从屏幕离开

手指触摸屏幕会产生一些列的点击事件,典型的是如下两种情况:

  • 点击屏幕后离开松开,事件序列为DOWN->UP
  • 点击屏幕滑动一会再松开,事件序列为DOWN->MOVE->...->MOVE->UP

如何获取点击位置的坐标,参考前面的纪录。

Android MotionEvent详解

TouchSlop

TouchSloup是系统所能识别出的被认为是滑动的最小距离,这是一个常量,与设备有关,可通过以下方法获得:

  1. ViewConfiguration.get(getContext()).getScaledTouchSloup().

VelocityTracker、GestureDetector和Scroller

VelocityTracker

速度追踪,用于追踪手指在滑动过程中的速度,包括水平放向速度和竖直方向速度。 使用方法:

1.在View的onTouchEvent方法中追踪当前单击事件的速度

  1. VelocityRracker velocityTracker = VelocityTracker.obtain();
  2. velocityTracker.addMovement(event);

2.计算速度,获得水平速度和竖直速度

  1. velocityTracker.computeCurrentVelocity(1000);
  2. int xVelocity = (int)velocityTracker.getXVelocity();
  3. int yVelocity = (int)velocityTracker.getYVelocity();

注意,获取速度之前必须先计算速度,即调用computeCurrentVelocity方法,这里指的速度是指一段时间内手指滑过的像素数,1000指的是1000毫秒,得到的是1000毫秒内滑过的像素数。速度可正可负:

  1. 速度 = (终点位置 - 起点位置) / 时间段

最后,当不需要使用的时候,需要调用clear()方法重置并回收内存:

  1. velocityTracker.clear();
  2. velocityTracker.recycle();

GestureDetector

手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。 使用方法:
1.创建一个GestureDetector对象并实现OnGestureListener接口,根据需要,也可实现OnDoubleTapListener接口从而监听双击行为:

  1. GestureDetector mGestureDetector = new GestureDetector(this);
  2. //解决长按屏幕后无法拖动的现象
  3. mGestureDetector.setIsLongpressEnabled(false);

2.在目标View的OnTouchEvent方法中添加以下实现:

  1. boolean consume = mGestureDetector.onTouchEvent(event);
  2. return consume;

3.实现OnGestureListener和OnDoubleTapListener接口中的方法,其中常用的方法有:onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)和onDoubleTap(双击)。 建议:如果只是监听滑动相关的,可以自己在onTouchEvent中实现,如果要监听双击这种行为,那么就使用GestureDetector。

使用示例&使用示例&使用示例

Scroller

弹性滑动对象,用于实现View的弹性滑动。其本身无法让View他行滑动,需要和View的computeScroll方法配合使用才能完成这个功能。 使用方法:

  1. Scroller scroller = new Scroller(mContext);
  2. //缓慢移动到指定位置
  3. private void smoothScrollTo(int destX,int destY){
  4. int scrollX = getScrollX();
  5. int delta = destX - scrollX;
  6. //1000ms内滑向destX,效果就是慢慢滑动
  7. mScroller.startScroll(scrollX,0,delta,0,1000);
  8. invalidate();
  9. }
  10. @Override
  11. public void computeScroll(){
  12. if(mScroller.computeScrollOffset()){
  13. scrollTo(mScroller.getCurrX,mScroller.getCurrY());
  14. postInvalidate();
  15. }
  16. }

参考资料:
《Android第一行代码》
Android自定义View之View的位置参数
android中getLocationInWindow和getLocationOnScreen的区别

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注