前言
android中的View形形色色,我们刚开始接触Android的时候就开始于它打交道了,但View以及与View相关的周边的知识点也是非常的繁多,并且从初级到高级我们都有很多可以学习的地方。我所知道的讲解View最全的应该是启航的Android自定义控件三部曲,非常面干货,在看人家博客的同时,我们也需要自己掌握相关知识。对于View我会根据我的理解以及看过的书和博客整理出View系列文章,从这里开始View之旅吧!
View的构造方法
我们使用的控件,都直接或间接的继承View,控件的使用有的是在xml里面有的是使用new的方式创建,所以我们先看看View的构造方法什么时候调用。
View的构造方法4个:
- 方法一 是在Code中New 这个View的时候调用
- 方法二 如果要在xml文件中使用,那么自定义的View得实现的方法,没有设置attrs,默认为null
- 方法三和方法四在自定义View中可根据自己需要实现,但方法四实现需要最小Api为21
public View(Context context){
}
public View(Context context, @Nullable AttributeSet attrs){
}
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
}
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
}
这里说一下构造方法中AttributeSet,defStyleAttr,defStyleRes
这三个参数;
AttributeSet
: 用于资源分析,接收xml中定义的属性信息,比如我们TextView默认字体大小,就是用AttributeSet
取到的,因此我们可以自己实现修改TextView等默认属性。当我们不实现AttributeSet就默认使用父类的,
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView);
int textColor = a.getColor(R.styleable.MyView_textColor,0XFFFFFFFF);
float textSize = a.getDimension(R.styleable.MyView_textSize, 36);
defStyleAttr
和defStyleRes
都是设置自定义属性,只是他们有一定的优先级,
在AppTheme中设置默认样式defStyleAttr
的同时,在自定义View中也设置defStyleRes
,默然情况下优先级:defStyleAttr > defStyleRes
,也就是说只有在defStyleAttr为0的情况下,系统才会读取defStyleRes的值。
在整个自定义View设置属性的优先级为:xml > style > defStyleAttr > defStyleRes > theme
View的位置参数
View的参数位置只是做一个记录,经常使用肯定会知道,本来是想在processon上自己画图,画了会感觉里面基础图形很不友好,并且箭头动不动就弯曲,不好用,求推荐好用的画图工具。
View的坐标系,起始点为左上角x,y:(0,0)往左边X轴逐渐增大,往下边Y轴逐渐增大。如果当前屏幕大小为1080*1920,那么右下角的坐标为(1080,1920)。
下面坐标获取的Api来自两个类,上面一类来自View,如 getTop() getBottom() getLeft() getRight()
下面一类是来自MotionEvent类在实现onTouchEvent等触摸方法里面得到参数。如event.getRawX() event.getRawY()
getX() getY()
是指:这个是android 3.0中View增加的,是指点击位置距离控件左上角的距离,如多点击的刚好是左上角那么得到的就是(0,0)getTranslationX() getTranslationY()
是指View左上角相对于父容器的偏移量,默认值为0.- 还有一个公式,getX()=getLeft+getTranslationX();
- getY()=getTop+getTranslationY();
要知道View和MotionEvent都有getX()和getY()这个方法
,MotionEvent的getX() getY()是指:手指触摸点距离当前View左上角的x和y坐标。
上面的Api方法来自MotionEvent类,在实现onTouchEvent等触摸方法里面得到参数。
event.getRawX() event.getRawY()
这个算一组,获取的是相对手机屏幕左上角x和y的坐标event.getX() event.getY()
算一组,获取的是手指触摸点距离当前View左上角的x和y坐标
坐标实战
在这里我们要实现一个View,使用手可以拖动着滑动,我们用到上面的左边来实现
我们分别用getX() getY()和getRawX getRawY实现
int lastX = 0;
int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
//View相对于父容器左上角的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//每次移动的过程中x y的值都在更新,跟新后的值减去
int offsetX = x - lastX;
int offsetY = y - lastY;
layout(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
主要是记录点击的地方移动的位置,然后再移动的时候加上移动的距离就好了。
int lastX = 0;
int lastY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取的是相对手机屏幕左上角x和y的坐标
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = rawX;
lastY = rawY;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = rawX - lastX;
int offsetY = rawY - lastY;
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
lastX = rawX;
lastY = rawY;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
View的相关方法
方法名称 | 方法作用 |
---|---|
View(Context context) | View的构造方法,当在Code中创建或在XML中使用时调用 |
onMeasure(int, int) | View测量方法,用于计算View的大小 |
onLayout(boolean, int, int, int, int) | View测量后的布局方法,用于设置View的位置 |
onSizeChanged(int, int, int, int) | 在View尺寸发生变化的时候调用 |
onDraw(android.graphics.Canvas) | 在Canvas上开始绘制View |
onKeyDown(int, KeyEvent) | 物理按键被按下的时候调用 |
onKeyUp(int, KeyEvent) | 物理按键抬起的时候调用时候调用 |
onTrackballEvent(MotionEvent) | 在View尺寸发生变化的时候调用 |
onFocusChanged(boolean, int, android.graphics.Rect) | 该方法在View焦点发生变化的时候被调用 |
onWindowFocusChanged(boolean) | 包含当前View的Window获得或失去焦点时被调用 |
onAttachedToWindow() | 当前View附加到Window时候调用 |
onDetachedFromWindow() | 当前View从Window上分离时候调用 |
onVisibilityChanged(View, int) | 当前View或其祖先的可见性改变时被调用 |
onWindowVisibilityChanged(int) | 当前View的window可见性改变时被调用 |
onFinishInflate() | 当View中所有的子控件均被映射成xml后触发,可用于初始化控件和数据 |
View与Activity的生命周期
在View的Measure过程中我们能看到下面相关方法,并且从下面周期中能为我们获取View大小的时机带来启发