自定义View应该明白的基础知识
来源:互联网 发布:淘宝买奢侈品 编辑:程序博客网 时间:2024/06/05 01:51
自定义View应该明白的基础知识
- 自定义View应该明白的基础知识
- 认识Android坐标系
- 获取相对于父坐标的距离
- MotionEvent中 event 获取的坐标
- 自定义View绘制流程
- 关于自定义View的构造函数
- 测量View的大小
- MeasureSpec 中的3种测量方式
- 确定View的大小
- 确定子View的位置
- 绘制View onDraw
- View事件分发
- 事件分发机制
- View中的OnTouch事件
- View的滑动冲突
- View的滑动
- ScrollTo和ScrollBy
- 使用属性动画
- 设置LayoutParams的值
- View的工作原理
- MeasureSpec 测量规则
- SpecMode 由三类
- 3个流程 mesure layout draw
- Measure 过程
- Layout过程
- Draw过程
- MeasureSpec 测量规则
- 参考
认识Android坐标系
Android的坐标系是从左上角开始,向左为X正方向,向下为Y正方向,与普通的坐标系有些区别。
获取相对于父坐标的距离
getTop(); //获取子View左上角距父View顶部的距离getLeft(); //获取子View左上角距父View左侧的距离getBottom(); //获取子View右下角距父View顶部的距离getRight(); //获取子View右下角距父View左侧的距离
Android3.0 之后还添加了 x,y,translationX,translationY 四个值,这四个值也是相对于父View的,默认 translationX和translationY 等于0,并且存在以下关系:
x = left + translationXy = top + translationY
当View发生平移时,left 和 top 不会改变,改变的是 x,y,translationX,translationY 四个值
MotionEvent中 event 获取的坐标
event.getX(); //触摸点相对于其所在组件坐标系的坐标event.getY();event.getRawX(); //触摸点相对于屏幕默认坐标系的坐标event.getRawY();
自定义View绘制流程
关于自定义View的构造函数
public void SloopView(Context context) {}public void SloopView(Context context, AttributeSet attrs) {}public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {}public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}
构造函数是以上四种,最常用的是带一个参数何两个参数的。调用的时机为:
SloopView view = new SloopView(this); // 调用一个参数的构造函数
//调用两个参数的构造函数 <com.sloop.study.SloopView android:layout_width="wrap_content" android:layout_height="wrap_content"/>
如果使用了自定义属性,则需要定义3个参数的构造函数,这里省略。
测量View的大小
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值 int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式 int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值 int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模式}
MeasureSpec 中的3种测量方式
注意:
如果对View的宽高进行修改了,不要调用super.onMeasure(widthMeasureSpec,heightMeasureSpec);要调用setMeasuredDimension(widthsize,heightsize); 这个函数。
确定View的大小
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); }
确定子View的位置
child.layout(l, t, r, b);
绘制View( onDraw() )
真正绘制View的部分,当计算玩View的大小,并且通过onLayout确定了它的位置,就可以通过onDraw() 绘制出View。
View事件分发
这样一个View层级:
结构如下:
事件分发流程:
当有点击事件,首先是Activity捕获到,一直传递到View,如果这一个过程事件都没有被处理,则事件会被反向传播给Activity,如果还没有被处理,则抛弃。
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View
如果在View1 上发生touch事件,会依次调用下面的方法,红色是正向传播,绿色是回传:
事件分发机制
当一个点击事件发生时,一般会经历下面3个重要的方法:
- dispatchTouchEvent(MotionEvent ev)
- onInterceptTouchEvent(MotionEvent ev)
- onTouchEvent(MotionEvent ev)
dispatchTouchEvent:用来进行事件分发,如果事件能传递给当前View,这个方法一定会被执行
onInterceptTouchEvent:在上述方法内部调用,用来判断是否拦截某个事件
onTouchEvent:在 dispatchTouchEvent 中调用,用于处理点击事件
三者的调用如下伪代码:
View中的OnTouch事件
onTouchListener 的优先级比 onTouch 高,如果 listener中返回 true,表示已经处理,就不会将事件继续传递给 onTouch,这样做的好处是方便外界处理点击事件。
View的滑动冲突
这里先省略,详细请看《Android开发艺术探索》
View的滑动
一般有以下几种常用的方法:
1. scrollTo和scrollBy
2. 通过动画(平移动画,属性动画两种)
3. 改变View的LayoutParams使得View重新布局
ScrollTo和ScrollBy
- 如果要使View向右下方向滑动,那么 传入的 x,y的值应该为负数。
- 移动时,只有View的内容移动,但是View本身不移动
使用属性动画
mLauncher1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { float x = mLauncher1.getX(); float y = mLauncher1.getY(); ObjectAnimator.ofFloat(mLauncher1, "translationX", x, x + 100).setDuration(100).start(); ObjectAnimator.ofFloat(mLauncher1, "translationY", y, y + 100).setDuration(100).start(); }});
设置LayoutParams的值
需要注意的是,这种情况会引起View的重新绘制,效率要低一些
mlauncher2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mlauncher2.getLayoutParams(); params.leftMargin += 100; params.topMargin += 100; mlauncher2.requestLayout(); }});
View的工作原理
MeasureSpec 测量规则
MeasureSpec是一个int型的值, 由两个值 Mode(高2位) 和 Size(低30位)组成。
子View的 MeasureSpec由 父容器的MeasureSpec和子View的LayoutParams共同决定
SpecMode 由三类
UNSPECIFIED:父容器不对View由任何的限制,一般系统中用,不用关注。
EXACTLY:父容器已经测量出View所需的大小,View的大小由 SpecSize所指定,它对对应于LayoutParams中的match_parent
AT_MOST:父容器指定一个可用的大小 SpecSize给View,对应 wrap_content
3个流程 mesure, layout, draw
Measure 过程
分为View的Measure过程和ViewGroup的Measure过程,ViewGroup的其实就是计算自己的,并且调用所有子View的Measure方法。
如何获取View的宽高:
1. 如果要获取某个View的宽高,在onCcreate,onResume,onStart这些方法中都是不能获取的。因为Activity和View的Measure不是同步执行的。可以在 onWindowFocusChanged 这个方法中获取,表示View的初始化已经完毕,会调用这个方法。但是它会被多吃调用,失去焦点和得到焦点都会被调用依次。
- view.post(runnable) 中获取
- ViewTreeObserver,onGlobalLayoutListener中可以,但是当View树状态改变时都会被调用。
- view.measure( measureSpecWidth, measureSpecHeight) 手动对View的measure中获得。根据LayoutParams获得,但是这种情况比较复杂,分为下面几种:
- match_parent:基本无法获得
- 具体值:width = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)
- wrap_content:width = MeasureSpec.makeMeasureSpec((1<<30)-1, MeasureSpec.AT_MOST)
Layout过程
测量自己应该在父容器中的位置
一般在onMeasure 中可以获取View的最终大小,但是极端情况也可能不正确。例如 onLayout中调用:super.layout(l,t,r+100,b+100),就会导致view的宽高都加100
Draw过程
一般分为下面4个步骤:
1. background.draw(canvas) 画背景
2. onDraw 画自己
3. dispatchDraw 画children
4. onDrawScrollBar 画装饰
参考
《Android开发艺术探索》
自定义View合集
- 自定义View应该明白的基础知识
- 【Android自定义View实战】之你应该明白的事儿
- 自定义View的基础知识
- Android自定义控件基础知识----View的生命周期
- android 自定义view基础知识
- 自定义view基础知识
- android 自定义view基础知识
- 自定义View 必备基础知识
- 男人应该明白的道理
- 自定义View应该怎么定义
- Android自定义view之基础知识
- Android 自定义View——View 基础知识
- 应该三十岁以前想明白的四件事
- 人应该明白的五句话
- 每个java 初学者都应该明白的
- 创业者应该明白的几件事情
- 程序员发展应该明白的十三个道理
- 做技术支持应该明白的事
- 跳槽季,你准备好了吗(二)?
- 队列 Queue 的定义及实现
- Udacity自动驾驶课程笔记(二)--计算机视觉和深度学习
- leetcode504: Base 7
- Jquery搜索页面的关键字
- 自定义View应该明白的基础知识
- matlab根据行列索引矩阵寻找元素
- 简单图片库
- js完美运动框架最终版
- ionic2 数据更新,刷新页面
- 菜鸟java
- LintCode 6 合并排序数组
- 移植三星版本uboot_s5pv210
- shell命令之基本的数组操作