View体系与自定义View(一)—— 什么是View

1 ViewViewGroup

ViewAndroid中所有控件的基类,不管是简单的ButtonTextView还是复杂的RelativeLayoutListView,它们的共同基类都是View。所以说,View是一种界面层的控件的一种抽象,它代表了一个控件。除了View还有ViewGroup,从名字上看,可以翻译成控件组,即一组ViewViewGroup可以包含很多View以及ViewGroup,而它包含的ViewGroup又可以包含ViewViewGroup,以此类推,形成一个View树。

View和ViewGroup

Android的设计中,ViewGroup也继承了View,这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件。

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { }

public abstract class ViewGroup extends View implements ViewParent, ViewManager { }

public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { }

public class Button extends TextView { }

public class ImageView extends View { }

public class LinearLayout extends ViewGroup { }

public class RelativeLayout extends ViewGroup { }

public class ListView extends AbsListView { }

public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
        ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
        ViewTreeObserver.OnTouchModeChangeListener,
        RemoteViewsAdapter.RemoteAdapterConnectionCallback { }

public abstract class AdapterView<T extends Adapter> extends ViewGroup { }

View的部分继承关系

2 坐标系

Android系统中有两种坐标系,分别为Android坐标系和View坐标系。

2.1 Android坐标系

Andriod中,将屏幕左上角的顶点作为Android坐标系的原点,这个原点向右是X轴正方向,向下是Y轴正方向。在触控事件中,使用getRawX()getRawY()方法获得的坐标是Android坐标系的坐标。

raw [rɔː] 生的;未加工的;阴冷的;刺痛的;擦掉皮的;无经验的;(在艺术等方面)不成熟的

Android坐标系

2.2 View坐标系

View坐标系,它与Android坐标系并不冲突,两者是共同存在的。它们可以帮助开发者更好的控制 View

View坐标系

2.2.1 View自身的坐标

View的位置主要是由它的四个顶点来决定的,分别对应View的四个属性:topleftrightbottom 其中top是左上角的纵坐标,left是左上角横坐标,right是右下角很坐标,bottom是右下角纵坐标。这些坐标都是相对于View的父容器来说的,是一种相对坐标。

View的四个参数,在源码中分别对应于mLeftmRightmTopmBottom这四个成员变量:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
  protected int mLeft;
  protected int mRight;
  protected int mTop;
  protected int mBottom;
  
  public final int getLeft() { return mLeft;  }
  public final int getRight() { return mRight; }  
  public final int getTop() { return mTop; }  
  public final int getBottom() { return mBottom; }  
}

通过如下方法可以获得View到其父控件(ViewGroup)的距离:

  • getLeft():获取View自身左边到其父布局左边的距离
  • getRight():获取View自身右边到其父布局左边的距离
  • getTop():获取View自身顶边到其父布局顶边的距离
  • getBottom():获取View自身底边到父布局顶边的距离

Android 3.0开始,View增加了几个额外的参数:xytranslationXtranslationY,其中xyview相对于父容器左上角的坐标,而translationXtranslationYView左上角相对于父容器的偏移量,并且translationXtranslationY的默认值是0 几个参数的换算关系如下:

x = left + translationX
y = top + translationY

public float getX() {
   return mLeft + getTranslationX();
}

public float getY() {
   return mTop + getTranslationY();
}

View的平移过程中,topleft表示的是原始左上角的位置信息,其值并不会发生改变,发生改变的是xytranslationXtranslationY

2.2.2 View获取自身的宽高

系统提供了获取View宽和高的方法,以下是View的源码:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
  
  public final int getWidth() {
    return mRight - mLeft;
  }
  
  public final int getHeight() {
    return mBottom - mTop;
  }
}
  • getWidth()用来获取View的宽度
  • getHeight()用来获取View自身的高度

3 MotionEventTouchSlop

motion [ˈmoʊʃn] 动作;移动;手势;请求;意向;议案 slop [slɑːp] 漫不经心地涂(或敷)

3.1 MotionEvent

无论是View还是ViewGroup,最终的点击事件都会由onTouchEvent(MotionEvent event)方法来处理。MotionEvent在用户交互过程中作用重大,其内部提供了很多事件常量,典型的事件类型有如下几种:

  • ACTION_DOWN:手指刚接触屏幕
  • ACTION_MOVE:手指在屏幕上移动
  • ACTION_UP:手指从屏幕上松开的一瞬间

正常情况下,一次手指触摸屏幕的行为会出发一系列点击事件,考虑如下几种情况:

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

此外,MotionEvent也提供了获取焦点坐标的各种方法:

  • getX():获取点击事件距离控件左边的距离,即视图坐标
  • getY():获取点击事件距离控件定边的距离,即视图坐标
  • getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标
  • getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标
3.2 TouchSlop

TouchSlop是系统所能识别出的被认为是滑动的最小距离,如果两次滑动之间的距离小于这个常量,那么系统不认为是在进行滑动操作。这个常量和设备相关,在不同的设备上这个值是不同的。通过以下方式可以获取这个常量ViewConfiguration.get(getContext()).getScaledTouchSlop();

4 VelocityTracker速度追踪

velocity [vəˈlɑːsəti]【物】速度 tracker [ˈtrækər] 拉纤者,纤夫;追踪系统,[自] 跟踪装置;追踪者

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

@Override
public boolean onTouchEvent(MotionEvent event) {
  	// 1. 在onTouchEvent事件中追踪当前点击事件的速度
    VelocityTracker velocityTracker = VelocityTracker.obtain();
    velocityTracker.addMovement(event);
  	// 2. 获取当前速度,获取之前先计算速度,需要设置时间间隔(单位是毫秒),注意,速度可以是负数
    velocityTracker.computeCurrentVelocity(1000);
    int xVelocity = (int) velocityTracker.getXVelocity();
    int yVelocity = (int) velocityTracker.getYVelocity();

    return super.onTouchEvent(event);
}

当不需要它的时候,需要调用clear方法来重制并回收内存:

velocityTracker.clear();
velocityTracker.recycle();

5 GestureDetector手势检测

gesture [ˈdʒestʃər] 手势,姿势;姿态,表示 detector [dɪˈtektər] 探测器;检测器;发现者;侦察器

手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。

首先,实现实现GestureDetector.OnGestureListener接口,根据需要还可以实现GestureDetector.OnDoubleTapListener从而能监听双击行为:

public class RoundImageView extends AppCompatImageView implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {

  @Override
  public boolean onDown(MotionEvent e) {
    return false;
  }

  @Override
  public void onShowPress(MotionEvent e) {

  }

  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    return false;
  }

  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return false;
  }

  @Override
  public void onLongPress(MotionEvent e) {

  }

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    return false;
  }

  @Override
  public boolean onSingleTapConfirmed(MotionEvent e) {
    return false;
  }

  @Override
  public boolean onDoubleTap(MotionEvent e) {
    return false;
  }

  @Override
  public boolean onDoubleTapEvent(MotionEvent e) {
    return false;
  }
}

其次,创建GestureDetector对象:

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

之后,接管目标ViewonTouchEvent方法,在ViewonTouchEvent方法中添加如下实现:

@Override
public boolean onTouchEvent(MotionEvent event) {  
  return gestureDetector.onTouchEvent(event);
}

完成之后,就可以有选择的实现GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener中的方法了。

OnGestureListener和OnDoubleTapListener方法介绍

在实际开发中,如果只是监听滑动相关的,建议在onTouchEvent中实现,如果要监听双击这种行为的话,就使用GestureDetector

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值