加入收藏 | 设为首页 | 会员中心 | 我要投稿 辽源站长网 (https://www.0437zz.com/)- 云专线、云连接、智能数据、边缘计算、数据安全!
当前位置: 首页 > 站长资讯 > 评论 > 正文

Android 绘制原理浅析「干货」

发布时间:2019-08-05 23:09:52 所属栏目:评论 来源:Newpaper
导读:背景 对于Android开发,在面试的时候,经常会被问到,说一说View的绘制流程?我也经常问面试者,View的绘制流程. 对于3年以上的开发人员来说,就知道onMeasure/onLayout/onDraw基本,知道他们呢是干些什么的,这样就够了吗? 如果你来我们公司,我是你的面试

看一下mTraversalRunnable

  1. final class TraversalRunnable implements Runnable { 
  2.  @Override 
  3.  public void run() { 
  4.  doTraversal(); 
  5.  } 
  6.  } 
  7. final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 
  8.   
  9.  void doTraversal() { 
  10.  if (mTraversalScheduled) { 
  11.  mTraversalScheduled = false; 
  12.  mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 
  13.  performTraversals(); 
  14.  } 
  15.  }  

在TraversalRunnable中,执行doTraversal.并在doTraversal执行performTraversals(),是不是看到了我们熟悉的performTraversals()了?是的,在这里才开始View的绘制工作.

在ViewRootImpl中的performTraversals(),这个方法代码很长(大约800行代码),大致流程是

  1. 判断是否需要重新计算视图大小,如果需要就执行performMeasure()
  2. 是否需要重新安置所在的位置,performLayout()
  3. 是否需要重新绘制performDraw()

那么是什么导致View的重绘呢?这里总结了3个主要原因

  1. 视图本身内部状态(enable,pressed等)变化,可能引起重绘
  2. View内部添加或者删除了View
  3. View本身的大小和可见性发生了变化

View的绘制流程

在上一小节了,讲述了performTraversals()的是被WMS IPC调用执行的.View的绘制流程一般是

从performTraversals -> performMeasure() -> performLayout() -> performDraw().

下面看一下performMeasure()

  1. //ViewRootImpl 
  2. private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 
  3.  if (mView == null) { 
  4.  return; 
  5.  } 
  6.  Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 
  7.  try { 
  8.  mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
  9.  } finally { 
  10.  Trace.traceEnd(Trace.TRACE_TAG_VIEW); 
  11.  } 
  12.  } 
  13.   
  14.  public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 
  15.  MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY 
  16.  && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; 
  17.  final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) 
  18.  && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); 
  19.  final boolean needsLayout = specChanged 
  20.  && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize); 
  21.  if (forceLayout || needsLayout) { 
  22.  mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; 
  23.  resolveRtlPropertiesIfNeeded(); 
  24.  int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); 
  25.  if (cacheIndex < 0 || sIgnoreMeasureCache) { 
  26.  //在这里调用了onMeasure 方法 
  27.  onMeasure(widthMeasureSpec, heightMeasureSpec); 
  28.  mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; 
  29.  }  
  30.  } 
  31.  } 

最终调用了View的measure方法,而View中的measure()方法被定义成final类型,保证整个流程的执行.performLayout()和performDraw()也是类似的过程.

而对于程序员,自定义View只需要关注他提供出来几个对应的方法,onMeasure/onLayout/onDraw. 关于这方面知识的网上介绍的资料很多,也可以很容易的看到View及ViewGroup里面的代码,推荐看LinerLayout的源码理解这部分知识,在这里不详细展开.

Android的绘图原理浅析

Android屏幕绘制

关于绘制,就要从performDraw()说起,我们来看一下这个流程到底是怎么绘制的.

  1. //ViewRootImpl 
  2. //1 
  3.  private void performDraw() { 
  4.  try { 
  5.  draw(fullRedrawNeeded); 
  6.  } finally { 
  7.  mIsDrawing = false; 
  8.  Trace.traceEnd(Trace.TRACE_TAG_VIEW); 
  9.  } 
  10.  } 
  11.   
  12.  //2 
  13.  private void draw(boolean fullRedrawNeeded) { 
  14.  Surface surface = mSurface; 
  15.  if (!surface.isValid()) { 
  16.  return; 
  17.  } 
  18.   
  19.  if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { 
  20.  return; 
  21.  } 
  22.  } 
  23.   
  24.  //3 
  25.  private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, 
  26.  boolean scalingRequired, Rect dirty) { 
  27.  Canvas canvas = mSurface.lockCanvas(dirty); 
  28.  }  

看代码执行流程,1—>2->3, 最终拿到了Java层的canvas,然后进行一系列绘制操作.而canvas是通过Suface.lockCanvas()得到的.

(编辑:辽源站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读