欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android View刷新机制实例分析

程序员文章站 2024-02-25 17:35:39
本文实例讲述了android view刷新机制。分享给大家供大家参考,具体如下: 一、总体说明 在android的布局体系中,父view负责刷新、布局显示子view;而...

本文实例讲述了android view刷新机制。分享给大家供大家参考,具体如下:

一、总体说明

在android的布局体系中,父view负责刷新、布局显示子view;而当子view需要刷新时,则是通知父view来完成。

二、代码分析

1).viewgroup的addview方法,理解参数的意义和传递

invalidate调用父类view的方法
addviewinner方法主要做的事情是

view的dispatchattachedtowindow(attachinfo info, int visibility)方法

1).view的invalidate方法,这是一个从下第向上回溯的过程,每一层的父view都将自己的显示区域与传入的刷新

rect做交集。

void invalidate(boolean invalidatecache) {
    if (viewdebug.trace_hierarchy) {
      viewdebug.trace(this, viewdebug.hierarchytracetype.invalidate);
    }
    if (skipinvalidate()) {
      return;
    }
    if ((mprivateflags & (drawn | has_bounds)) == (drawn | has_bounds) ||
        (invalidatecache && (mprivateflags & drawing_cache_valid) == drawing_cache_valid) ||
        (mprivateflags & invalidated) != invalidated || isopaque() != mlastisopaque) {
      mlastisopaque = isopaque();
      mprivateflags &= ~drawn;
      mprivateflags |= dirty;
      if (invalidatecache) {
        mprivateflags |= invalidated;
        mprivateflags &= ~drawing_cache_valid;
      }
      final attachinfo ai = mattachinfo;
      final viewparent p = mparent;
      //noinspection pointlessbooleanexpression,constantconditions
      if (!hardwarerenderer.render_dirty_regions) {
        if (p != null && ai != null && ai.mhardwareaccelerated) {
          // fast-track for gl-enabled applications; just invalidate the whole hierarchy
          // with a null dirty rect, which tells the viewancestor to redraw everything
          p.invalidatechild(this, null);
          return;
        }
      }
      if (p != null && ai != null) {
        final rect r = ai.mtmpinvalrect;
        r.set(0, 0, mright - mleft, mbottom - mtop);
        // don't call invalidate -- we don't want to internally scroll
        // our own bounds
        p.invalidatechild(this, r);//调用子类的方法完成
      }
    }
  }

2)viewgrop的invalidatechild方法

public final void invalidatechild(view child, final rect dirty) {
  viewparent parent = this;
  final attachinfo attachinfo = mattachinfo;
  if (attachinfo != null) {
    final int[] location = attachinfo.minvalidatechildlocation;
    // 需要刷新的子view的位置 
    location[child_left_index] = child.mleft;
    location[child_top_index] = child.mtop;
    // if the child is drawing an animation, we want to copy this flag onto
    // ourselves and the parent to make sure the invalidate request goes through
    final boolean drawanimation = (child.mprivateflags & draw_animation) == draw_animation;
    // check whether the child that requests the invalidate is fully opaque
    final boolean isopaque = child.isopaque() && !drawanimation && child.getanimation() != null;
    // mark the child as dirty, using the appropriate flag
    // make sure we do not set both flags at the same time
    final int opaqueflag = isopaque ? dirty_opaque : dirty;
    do {
      view view = null;
      if (parent instanceof view) {
        view = (view) parent;
      }
      if (drawanimation) {
        if (view != null) {
            view.mprivateflags |= draw_animation;
        } else if (parent instanceof viewroot) {
            ((viewroot) parent).misanimating = true;
        }
      }
        // if the parent is dirty opaque or not dirty, mark it dirty with the opaque
        // flag coming from the child that initiated the invalidate
      if (view != null && (view.mprivateflags & dirty_mask) != dirty) {
        view.mprivateflags = (view.mprivateflags & ~dirty_mask) | opaqueflag;
      }
      parent = parent.invalidatechildinparent(location, dirty);
    } while (parent != null);
  }
}
public viewparent invalidatechildinparent(final int[] location, final rect dirty) {
  if ((mprivateflags & drawn) == drawn) {
    if ((mgroupflags & (flag_optimize_invalidate | flag_animation_done)) !=
            flag_optimize_invalidate) {
      // 根据父view的位置,偏移刷新区域 
      dirty.offset(location[child_left_index] - mscrollx, location[child_top_index] - mscrolly);
      final int left = mleft;
      final int top = mtop;
      //计算实际可刷新区域 
      if (dirty.intersect(0, 0, mright - left, mbottom - top) ||
            (mprivateflags & draw_animation) == draw_animation) {
        mprivateflags &= ~drawing_cache_valid;
        location[child_left_index] = left;
        location[child_top_index] = top;
        return mparent;
      }
    } else {
      mprivateflags &= ~drawn & ~drawing_cache_valid;
      location[child_left_index] = mleft;
      location[child_top_index] = mtop;
      dirty.set(0, 0, mright - location[child_left_index],
            mbottom - location[child_top_index]);
        return mparent;
      }
    }
    return null;
}

这个向上回溯的过程直到viewroot那里结束,由viewroot对这个最终的刷新区域做刷新

viewroot.java

public void invalidatechild(view child, rect dirty) {
}

由viewroot对象的performtraversals()方法调用draw()方法发起绘制该view树,值得注意的是每次发起绘图时,并不会重新绘制每个view树的视图,而只会重新绘制那些“需要重绘”的视图,view类内部变量包含了一个标志位drawn,当该视图需要重绘时,就会为该view添加该标志位。

调用流程

mview.draw()开始绘制,draw()方法实现的功能如下:

1 、绘制该view的背景
2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)
3、调用ondraw()方法绘制视图本身   (每个view都需要重载该方法,viewgroup不需要实现该方法)
4、调用dispatchdraw ()方法绘制子视图(如果该view类型不为viewgroup,即不包含子视图,不需要重载该
方法)值得说明的是,viewgroup类已经为我们重写了dispatchdraw ()的功能实现,应用程序一般不需要重写该
方法,但可以重载父类函数实现具体的功能。

4.1 dispatchdraw()方法内部会遍历每个子视图,调用drawchild()去重新回调每个子视图的draw()方法(注意,这个 地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,viewgroup类已经为我们重写了dispatch

draw()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

更多关于android相关内容感兴趣的读者可查看本站专题:《android开发入门与进阶教程》、《android多媒体操作技巧汇总(音频,视频,录音等)》、《android基本组件用法总结》、《android视图view技巧总结》、《android布局layout技巧总结》及《android控件用法总结

希望本文所述对大家android程序设计有所帮助。