android 加载view什么时候进行View中Background的加载

Android什么时候进行View中Background的加载 - 推酷
Android什么时候进行View中Background的加载
对大多数Android的开发者来说,最经常的操作莫过于对界面进行布局,View中背景图片的加载是最经常做的。但是我们很少关注这个过程,这篇文章主要解析view中背景图片加载的流程。了解view中背景图片的加载(资源的加载)可以让我们对资源加载的过程进行一些优化,另外当需要进行整个应用的换肤时,也可以更得心应手。
View图片的加载,我们最常见的就是通过在XML文件当中进行drawable的设置,然后让Android系统帮我们完成,或者手动写代码加载成Bitmap,然后加载到View上。这篇文章主要分析Android在什么时候以及怎么帮我们完成背景图片的加载的,那么我们就从Activity.setContentView还是LayoutInflater.inflate(...)方法开始分析。
方法进行View的初始化,最终都会到达LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)这个方法中。在这里我们主要关注View的背景图片加载,对于XML如何解析和加载就放过了。
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mC
View result =
// Look for the root node.
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ &: No start tag found!&);
final String name = parser.getName();
if (DEBUG) {
System.out.println(&**************************&);
System.out.println(&Creating root view: &
System.out.println(&**************************&);
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(&&merge /& can be used only with a valid &
+ &ViewGroup root and attachToRoot=true&);
rInflate(parser, root, attrs, false);
// Temp is the root view that was found in the xml
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
temp = createViewFromTag(root, name, attrs);
ViewGroup.LayoutParams params =
if (root != null) {
if (DEBUG) {
System.out.println(&Creating params from root: & +
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
if (DEBUG) {
System.out.println(&-----& start inflating children&);
// Inflate all children under temp
rInflate(parser, temp, attrs, true);
if (DEBUG) {
System.out.println(&-----& done inflating children&);
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ &: & + e.getMessage());
ex.initCause(e);
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastC
mConstructorArgs[1] =
上面这么长一串代码,其实思路很清晰,就是针对XML文件进行解析,然后根据XML解析出的每一个节点进行View的初始化,紧接着将View的Layout参数设置到View上,然后将View添加到它的父控件上。为了了解View是怎么被加载出来的,我们只需要了解
temp = createViewFromTag(root, name, attrs);
跟进去看看。
* default visibility so the BridgeInflater can override it.
View createViewFromTag(View parent, String name, AttributeSet attrs) {
if (name.equals(&view&)) {
name = attrs.getAttributeValue(null, &class&);
if (DEBUG) System.out.println(&******** Creating view: & + name);
if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
else view =
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
view = createView(name, null, attrs);
if (DEBUG) System.out.println(&Created view is: & + view);
} catch (InflateException e) {
} catch (ClassNotFoundException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ &: Error inflating class & + name);
ie.initCause(e);
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ &: Error inflating class & + name);
ie.initCause(e);
上面代码的重点在于try...Catch里的内容。try包起来的东西就是对View进行初始化,注意到上面代码中有几个Factory,这些Factory可以在View进行初始化,也就是说其实我们可以在这里干预View的初始化。从上面代码我们可以知道,如果我们自定义了一个Factory,那么当前要初始化的View会优先被我们自定义的Factory初始化,而不通过系统默认的Factory初始化。那么如果我们要自定义Factory,应该在哪里定义呢?容易想到,Factory必须要赶在资源加载前自定义完成,所以我们应该在onCreate(...)的this.setContentView(...)之前设置LayoutInflater.Factory。
getLayoutInflater().setFactory(factory);
接下来我们看到上面函数里面的
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
view = createView(name, null, attrs);
这段函数就是对View进行初始化,有两种情况,一种是系统自带的View,它在
if (-1 == name.indexOf('.'))
这里面进行初始化,因为如果是系统自带的View,传入的那么一般不带系统的前缀&android.view.&。另一个分支初始化的是我们自定义的View。我们跟进onCreateView看看。
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, &android.view.&, attrs);
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor&? extends View& constructor = sConstructorMap.get(name);
Class&? extends View& clazz =
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
Object[] args = mConstructorA
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// always use ourselves when inflating ViewStub later
final ViewStub viewStub = (ViewStub)
viewStub.setLayoutInflater(this);
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ &: Error inflating class &
+ (prefix != null ? (prefix + name) : name));
ie.initCause(e);
} catch (ClassCastException e) {
// If loaded class is not a View subclass
InflateException ie = new InflateException(attrs.getPositionDescription()
+ &: Class is not a View &
+ (prefix != null ? (prefix + name) : name));
ie.initCause(e);
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ &: Error inflating class &
+ (clazz == null ? &&unknown&& : clazz.getName()));
ie.initCause(e);
从onCreateView(...)中我们知道,其实createViewFromTag(...)中对View的初始化最终都是通过createView(...)这个函数进行初始化的,不同只在于系统控件需要通过onCreateView(...)加上前缀,以便类加载器(ClassLoader)正确地通过类所在的包初始化这个类。createView(...)这个函数的思路很清晰,不看catch里面的内容,try里面开头的两个分支就是用来将所要用的类构造函数提取出来,Android系统会对使用过的类构造函数进行缓存,因为像TextView这些常用的控件可能会被使用很多次。接下来,就是通过类构造函数对View进行初始化了。我们注意到传入构造函数的mConstructorArgs是一个包含两个元素的数组。
final Object[] mConstructorArgs = new Object[2];
那么我们就很清楚了,它就是调用系统控件中对应两个参数的构造函数。为了方便,我们就从最基础的View进行分析。
public View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
public View(Context context, AttributeSet attrs, int defStyle) {
this(context);
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
defStyle, 0);
Drawable background =
int leftPadding = -1;
int topPadding = -1;
int rightPadding = -1;
int bottomPadding = -1;
int startPadding = UNDEFINED_PADDING;
int endPadding = UNDEFINED_PADDING;
int padding = -1;
int viewFlagValues = 0;
int viewFlagMasks = 0;
boolean setScrollContainer =
int x = 0;
int y = 0;
float tx = 0;
float ty = 0;
float rotation = 0;
float rotationX = 0;
float rotationY = 0;
float sx = 1f;
float sy = 1f;
boolean transformSet =
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
int overScrollMode = mOverScrollM
boolean initializeScrollbars =
boolean leftPaddingDefined =
boolean rightPaddingDefined =
boolean startPaddingDefined =
boolean endPaddingDefined =
final int targetSdkVersion = context.getApplicationInfo().targetSdkV
final int N = a.getIndexCount();
for (int i = 0; i & N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial =
mUserPaddingRightInitial =
leftPaddingDefined =
rightPaddingDefined =
//省略一大串无关的函数
由于我们只关注View中的背景图是怎么加载的,注意这个函数其实就是遍历AttributeSet attrs这个东西,然后对View的各个属性进行初始化。我们直接进入
background = a.getDrawable(attr);
这里看看(TypedArray.getDrawable)。
public Drawable getDrawable(int index) {
final TypedValue value = mV
if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (false) {
System.out.println(&******************************************************************&);
System.out.println(&Got drawable resource: type=&
+ value.type
+ & str=& + value.string
+ & int=0x& + Integer.toHexString(value.data)
+ & cookie=& + value.assetCookie);
System.out.println(&******************************************************************&);
return mResources.loadDrawable(value, value.resourceId);
我们发现它调用mResources.loadDrawable(...),进去看看。
/*package*/ Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
if (TRACE_FOR_PRELOAD) {
// Log only framework resources
if ((id &&& 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) android.util.Log.d(&PreloadDrawable&, name);
boolean isColorDrawable =
if (value.type &= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type &= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable =
final long key = isColorDrawable ? value.data :
(((long) value.assetCookie) && 32) | value.
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
if (dr != null) {
Drawable.ConstantState cs = isColorDrawable
? sPreloadedColorDrawables.get(key)
: (sPreloadedDensity == mConfiguration.densityDpi
? sPreloadedDrawables.get(key) : null);
if (cs != null) {
dr = cs.newDrawable(this);
if (isColorDrawable) {
dr = new ColorDrawable(value.data);
if (dr == null) {
if (value.string == null) {
throw new NotFoundException(
&Resource is not a Drawable (color or path): & + value);
String file = value.string.toString();
if (TRACE_FOR_MISS_PRELOAD) {
// Log only framework resources
if ((id &&& 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) android.util.Log.d(TAG, &Loading framework drawable #&
+ Integer.toHexString(id) + &: & + name
+ & at & + file);
if (DEBUG_LOAD) Log.v(TAG, &Loading drawable for cookie &
+ value.assetCookie + &: & + file);
if (file.endsWith(&.xml&)) {
XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, &drawable&);
dr = Drawable.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
&File & + file + & from drawable resource ID #0x&
+ Integer.toHexString(id));
rnf.initCause(e);
InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
System.out.println(&Opened file & + file + &: & + is);
dr = Drawable.createFromResourceStream(this, value, is,
file, null);
is.close();
System.out.println(&Created stream: & + dr);
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
&File & + file + & from drawable resource ID #0x&
+ Integer.toHexString(id));
rnf.initCause(e);
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
cs = dr.getConstantState();
if (cs != null) {
if (mPreloading) {
if (verifyPreloadConfig(value, &drawable&)) {
if (isColorDrawable) {
sPreloadedColorDrawables.put(key, cs);
sPreloadedDrawables.put(key, cs);
synchronized (mTmpValue) {
//Log.i(TAG, &Saving cached drawable @ #& +
Integer.toHexString(key.intValue())
+ & in & + this + &: & + cs);
if (isColorDrawable) {
mColorDrawableCache.put(key, new WeakReference&Drawable.ConstantState&(cs));
mDrawableCache.put(key, new WeakReference&Drawable.ConstantState&(cs));
就是这个函数了,所有View的背景的加载都在这里了。这个函数的逻辑就比较复杂了,大体说来就是根据背景的类型(纯颜色、定义在XML文件中的,或者是一张静态的背景),如果缓存里面有,就直接用缓存里的。
总结一下,经过上面的分析,我们知道了,Android就是在Activity.setContentView(...)中为我们进行资源文件的加载,精确到具体的函数的话,资源文件的加载就是在每一个被初始化的View的构造函数中进行加载的。
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致Android什么时候进行View中Background的加载_百度知道
Android什么时候进行View中Background的加载
&; + file + &quot, AssetM
if (value: &quot,就直接用缓存里的.type &gt, rp)。但是我们很少关注这个过程;
&#47, value,然后让Android系统帮我们完成, new WeakReference&lt.ConstantState cs = isColorDrawa&#47.type &lt,所有View的背景的加载都在这里了; from drawable resource ID #0x&quot.assetCookie) &lt.;&/,经过上面的分析..)中为我们进行资源文件的加载, &quot.toHexString(id));
is.PreloadDrawable&Saving cached drawable @ #&/
if (TRACE_FOR_MISS_PRELOAD) {
InputStream is = mA(cs));)) {
XmlResourceParser rp = loadXmlResourceParser(
file.endsWith(&quot。这篇文章主要分析Android在什么时候以及怎么帮我们完成背景图片的加载的; 32) | value: (sPreloadedDensity == mConfiguration!= null) {
+ value.ConstantState&
sPreloadedDrawables.data.xml&quot.close();
return dr.toHexString(id) + &quot!= null) android,这篇文章主要解析view中背景图片加载的流程;&gt.put(
if (isColorDrawable) {
dr = new ColorDrawable(value? mColorDrawableC)) {
if (isColorDrawable) {
sPreloadedColorDrawables.inflate(!= null) {
return dr.toHexString(id));
rp.setContentView(;
boolean isColorDrawable =File &quot,进去看看;
D 24) == 0x1) {
final String name = getResourceName(id).)方法进行View的初始化;
Drawable dr = getCachedDrawable(isColorDrawable ,如果缓存里面有.d(TAG;
mDrawableC
rnf。在这里我们主要关注View的背景图片加载.L
cs =; Drawable loadDrawable(TypedValue value? sPreloadedDrawables.,然后加载到View上;
if (cs : null);
if ((cs)).TYPE_FIRST_COLOR_INT &&
value.setChangingConfigurations(value.toHexString(key.ConstantState&Resource is not a Drawable (color or path).assetCookie,也可以更得心应手, boolean attachToRoot)这个方法中;
if (dr == null) {
if (value.setContentView(; in &quot。
  我们发现它调用mR
+ I*package*&#47。这个函数的逻辑就比较复杂了;
final long key = isColorD + is),
file: &quot.put(key.densityDpi
File &quot.getConstantState().inflate(, &quot.newDrawable(this).openNonAsset(
if (isColorDrawable) {
mColorDrawableCache,最经常的操作莫过于对界面进行布局.out: &&#47,View中背景图片的加载是最经常做的.;Opened file &D&#47。   总结一下, is.intValue())
ILog. at & + file), new WeakReference&lt.; + value).,Loading drawable for cookie &quot.).L= TypedV
  就是这个函数了., int id)
throws NotFoundException {
if (TRACE_FOR_PRELOAD) {
&#47: &quot: &
String file =
if (name ,或者手动写代码加载成Bitmap.)还是LayoutInflater.println(&quot,精确到具体的函数的话;
throw rnf,另外当需要进行整个应用的换肤时;&#47, cs)!= null) {
if (mPreloading) {
if (verifyPreloadConfig(value.println(&quot.close();
rnf..assetCookie + &quot.get(key) ; 24) == 0x1) {
final String name = getResourceName(id);
dr = Drawable.put(key.v(TAG, cs), &quot.data), key);!= null) {
SCreated stream.setContentView还是LayoutI
dr = D from drawable resource ID #0x&drawable& + this + &quot, name).initCause(e);
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
&quot, id.changingConfigurations),对于XML如何解析和加载就放过了。了解view中背景图片的加载(资源的加载)可以让我们对资源加载的过程进行一些优化.string。   View图片的加载? value.string == null) {
throw new NotFoundException(
&quot.get(key) Log only framework resources
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
&quot,大体说来就是根据背景的类型(纯颜色; +
+ I;&lt: &quot,我们知道了.createFromResourceStream(this, null)。   不管是从A= TypedV& + dr).initCause(e).loadDrawable(。   /
System,最终都会到达LayoutInflater、定义在XML文件中的..ACCESS_STREAMING);drawable& Log only framework resources
if ((id &gt, ViewG + name
if (cs , value.)方法开始分析;
synchronized (mTmpValue) {
&#47. + file + &
if (DEBUG_LOAD) Log,资源文件的加载就是在每一个被初始化的View的构造函数中进行加载的.toString()!= null)&gt.data ? sPreloadedColorDrawables.d(&quot.inflate(XmlPullParser parser.: mDrawableC + file + &quot.TYPE_LAST_COLOR_INT) {
isColorDrawable = true.i(TAG;
/ + file),Android就是在Activity.assetC
if (dr .createFromXml(this:
(((long) value,或者是一张静态的背景),那么我们就从A + cs).put(D&#47,我们最常见的就是通过在XML文件当中进行drawable的设置;
Loading framework drawable #&quot  对大多数Android的开发者来说
知道智能回答机器人
我是知道站内的人工智能,可高效智能地为您解答问题。很高兴为您服务。
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁Android什么时候进行View中Background的加载_百度知道
Android什么时候进行View中Background的加载
提问者采纳
最终都会到达LayoutInflater。在这里我们主要关注View的背景图片加载.inflate( 不管是从Activity.inflate(XmlPullParser parser...)方法进行View的初始化..setContentView(.)还是LayoutInflater., ViewGroup root, boolean attachToRoot)这个方法中,对于XML如何解析和加载就放过了
其他类似问题
为您推荐:
android的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁下次自动登录
现在的位置:
& 综合 & 正文
android中src和background区别
ImageView中XML属性src和background的区别:
background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸。src是图片内容(前景),bg是背景,可以同时使用。
此外:scaleType只对src起作用;bg可设置透明度,比如在ImageButton中就可以用android:scaleType控制图片的缩放方式,示例如下:
&ImageView android:id="@+id/img"
android:src="@drawable/logo"
android:scaleType="centerInside"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_centerVertical="true"/&
  说明:centerInside表示按比例缩放图片,使得图片长 (宽)的小于等于视图的相应维度。
  注意:控制的图片为资源而不是背景,即android:src="@drawable/logo",而非android:background="@drawable/logo"。中动态加载图片也类似,如:应该imgView.setImageResource(R.drawable.*);而非imgView.setBackgroundResource(R.drawable.*);
附:更详细的scaleType说明:
CENTER /center 在视图中心显示图片,并且不缩放图片
CENTER_CROP / centerCrop 按比例缩放图片,使得图片长 (宽)的大于等于视图的相应维度
CENTER_INSIDE / centerInside 按比例缩放图片,使得图片长 (宽)的小于等于视图的相应维度
FIT_CENTER / fitCenter 按比例缩放图片到视图的最小边,居中显示
FIT_END / fitEnd 按比例缩放图片到视图的最小边,显示在视图的下部分位置
FIT_START / fitStart 把图片按比例扩大/缩小到视图的最小边,显示在视图的上部分位置
FIT_XY / fitXY 把图片不按比例缩放到视图的大小显示
MATRIX / matrix 用矩阵来绘制
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 android view加载完毕 的文章

 

随机推荐