浅谈Android View绘制三大流程探索及常见问题

View绘制的三大流程,指的是measure(测量)、layout(布局)、draw(绘制)

measure负责确定View的测量宽/高,也就是该View需要占用屏幕的大小,确定完View需要占用的屏幕大小后,就会通过layout确定View的最终宽/高和四个顶点在手机界面上的位置,等通过measure和layout过程确定了View的宽高和要显示的位置后,就会执行draw绘制View的内容到手机屏幕上。

在详细介绍这三大流程之前,需要简单了解一下ViewRootImpl,View绘制的三大步骤都是通过ViewRootImpl实现的,ViewRootImpl是连接WindowManager窗口管理和DecorView顶层视图的纽带。View的绘制流程从ViewRootImpl的performTraversals方法开始,顺序执行measure、layout、draw这三个流程,最终完成对View的绘制工作,在performTraversals方法中,会调用measure、layout、draw这三个方法,这三个方法内部也会调用其对应的onMeasure、onLayout、onDraw方法,通常我们在自定义View时,也就是重写的这三个方法来实现View的具体绘制逻辑

下面详细了解下各个步骤经历的主要方法(这里贴的源码版本为API 23)

一、measure

在performTraversals方法中,第一个需要进行的就是measure过程,获取到必要信息后,performTraversals方法中首先会调用measureHierarchy方法,接着measureHierarchy方法里再去调用performMeasure方法,在performMeasure方法中最终就会去调用View的measure方法,从而开始进行测量过程

 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
  Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
  try {
   mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  } finally {
   Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  }
 }

mView其实指的就是DecorView顶层视图,从源码可以看出,measure的递归过程就是从DecorView开始的

View和ViewGroup的测量方法有一定区别,View通过measure方法就可以完成自身的测量过程,而ViewGroup不仅需要调用measure方法测量自己,还需要去遍历其子元素的measure方法,其子元素如果是ViewGroup,则该子元素需使用同样的方法再次递归下去。

View

来看看View是如何测量自己的宽高的

先在View源码中找到measure方法

 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  // ......

  if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
    widthMeasureSpec != mOldWidthMeasureSpec ||
    heightMeasureSpec != mOldHeightMeasureSpec) {
   // ......
   if (cacheIndex < 0 || sIgnoreMeasureCache) {
    // measure ourselves, this should set the measured dimension flag back
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
   }
   // .....
 }

View的measure过程就是通过measure方法来完成,View中的measure方法是由ViewGroup的measureChild方法调用的,ViewGroup在调用该子View的measure方法的同时还传入了子View的widthMeasureSpec和heightMeasureSpec值。该方法被定义为final类型,也就是说其measure过程是固定的,在measure中调用了onMeasure方法,如果想要自定义测量过程的话,需要重写onMeasure方法。

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 }

Google在介绍该方法的时候也说了

Measure the view and its content to determine the measured width and the measured height. This method is invoked by {@link #measure(int, int)} and should be overridden by subclasses to provide accurate and efficient measurement of their contents.

该方法需要被子类覆盖,让子类提供精准、有效的测量数据,所以我们一般在进行自定义View开发时,需要自定义测量过程就需要复写此方法。

setMeasuredDimension方法的作用就是设置View的测量宽高,其实我们在使用getMeasuredWidth/getMeasuredHeight 方法获取的宽高值就是此处设置的值。

如果不复写此onMeasure方法,则默认使用getDefaultSize方法得到的值。

public static int getDefaultSize(int size, int measureSpec) {
  int result = size;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec);

  switch (specMode) {
  case MeasureSpec.UNSPECIFIED:
   result = size;
   break;
  case MeasureSpec.AT_MOST:
  case MeasureSpec.EXACTLY:
   result = specSize;
   break;
  }
  return result;
 }

可以发现,传入的measureSpec数值被MeasureSpec解析成了对应的数据,这里简单介绍下MeasureSpec,它的作用就是告诉View应该以哪一种模式测量这个View,SpecMode有三种模式:

• UNSPECIFIED:表示父容器不对View有任何限制,这种模式主要用于系统内部多次Measure的情况,不需要过多关注

• AT_MOST:父容器已经指定了大小,View的大小不能大于这个值,相当于布局中使用的wrap_content模式

• EXACTLY:表示View已经定义了精确的大小,使用这个指定的精确大小specSize作为该View的大小,相当于布局中我们指定了66dp这种精确数值或者match_parent模式

传入的measureSpec值经过MeasureSpec.getMode方法获取它的测量模式,MeasureSpec.getSize方法获取对应模式下的规格大小,从而确定了其最终的测量大小。

ViewGroup

ViewGroup是一个继承至View的抽象类,ViewGroup没有实现测量自己的具体过程,因为其过程是需要各个子类根据自己的需要再具体实现,比如LinearLayout、RelativeLayout等布局的特性都是不同的,不能统一的去管理,所以就交给其子类自己去实现

ViewGroup在measure时,除了实现自身的测量,还需要对它的每个子元素进行measure,在ViewGroup内部提供了一个measureChildren的方法

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
  final int size = mChildrenCount;
  final View[] children = mChildren;
  for (int i = 0; i < size; ++i) {
   final View child = children[i];
   if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
    measureChild(child, widthMeasureSpec, heightMeasureSpec);
   }
  }
 }

其中,mChilderenCount指的是该ViewGroup所拥有的子元素的个数,通过一个for循环调用measureChild方法来测量其所有子元素

protected void measureChild(View child, int parentWidthMeasureSpec,
   int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();

final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
    mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
    mPaddingTop + mPaddingBottom, lp.height);

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

该方法先通过child.getLayoutParams方法取得子元素的LayoutParams,然后调用getChildMeasureSpec方法计算出该子元素正确的MeasureSpec,再使用child.measure方法把这个MeasureSpec传递给View进行测量。

通过这一系列过程,就能让各个子元素依次进入measure了

二、layout

通过之前的measure过程,View已经测量出了自己需要的宽高大小,performTraversals方法接下来就会执行layout过程

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

layout的过程主要是用来确定View的四个顶点所在屏幕上的位置

layout过程首先从View中的layout方法开始

public void layout(int l, int t, int r, int b) {
  if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
   onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
   mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  }

  int oldL = mLeft;
  int oldT = mTop;
  int oldB = mBottom;
  int oldR = mRight;

boolean changed = isLayoutModeOptical(mParent) ?
    setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
   onLayout(changed, l, t, r, b);
   mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

   ListenerInfo li = mListenerInfo;
   if (li != null && li.mOnLayoutChangeListeners != null) {
    ArrayList<OnLayoutChangeListener> listenersCopy =
      (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
    int numListeners = listenersCopy.size();
    for (int i = 0; i < numListeners; ++i) {
     listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
    }
   }
  }

  mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
  mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

layout(int l, int t, int r, int b)方法里的四个参数分别指的是左、上、右、下的位置,这四个值是通过ViewRootImpl类里的performTraversals方法传入的

layout方法用来确定View自身的位置,mLeft、mTop、mBottom、mRight的值最终会由setOpticalFrame和setFrame方法确定,其实setOpticalFrame内部最后也是通过调用setFrame方法设置的

 private boolean setOpticalFrame(int left, int top, int right, int bottom) {
  Insets parentInsets = mParent instanceof View ?
    ((View) mParent).getOpticalInsets() : Insets.NONE;
  Insets childInsets = getOpticalInsets();
  return setFrame(
    left + parentInsets.left - childInsets.left,
    top + parentInsets.top - childInsets.top,
    right + parentInsets.left + childInsets.right,
    bottom + parentInsets.top + childInsets.bottom);
 }

确定完View的四个顶点位置后,就相当于View在父容器中的位置被确定了,接下来会调用onLayout方法,这个方法是没有具体实现的

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
 }

和ViewGroup的onMeasure类似,onLayout方法的具体实现也是需要根据各个View或ViewGroup的特性来决定的,所以源码中是个空方法,有兴趣的可以去看看LinearLayout、RelativeLayout等实现了onLayout方法的ViewGroup子类

之前的measure过程,得到的是测量宽高,而通过onLayout方法,进一步确定了View的最终宽高,一般情况下,measure过程的测量宽高和layout过程确定的最终宽高是一样的

三、draw

经过以上步骤,View已经确定好了大小和屏幕中显示的位置,接着就可以绘制自身需要显示的内容了

在performTraversals方法中,会调用performDraw方法,performDraw方法中调用draw方法,draw方法中接着调用drawSoftware方法

 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
   boolean scalingRequired, Rect dirty) {

  // Draw with software renderer.
  final Canvas canvas;
  try {
   ......
   canvas = mSurface.lockCanvas(dirty);
  } 

  try {
   ......
   try {
    mView.draw(canvas);
   }
  }
 }

首先会通过lockCanvas方法取得一个Canvas画布对象,接着由mView(DecorView)顶层视图去调用View的draw方法,并传入一个Canvas画布对象

其实Google的工程师已经把draw的绘制过程注释的非常详细了

Draw traversal performs several drawing steps which must be executed

in the appropriate order:

1. Draw the background

2. If necessary, save the canvas' layers to prepare for fading

3. Draw view's content

4. Draw children

5. If necessary, draw the fading edges and restore layers

6. Draw decorations (scrollbars for instance)

1. 绘制View的背景

如果该View设置了背景,则绘制背景。此背景指的是我们在布局文件中通过android:background属性,或代码中使用setBackgroundResource、setBackgroundColor等方法设置的背景图片或背景颜色

  if (!dirtyOpaque) {
   drawBackground(canvas);
  }

dirtyOpaque属性用来判断该View是否是透明的,如果是透明的则不执行某些步骤,比如绘制背景,绘制内容等

2. 如果有必要的话,保存这个canvas画布,为该层边缘的fading效果作准备

第2步和第5步是配套的,我们一般不用管2和5,源码中的注释也说了,其中的2和5方法在通常情况下是直接跳过的(skip step 2 & 5 if possible (common case)),其主要作用是实现一些如同View滑动到边缘时产生的阴影效果,可以不用过多关注

3. 绘制View的内容

该步骤调用了onDraw方法,这个方法是一个空实现

 /**
  * Implement this to do your drawing.
  *
  * @param canvas the canvas on which the background will be drawn
  */
 protected void onDraw(Canvas canvas) {
 }

每个子View需要展示的内容肯定是不相同的,所以onDraw的详细过程需要子类自己去实现

4. 绘制子View

和第3步一样,此方法也是一个空实现

 /**
  * Called by draw to draw the child views. This may be overridden
  * by derived classes to gain control just before its children are drawn
  * (but after its own view has been drawn).
  * @param canvas the canvas on which to draw the view
  */
 protected void dispatchDraw(Canvas canvas) {

 }

对于单纯的View来说,它是没有子View的,所以不需要实现该方法,该方法主要是被ViewGroup重写了,找到ViewGroup中重写的dispatchDraw方法

 @Override
 protected void dispatchDraw(Canvas canvas) {
  ......
  for (int i = 0; i < childrenCount; i++) {
   while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
    final View transientChild = mTransientViews.get(transientIndex);
    if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
      transientChild.getAnimation() != null) {
     more |= drawChild(canvas, transientChild, drawingTime);
    }
    transientIndex++;
    if (transientIndex >= transientCount) {
     transientIndex = -1;
    }
   }
   int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
   final View child = (preorderedList == null)
     ? children[childIndex] : preorderedList.get(childIndex);
   if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
    more |= drawChild(canvas, child, drawingTime);
   }
  }
  ......
 }

在ViewGroup的dispatchDraw方法中通过for循环调用drawChild方法

 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
  return child.draw(canvas, this, drawingTime);
 }

drawChild方法里调用子视图的draw方法,从而让其子视图进入draw过程

5. 绘制View边缘的渐变褪色效果,类似于阴影效果

当第2个步骤保存了canvas画布后,就可以为这个画布实现阴影效果

6. 绘制View的装饰物

View的装饰物,指的是View除了背景、内容、子View的其它部分,比如滚动条这些

四、常见问题

1.在Activity中获取View的宽高,得到的值为0

通过上面的measure分析可以知道,View的measure过程和Activity的生命周期方法不是同步的,所以无法保证Activity的某个生命周期执行后View就一定能获取到值,当我们在View还没有完成measure过程就去获取它的宽高,当然获取不到了,解决这问题的方法有很多,这里推荐使用以下方法

(1)在View的post方法中获取:

这个方法简单快捷,推荐使用

  mView.post(new Runnable() {
   @Override
   public void run() {
    width = mView.getMeasuredWidth();
    height = mView.getMeasuredHeight();
   }
  });

post方法中传入的Runnable对象将会在View的measure、layout过程后触发,因为UI的事件队列是按顺序执行的,所以任何post到队列中的请求都会在Layout发生变化后执行。

(2)使用View的观察者ViewTreeObserver

ViewTreeObserver是视图树的观察者,其中OnGlobalLayoutListener监听的是一个视图树中布局发生改变或某个视图的可视状态发生改变时,就会触发此类监听事件,其中onGlobalLayout回调方法会在View完成layout过程后调用,此时是获取View宽高的好时机

 ViewTreeObserver observer = mView.getViewTreeObserver();
  observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    mView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    width = mScanIv.getMeasuredWidth();
    height = mScanIv.getMeasuredHeight();
   }
  });

使用这个方法需要注意,随着View树的状态改变,onGlobalLayout方法会被回调多次,所以在进入onGlobalLayout回调方法时,就移除这个观察者,保证onGlobalLayout方法只被执行一次就好了

(3)在onWindowFocusChanged回调中获取

此方法是在View已经初始化完成,measure和layout过程已经执行完成,UI视图已经渲染完成时被回调,此时View的宽高肯定也已经被确定了,这个时候就可以去获取View的宽高了

 @Override
 public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  if (hasFocus) {
   width = mView.getMeasuredWidth();
   height = mView.getMeasuredHeight();
  }
 }

这个方法在Activity界面发生变化时也会被多次回调,如果只需要获取一次宽高的话,建议加上标记加以限制

除了以上方法,还有其它的方法也能获取到宽高,比如在onClick方法中获取,手动调用measure方法,使用postDelayed等,了解了View绘制原理后,这些都是很容易就能理解的。

以上这篇浅谈Android View绘制三大流程探索及常见问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Android View 绘制流程(Draw)全面解析

    前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程--绘制流程.测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一个View该显示什么由绘制流程完成.以下源码均取自Android API 21. 从performDraw说起 前面几篇文章提到,三大工作流程始于ViewRootImpl#performTraversals,在这个方法内部会分别调用performMeasure

  • Android View如何绘制

    上文说道了Android如何测量,但是一个漂亮的控件我只知道您长到哪儿,这当然不行.只需要简单重写OnDraw方法,并在Canvas(画布)对象上调用那根五颜六色的画笔就能够画出这控件"性感"的外表.那么View又是如何进行绘制了? 要了解View如何绘制,就需要了解canvas(画布)是什么?paint(画笔)能够做什么. Ⅰ.canvas就是表示一块画布,你可以在上面画你所朝思暮想的东西.当我们重写onDraw方法的时候,就能够拿到一个Canvas对象,这个就是你的舞台,画你所思所

  • 深入理解Android中View绘制的三大流程

    前言 最近对Android中View的绘制机制有了一些新的认识,所以想记录下来并分享给大家.View的工作流程主要是指measure.layout.draw这三大流程,即测量.布局和绘制,其中measure确定View的测量宽高,layout根据测量的宽高确定View在其父View中的四个顶点的位置,而draw则将View绘制到屏幕上,这样通过ViewGroup的递归遍历,一个View树就展现在屏幕上了. 说的简单,下面带大家一步一步从源码中分析: Android的View是树形结构的: 基本概

  • Android自定义View实现绘制虚线的方法详解

    前言 说实话当第一次看到这个需求的时候,第一反应就是Canvas只有drawLine方法,并没有drawDashLine方法啊!这咋整啊,难道要我自己做个遍历不断的drawLine?不到1秒,我就放弃这个想法了,因为太恶心了.方法肯定是有的,只不过我不知道而已. 绘制方法 最简单的方法是利用ShapeDrawable,比如说你想用虚线要隔开两个控件,就可以在这两个控件中加个View,然后给它个虚线背景. 嗯,理论上就是这样子的,实现上也很简单. <!-- drawable 文件 --> <

  • Android视图的绘制流程(上) View的测量

    综述 View的绘制流程可以分为三大步,它们分别是measure,layout和draw过程.measure表示View的测量过程,用于测量View的宽度和高度:layout用于确定View在父容器的位置:draw则是负责将View绘制到屏幕中.下面主要来看一下View的Measure过程. 测量过程 View的绘制流程是从ViewRoot的performTraversals方法开始的,ViewRoot对应ViewRootImpl类.ViewRoot在performTraversals中会调用p

  • Android应用开发中View绘制的一些优化点解析

    一个通常的错误观念就是使用基本的布局结构(例如:LinearLayout.FrameLayout等)能够在大多数情况下    产生高效率 的布局. 显然,你的应用程序里添加的每一个控件和每一个布局都需要初始化.布局(layout).    绘制 (drawing).举例来说:嵌入一个LinearLayout会产生一个太深的布局层次.更严重的是,嵌入几个使    用 layout_weight属性的LinearLayout 将会导致大量的开销,因为每个子视图都需要被测量两次.这是反复解析    布

  • Android使用自定义View绘制渐隐渐现动画

    实现了一个有趣的小东西:使用自定义View绘图,一边画线,画出的线条渐渐变淡,直到消失.效果如下图所示: 用属性动画或者渐变填充(Shader)可以做到一笔一笔的变化,但要想一笔渐变(手指不抬起边画边渐隐),没在Android中找到现成的API可用.所以,自己做了一个. 基本的想法是这样的: 在View的onTouchEvent中记录触摸点,生成一条一条的线LineElement,放在一个List中.给每个LineElement配置一个Paint实例. 在onDraw中绘制线段. 变换LineE

  • 浅谈Android View绘制三大流程探索及常见问题

    View绘制的三大流程,指的是measure(测量).layout(布局).draw(绘制) measure负责确定View的测量宽/高,也就是该View需要占用屏幕的大小,确定完View需要占用的屏幕大小后,就会通过layout确定View的最终宽/高和四个顶点在手机界面上的位置,等通过measure和layout过程确定了View的宽高和要显示的位置后,就会执行draw绘制View的内容到手机屏幕上. 在详细介绍这三大流程之前,需要简单了解一下ViewRootImpl,View绘制的三大步骤

  • 浅谈Android View滑动冲突的解决方法

    引言 这一篇文章我们就通过介绍滑动冲突的规则和一个实例来更加深入的学习View的事件分发机制. 1.外部滑动方向和内部滑动方向不一致 考虑这样一种场景,开发中我们经常使用ViewPager和Fragment配合使用所组成的页面滑动效果,很多主流的应用都会使用这样的效果.在这种效果中,可以使用左右滑动来切换界面,而每一个界面里面往往又都是ListView这样的控件.本来这种情况是存在滑动冲突的,只是ViewPager内部处理了这种滑动冲突.如果我们不使用ViewPager而是使用ScrollVie

  • 浅谈Android截屏和指定View生成截图

    当前页面截图(截取整个屏幕) 截取当前Activity页面的截图,可以通过窗体最底层的decorView进行缓存,然后根据这个缓存对象生成一张图片.有的需要不需要状态栏,也可以指定生成图片的宽高,把状态栏去除. /** * 截取当前窗体的截图,根据[isShowStatusBar]判断是否包含当前窗体的状态栏 * 原理是获取当前窗体decorView的缓存生成图片 */ fun captureWindow(activity: Activity, isShowStatusBar: Boolean)

  • Android WindowManager深层理解view绘制实现流程

    目录 前言 setContentView()流程 WindowManager.addView流程 前言 又是一年一度的1024程序员节了,今天不写点什么总感觉对不起这个节日.想来想去,就写点关于View的绘制.本文不会重点讲View绘制三大回调函数:onMeasure.onLayout.onDraw,而是站在Android framework的角度去分析一下View的绘制. View是如何被渲染到屏幕中的? ViewRoot.DecorView.Activity.Window.WindowMan

  • Android view绘制流程详解

    绘制流程 measure 流程测量出 View 的宽高尺寸. layout 流程确定 View 的位置及最终尺寸. draw 流程将 View 绘制在屏幕上. Measure 测量流程 系统是通过 MeasureSpec 测量 View 的,在了解测量过程之前一定要了解这个 MeasureSpec . MeasureSpec MeasureSpec 是一个 32 位的 int 值打包而来的,打包为 MeasureSpec 主要是为了避免过多的对象内存分配. 为了方便操作,MeasureSpec

  • 浅谈android性能优化之启动过程(冷启动和热启动)

    本文介绍了浅谈android性能优化之启动过程(冷启动和热启动) ,分享给大家,具体如下: 一.应用的启动方式 通常来说,启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热

  • 浅谈Android中Service的注册方式及使用

    Service通常总是称之为"后台服务",其中"后台"一词是相对于前台而言的,具体是指其本身的运行并不依赖于用户可视的UI界面,因此,从实际业务需求上来理解,Service的适用场景应该具备以下条件: 1.并不依赖于用户可视的UI界面(当然,这一条其实也不是绝对的,如前台Service就是与Notification界面结合使用的): 2.具有较长时间的运行特性. 1.Service AndroidManifest.xml 声明 一般而言,从Service的启动方式上

  • 浅谈Android获取ImageView上的图片,和一个有可能遇到的问题

    1.在获取图片前先调用setDrawingCacheEnabled(true)这个方法: 举例:mImageView.setDrawingCacheEnabled(true); 2.之后可以通过getDrawingCache()获取图片 举例:Bitmap obmp = Bitmap.createBitmap(mImageView.getDrawingCache());  //获取到Bitmap的图片 3.获取完图片后记得调用setDrawingCacheEnabled(false) 举例:mI

  • 13问13答全面学习Android View绘制

    本文通过13问13答学习Android View绘制,供大家参考,具体内容如下 1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw流程结束以后就可以在屏幕上看到view了. 2.view的测量宽高和实际宽高有区别吗? 答:基本上百分之99的情况下都是可以认为没有区别的.有两种情况,有区别.第一种 就是有的时候会因为某些原因 view会多次测量,那第

  • 浅谈Android为RecyclerView增加监听以及数据混乱的小坑

    为 RecyclerView增加监听 1.在实现好的MyAdapter中写内部接口: public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { this.onItemLongClickListener = onItemLongClickListener; } public void setOnItemClickListener(OnItemClickListener onIt

随机推荐