HarmonyOS 鸿蒙Next中View的事件体系和工作流程
HarmonyOS 鸿蒙Next中View的事件体系和工作流程
View
1.View的事件体系
1.1 View基础知识
1.1.1 什么是View:View是所有控件的基类
ViewGroup也继承了View,即一组View,每个View又可以是一个ViewGroup
1.1.2 View的位置参数
x,y,translationX,translationY:
x,y是view左上角的坐标:x=left+translationX,y=top+translationY top,left是原生的左上角位置,不会改变,变的是x,y,translationX,translationY
1.1.3 MotionEvent和TouchSlop
1.MotionEvent:ACTION_DOWN,ACTION_MOVE,ACTION_UP
getX/getY:获得当前View的左上角的x,y坐标
getRowX/getRowY:获得手机屏幕左上角的x,y坐标
2.TouchSlop:可被识别为滑动的最小距离
这是一个常量,和设备有关,用ViewConfiguration.get(getContext()).getScaledTouchSlop()
1.1.4 VelocityTracker,GestureDetector,Scoller
1.VelocityTracker:追踪滑动速度(滑动过程中可能触发多次微小滑动事件)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var velocityTracker: VelocityTracker
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding=ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
velocityTracker=VelocityTracker.obtain()
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
velocityTracker.addMovement(event)
velocityTracker.computeCurrentVelocity(1000)//设置时间间隔,根据这个间隔内划过的像素数计算速度
val xVelocity=velocityTracker.xVelocity//与父布局坐标轴正方向一致,则速度为正
val yVelocity=velocityTracker.yVelocity
Toast.makeText(this,"水平速度是 ${xVelocity} , 竖直速度是 ${yVelocity}",Toast.LENGTH_SHORT).show()
return super.onTouchEvent(event)
}
override fun onDestroy() {
super.onDestroy()
// 释放VelocityTracker资源
velocityTracker.clear()
velocityTracker.recycle()
}
}
2.GestureDetector
1.实现GestureDetector.OnGestureListener接口:还要重写onTouchEvent
必须重写接口的所有方法:onDown,onShowPress, onSingleTapUp(单击),onScroll(拖动),onLongPress(长按),onFling(快速滑动)
2.使用GestureDetector.SimpleOnGestureListener()
只需要重写需要的方法
mGestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
Toast.makeText(this@MainActivity, "你按下了", Toast.LENGTH_SHORT).show()
return true//true表示继续接收后续事件,比如滚动,滑动等
}
override fun onSingleTapUp(e: MotionEvent): Boolean {
Toast.makeText(this@MainActivity, "你松开了", Toast.LENGTH_SHORT).show()
return true
}
})
override fun onTouchEvent(event: MotionEvent?): Boolean {
event?.let {
// 将事件传递给GestureDetector处理
if (mGestureDetector.onTouchEvent(it)) {
return true
}
}
return super.onTouchEvent(event)
}
3.实现GestureDetector.OnDoubleTapListener接口:onDoubleTap(双击),onSingleTapConfirmed(严格的单击,后面不能跟另外的单击),onDoubleTapEvent(双击,双击过程中的任何MotionEvent都会触发这个回调)
1.2 View的滑动
1.2.1 scrollTo/scrollBy
private var scrollX = 0
private var scrollY = 0
mGestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
// 使用 scrollBy 实现跟随手指滚动
binding.root.scrollBy(distanceX.toInt(), distanceY.toInt())
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
// 快速滑动动画 - 方向应与手势一致
// 使用 Scroller 实现更平滑的动画效果
// 这里简化处理
scrollX -= (velocityX / 50).toInt() // 调整系数控制滑动距离
scrollY -= (velocityY / 50).toInt()
binding.root.scrollTo(scrollX, scrollY)
return true
}
})
1.2.2 使用动画
binding.btn.setOnClickListener {
moveViewWithPropertyAnimator(binding.btn)
}
}
private fun moveViewWithPropertyAnimator(view: View) {
view.animate()//获取 View 的 ViewPropertyAnimator 实例
.translationX(300f) // 移动到 X=300 位置
.translationY(200f) // 移动到 Y=200 位置
.setDuration(1000) // 动画持续时间
.setInterpolator(AccelerateDecelerateInterpolator()) // 插值器,设置动画的变化速率
.start()
}
1.2.3 修改布局参数:
如果想让一个button向右平移100px,那我可以在button放一个空的view,只需要设置空的view宽度为100px,button自动会被挤到右边
1.3 View的事件分发机制
MotionEvent事件的分发过程:当MotionEvent产生后,系统要把这个事件传递给一个具体的View,这个传递的过程就是分发过程。
分发过程需要三个重要方法共同参与:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent
只要事件可以传递给当前View,那么dispatchTouchEvent一定会被调用
fun dispatchTouchEvent(ev:MotionEvent):Boolean{
val consume:Boolean=false
if(onInterceptTouchEvent(ev)){
consume=onTouchEvent(ev)
}else{
consume=child.dispatchTouchEvent(ev)
}
}
onTouchListener>onTouchEvent>onClickListener
同一个事件序列:从down到up的整个过程,中间包含了数量不定的move
点击事件的传递顺序:Activity->Windows->View
1.如果View拦截了某个事件,那么在同一个事件序列当中,onIntercepTouchEvent将不会被再次调用
2.如果在onTouchEvent中,View不消耗当前点击事件,那么在同一个事件序列中,当前view将无法再次收到事件
3.如果一个View拦截了点击事件,但是onTouchEvent返回了false(没有消耗ACTION_DOWN事件),那么就会调用父容器的onTouchEvent,如果还是返回false,那么就一层层往上传递,最后传递给Activity处理
4.一般来说,一个事件序列只能被一个View被处理,除非这个View在onTouchEvent中强行把自己的事件传递给其他View
5.如果View不消耗除了ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent不会被调用,并且当前的View可以持续收到后续的事件,最终消失的点击事件会传递给Activity处理 一个完整的触摸事件序列:ACTION_DOWN → ACTION_MOVE* → ACTION_UP/ACTION_CANCEL 一旦某个View的onTouchEvent返回true消费了ACTION_DOWN,系统会记住这个View后续的事件会直接传递给这个View,不会重新走分发流程
View 不消耗 ACTION_DOWN 以外的其他事件的情况:
-
ACTION_DOWN:
- View.onTouchEvent() 返回 true (消费了)
- 系统记住这个View作为事件处理者
-
ACTION_MOVE:
- 直接传递给该View
- View.onTouchEvent() 返回 false (不消费)
- 事件不会传递给父View的onTouchEvent()
- 但View仍会继续收到后续事件
-
最终:
- 未处理的事件传递给Activity
6.ViewGroup默认不会拦截任何事件,即onInterceptTouchEvent默认返回false
7.View没有onInterceptTouchEvent,因为View是ui层级的最底层,没有子View需要保护,只要有事件传递给他,onTouchEvent就会被调用
8.View的onTouchEvent默认会消耗事件,除非是不可点击的(clickable和longClickable都为false)
9.事件传递是由外向内的,父元素分发给子View,使用requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素事件的分发过程,但是除了ACTION_DOWN外
2.View的工作原理
2.1 ViewRoot和DecorView
ViewRoot的实现类是ViewRootImpl类,他是连接WindowManager和DecorView的纽带
当Acitivity对象被创建完毕后,会把DecorView添加到Window中,同时会创建ViewRootImpl对象,并且把ViewRootImpl和DecorView建立联系
1.流程从ViewRoot的performTraversals开始,依次调用:performMeasure,performLayout和performDraw。
这三个方法分别完成了顶级View的measure,layout,draw的流程
2.performMeasure- >measure->onMeasure 在onMeasure中又会对所有的子元素完成measure流程,传递下去就完成了对整个View树的遍历
3.performLayout和performDraw也类似,不过performDraw->draw->dispatchDraw ,本质上没有区别
只有draw方法完成后,View的内容才能显示在屏幕上,
4.DecorView是顶级的View,本质就是一个FrameLayout,内部包含一个竖直的LinearLayout(标题栏+内容栏)
2.2 MeasureSpec
MeasureSpec 是 Android 中用于封装 View 测量规格的工具类,包含测量模式和尺寸大小两部分信息,用来在测量过程中指导 View 如何确定自己的尺寸。
MeasureSpec为了避免过多的对象内存分配以及方便操作,将SpecMode(高2位)和SpecSize(低30位)打包成一个int值(32位),SpecMode和SpecSize也是int
SpecMode分类
LayoutParams 是 Android 中用于描述 View 在父容器中布局参数的类,它定义了 View 的尺寸、位置以及其他布局相关属性。每个 View 都必须有一个与之关联的 LayoutParams。
对于普通的View:根据父容器施加的规则将当前View的LayoutParams转换成对应的MeasureSpec,然后根据measureSpec测量出View的宽/高(测量)(也就是说,View的MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams共同决定的,MeasureSpec一旦确定,onMeasure就可以确定View的测量宽/高)
对于DecorView:根据他的LayoutParams的规则:LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 固定大小
3.View的工作流程
View的三大绘制流程:测量流程(measure),布局流程(layout),绘制流程(draw)
measure:确定View的测量宽/高 layout:确定View的最终宽/高和四个顶点的位置 draw:负责把View绘制在屏幕上
3.1 measure
3.1.1 View的measure
measure- >onMeasure->getDefaultSize-> 返回measureSpec的specSize,specSize就是View测量后的大小
3.1.2 ViewGroup的measure
没有重写onMeasure,而是提供了measureChild,取出子元素的LayoutParams创建子元素的MeasureSpec,再把MeasureSpec直接传给View的measure去测量
measure过程和Activity的生命周期不是同步的
3.2 layout
layout->确定四个顶点,再调用onLayout->onLayout遍历所有子元素,再调用子元素中的layout
一般来说,测量宽/高和最终宽/高都是相等的
3.3 draw
1.绘制背景background.draw(canvas)
2.绘制自己(onDraw)
3.绘制子元素(dispatchDraw)//绘制过程的传递是通过dispatchDraw实现,
diapatchDraw-> 遍历所有子元素,调用子元素的draw方法
4.绘制装饰(onDrawScollBars)
更多关于HarmonyOS 鸿蒙Next中View的事件体系和工作流程的实战教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS Next中View的事件体系基于事件分发机制,通过事件源、事件对象和事件监听器协同工作。事件从Window开始,依次经过根View、父View到目标View的传递流程,支持事件拦截和处理。工作流程包括事件采集、分发、消费和回调,最终由系统统一管理事件生命周期。
更多关于HarmonyOS 鸿蒙Next中View的事件体系和工作流程的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,View的事件体系和工作流程是其UI框架的核心,与Android有相似之处但也有其独特的设计。以下是对您所提内容的专业解读:
1. View的事件体系
1.1 事件分发机制
HarmonyOS Next的事件分发同样遵循从外到内的传递逻辑,核心方法包括:
- dispatchTouchEvent:负责事件分发。
- onInterceptTouchEvent(仅ViewGroup):决定是否拦截事件。
- onTouchEvent:处理事件。
事件传递顺序为:Ability → Window → View。与Android类似,ViewGroup默认不拦截事件,而View没有拦截方法。
1.2 触摸事件处理
- MotionEvent:包含
ACTION_DOWN、ACTION_MOVE、ACTION_UP等事件类型,通过getX/getY获取相对坐标。 - VelocityTracker:用于计算滑动速度,需在事件结束后释放资源。
- GestureDetector:支持手势识别(如单击、长按、滑动),可通过
SimpleOnGestureListener简化实现。
1.3 View的滑动
- scrollTo/scrollBy:通过改变View的滚动位置实现滑动,但注意滚动的是内容而非View本身。
- 属性动画:使用
animate()方法实现平滑移动,支持插值器控制动画效果。 - 布局参数:通过修改
LayoutConfig(类似Android的LayoutParams)调整View位置。
2. View的工作原理
2.1 测量与布局
- MeasureSpec:封装测量模式和尺寸,由父容器和View自身的布局参数共同决定。模式包括:
EXACTLY:精确尺寸。AT_MOST:最大尺寸。UNSPECIFIED:无限制。
- 测量流程:从根View开始递归调用
measure()和onMeasure(),确定View的测量宽高。 - 布局流程:通过
layout()和onLayout()确定View的最终位置和大小。
2.2 绘制流程
绘制顺序遵循:
- 绘制背景。
- 绘制自身内容(
onDraw)。 - 绘制子View(
dispatchDraw)。 - 绘制装饰(如滚动条)。
3. 与Android的差异点
- 组件命名:HarmonyOS Next中使用
Component作为基类,而非View,但事件体系类似。 - 布局参数:使用
LayoutConfig替代LayoutParams。 - API调用:部分方法名和参数可能不同,需参考HarmonyOS官方文档。
总结
HarmonyOS Next的事件分发和工作流程继承了经典的UI框架设计,开发者可借助Android经验快速上手。重点在于理解事件传递的递归性质、测量布局的协作机制以及绘制流程的层次顺序。在实际开发中,合理使用手势识别和动画API能提升交互体验。

