一、事件传递机制
1. 核心方法
java// ViewGroup的三个关键方法 public boolean dispatchTouchEvent(MotionEvent ev) // 分发事件 public boolean onInterceptTouchEvent(MotionEvent ev) // 拦截事件 public boolean onTouchEvent(MotionEvent event) // 消费事件 // View的两个关键方法 public boolean dispatchTouchEvent(MotionEvent ev) public boolean onTouchEvent(MotionEvent event)2. 传递流程
自上而下分发,自下而上处理
ACTION_DOWN 决定事件链:
Activity → Window → DecorView → ... → 子View
任一View的
onTouchEvent()返回true,则成为目标视图(TARGET)ACTION_MOVE/UP 跟随事件链:
- 直接发送给TARGET,不再进行拦截判断
3. 关键规则
// 1. 拦截器规则 // 父View一旦拦截(返回true),后续事件(MOVE/UP)不再询问,直接给父View处理 // 但子View可以调用requestDisallowInterceptTouchEvent()禁止父View拦截 // 2. 消费规则 // onTouchListener > onTouchEvent > onClickListener // 优先级:setOnTouchListener > 自身onTouchEvent > setOnClickListener // 3. 事件序列 // DOWN → MOVE* → UP/CANCEL (一个完整的触摸序列)4. 实战技巧
// 解决滑动冲突的核心代码 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = ev.getX(); mLastY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float deltaX = Math.abs(ev.getX() - mLastX); float deltaY = Math.abs(ev.getY() - mLastY); // 水平滑动距离大于垂直,父View拦截 if (deltaX > deltaY && deltaX > mTouchSlop) { return true; // 拦截事件 } break; } return super.onInterceptTouchEvent(ev); }
二、View绘制流程
1. 三个阶段总览
// 三大流程在ViewRootImpl.performTraversals()中触发 performMeasure() // 测量: 确定View的宽高(MeasureSpec) performLayout() // 布局: 确定View的位置 performDraw() // 绘制: 渲染到屏幕2. MeasureSpec机制
// MeasureSpec = mode + size (32位int, 前2位mode, 后30位size) public static class MeasureSpec { public static final int UNSPECIFIED = 0 << MODE_SHIFT; // 父View不限制(如ScrollView) public static final int EXACTLY = 1 << MODE_SHIFT; // 精确值(match_parent/具体dp) public static final int AT_MOST = 2 << MODE_SHIFT; // 最大限制(wrap_content) } // 在onMeasure中必须使用MeasureSpec.getMode()和getSize() @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); // ... 计算最终宽高 setMeasuredDimension(width, height); }3. 绘制流程详解
// 1. MEASURE阶段 ViewRootImpl.performMeasure() → View.measure() → onMeasure() // 子类必须实现 // 对于ViewGroup,需遍历measure所有子View → measureChildren() → child.measure() // 2. LAYOUT阶段 performLayout() → View.layout(l, t, r, b) // 设置最终位置 → onLayout() // ViewGroup必须实现,摆放子View // 3. DRAW阶段 performDraw() → View.draw(Canvas) 1. drawBackground() // 绘制背景 2. onDraw() // 绘制自身内容(重写此方法) 3. dispatchDraw() // ViewGroup绘制子View 4. onDrawForeground() // 绘制装饰(scrollBar等)4. 性能优化
// 1. 避免过度绘制 // 2. 在onDraw中避免创建对象 @Override protected void onDraw(Canvas canvas) { // 错误:频繁创建Paint // Paint paint = new Paint(); // 正确:复用对象 canvas.drawRect(mRect, mPaint); } // 3. 使用ViewStub延迟加载 // 4. 使用merge标签减少层级
三、Handler机制
1. 核心组件
// 1. Looper: 循环读取消息队列 // 2. MessageQueue: 消息队列(底层为链表, native实现) // 3. Handler: 发送和处理消息 // 4. Message: 消息载体2. 工作原理
// 主线程Looper初始化(ActivityThread.main) Looper.prepareMainLooper(); // 创建Looper和MessageQueue // Looper.loop() 死循环开始工作 // Loop循环核心逻辑 for (;;) { Message msg = queue.next(); // 可能阻塞 if (msg == null) return; // 退出循环 msg.target.dispatchMessage(msg); // target就是Handler msg.recycleUnchecked(); // 消息回收 }3. 内存泄漏与解决
// 错误写法:隐式持有外部类引用 private Handler mHandler = new Handler() { public void handleMessage(Message msg) { // 隐式持有Activity引用 } }; // 正确写法1:静态内部类 + WeakReference private static class MyHandler extends Handler { private WeakReference<Activity> mRef; MyHandler(Activity activity) { mRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = mRef.get(); if (activity != null) { // 处理消息 } } } // 正确写法2:View.post() + 生命周期移除 @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 移除所有回调 }4. 高级应用
// 1. IdleHandler: 主线程空闲时执行 Looper.myQueue().addIdleHandler(() -> { // 延迟初始化、埋点上报等 return false; // true表示保留,false执行后移除 }); // 2. Barrier(同步屏障): 优先处理异步消息 // 用于UI绘制,确保VSync信号优先处理 MessageQueue.postSyncBarrier(); // 3. HandlerThread: 自带Looper的子线程 HandlerThread thread = new HandlerThread("Worker"); thread.start(); Handler workerHandler = new Handler(thread.getLooper());
四、动画机制
1. 三种动画对比
类型 View Animation Property Animation Transition API Tween/Frame ObjectAnimator/ValueAnimator Transition API 特点 仅视觉变化 真实改变属性 场景切换 性能 较差 好 好 适用 简单效果 复杂交互 Activity/Fragment切换 2. 属性动画核心
// 1. ObjectAnimator: 直接修改属性 ObjectAnimator animator = ObjectAnimator.ofFloat( view, "translationX", 0f, 100f ); animator.setDuration(300); animator.setInterpolator(new FastOutSlowInInterpolator()); animator.start(); // 2. ValueAnimator: 自定义属性 ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100); valueAnimator.addUpdateListener(animation -> { int value = (int) animation.getAnimatedValue(); // 手动更新属性 view.getLayoutParams().height = value; view.requestLayout(); }); // 3. AnimatorSet: 组合动画 AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.5f), ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.5f) ); set.setDuration(500).start();3. 硬件加速与离屏缓冲
// 开启硬件加速 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); // 动画结束后关闭(重要!避免内存泄漏) animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(View.LAYER_TYPE_NONE, null); } }); // 离屏缓冲优化复杂动画 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); // 关闭硬件加速,兼容某些Canvas操作4. 过渡动画(Transition)
// 1. Activity转场动画 ActivityCompat.startActivity(this, intent, ActivityOptionsCompat.makeSceneTransitionAnimation( this, sharedView, "transition_name" ).toBundle() ); // 2. Fragment过渡 fragment.setSharedElementEnterTransition(new ChangeBounds()); fragment.setEnterTransition(new Fade()); // 3. 布局变化过渡 TransitionManager.beginDelayedTransition(rootView); view.setVisibility(View.GONE); // 自动执行消失动画5. 性能优化
java// 1. 避免在动画中触发Layout // 使用translationX/Y代替layout(left,top,right,bottom) // 2. 使用ViewPropertyAnimator简化代码 view.animate() .translationX(100) .alpha(0.5f) .setDuration(300) .withEndAction(() -> { /* 结束回调 */ }); // 3. 批量更新属性 view.animate().translationX(100).translationY(100); // 一次动画