編輯:關於Android編程
在看inflate()方法時,我們隨便看下如何獲得 LayoutInflater ,獲得LayoutInflater 實例有三種方式
LayoutInflater inflater =
getLayoutInflater();//調用Activity的getLayoutInflater()
LayoutInflater inflater = LayoutInflater.from(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
其實,這三種方式本質是相同的,從源碼中可以看出:
getLayoutInflater()
Activity 的 getLayoutInflater() 方法是調用 PhoneWindow 的getLayoutInflater()方法,看一下該源代碼:
public PhoneWindow(Context context)
{
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
可以看出它其實是調用 LayoutInflater.from(Context context)方法。
LayoutInflater.from(Context context)
public static LayoutInflater from(Context context)
{
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null)
{
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
可以看出它其實調用 context.getSystemService()。
所以這三種方式最終本質是都是調用的Context.getSystemService()。
接下來我們來看下inflate()方法,
inflate()LayoutInflater的inflate方法一共有四種
public View inflate(int, ViewGroup)
public View inflate(XmlPullParser, ViewGroup)
public View inflate(int, ViewGroup, boolean)
public View inflate(XmlPullParser, ViewGroup, boolean)
查看源碼我們會發現inflate(int, ViewGroup)調用的是inflate(int, ViewGroup, boolean)方法
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
而inflate(int, ViewGroup, boolean)調用的是inflate(XmlPullParser, ViewGroup, boolean)方法
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
再看View inflate(XmlPullParser, ViewGroup)我們會發現它調用的也是inflate(XmlPullParser, ViewGroup, boolean)方法
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
所以呢?這四個方法都是public View inflate(XmlPullParser, ViewGroup, boolean)方法,那其他三個我們就不看了,我們來分析這個方法。
看下別人對這個方法的參數是怎樣描述的:
返回值View:
返回的值是View指向的根節點。大家可能會有個疑問,第二個參數root不就是根結點嗎?那返回根結點的話,不就是我們傳進去的root嗎?這可不一定,大家知道返回根結點的VIEW之後,繼續往下看參數的具體講解。
第一個參數XmlPullParser:
也就說根據其他幾個方法傳進來的xml布局文件在這裡會被用傳進來的parser進行解析 第二個參數root:
表示根結點,它的作用主要取決於第三個參數
第三個參數attachToRoot:
表示是否將轉換後的VIEW直接添加在根結點上,如果是TRUE,那麼在轉換出來VIEW之後,內部直接會調用root.addView()來將其添加到root結點上,然後返回根結點,當然是我們傳進去的ROOT結點。如果設為FALSE,那只會將XML布局轉換成對應的VIEW,不會將其添加的我們傳進去的root結點上。
第三個參數可能這樣說比較難理解,我們來舉個例子:
1 . 創建一個activity_root.xml文件,一個垂直的線性布局id為root,只有一個TextView
" data-snippet-id="ext.80283f5a3bd6b5a922acb5a05793b5bf" data-snippet-saved="false" data-codota-status="done">
2 . 然後再建一個布局:add_layout.xml,也是一個垂直的線性布局背景顏色是紅色,裡面有個TextView
" data-snippet-id="ext.300890aa6fce25bc88f605b58eba0e99" data-snippet-saved="false" data-codota-status="done">
我們先來試試TRUE這個參數
public class InflateDomeActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
//顯示activity_root布局
setContentView(R.layout.activity_root);
//通過LayoutInflater.from(Context context);來獲取LayoutInflater的實例
LayoutInflater layoutInflater = LayoutInflater.from(this);
//獲取根結點的控件實例
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.root);
//將activity_add加載到activity_root布局中
layoutInflater.inflate(R.layout.activity_add,linearLayout,true);
}
}
效果:

那如果將TRUE換為FALSE呢?
public class InflateDomeActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_root);
LayoutInflater layoutInflater = LayoutInflater.from(this);
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.root);
layoutInflater.inflate(R.layout.activity_add,linearLayout,false);
}
}
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
效果:

可以看到,我們的主布局沒有任何變化,也就是說add_layout.xml的布局沒有被添加到activity_mian.xml中;
我們開頭就講過,如果attachToRoot設為false,那轉換後的布局是不會被添加到root中的,會作為結果返回。
其實attachToRoot設為TRUE的代碼與下面的代碼是等價的:public class InflateDomeActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_root);
LayoutInflater layoutInflater = LayoutInflater.from(this);
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.root);
View view = layoutInflater.inflate(R.layout.activity_add, linearLayout, false);
linearLayout.addView(view);
}
}
透過源碼分析LayoutInflater.inflate()的過程public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)方法源碼:
can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
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 against its context.
rInflateChildren(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) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}" data-snippet-id="ext.438ac77f5aa684def78e1a328ada9568" data-snippet-saved="false" data-codota-status="done"> public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
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: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
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 against its context.
rInflateChildren(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) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
我們一點點來分析:
再源碼的基礎上我們保留下一些核心代碼來分析下
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
//第一步:初始化
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
//注意這裡,在初始化時,result表示要返回的視圖,默認是返回root
View result = root;
…………
final String name = parser.getName();
//第二步:創建XML對應的空白VIEW:temp
if (TAG_MERGE.equals(name)) {
//如果是merge標簽:拋出異常 (因為merge標簽能夠將該標簽中的所有控件直接連在上一級布局上面,從而減少布局層級,假如一個線性布局替換為merge標簽,那麼原線性布局下的多個控件將直接連在上一層結構上,也就是如果加載進來的root根節點是root的話,那麼將來無法知道布局的根節點是什麼)
…………
} else {
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
//第三步:從根結點中,獲取布局參數,設置到temp中
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
//第四步:初始化temp中的子控件
rInflate(parser, temp, attrs, true);
//第五步:如果root不為空,而且attachToRoot設為TRUE,則將其視圖通過addView添加到root中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//第六步:如果root為空,或者attachToRoot設為FALSE,那麼就將TEMP視圖做為result返回
if (root == null || !attachToRoot) {
result = temp;
}
}
return result;
}
}
我們來逐步分析一下:
第一步:一進來是初始化部分:
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
//注意這裡,在初始化時,result表示要返回的視圖,默認是返回root
View result = root;
通過XML文件獲取布局中各個控件的屬性集:AttributeSet,注意:這裡通過XML只能知道布局裡每個控件的布局參數。那整個LAYOUT的布局參數呢,雖然我們在XML的根結點的布局可能寫的是layout_width:fill_parent,layout_height:fill_parent。但這只是很籠統的,系統要確實計算出它的寬高數來。這個寬高數的計算就是通過root中的屬性來得出來的,下面代碼中會提到。
一個很重要的部分在於最後一句話!!!
[java] view plain copy
View result = root;
result表示最後返回的視圖VIEW,所以這表示在默認情況下,返回root的視圖!!注意這只是在默認情況下,下面會對result進行賦值的!
第二步:創建XML對應的空白視圖temp
[java] view plain copy
//第二步:創建XML對應的空白VIEW:temp
if (TAG_MERGE.equals(name)) {
//如果是merge標簽,拋出異常
…………
} else {
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
在這裡首先判斷XML的根結點是不是merge標簽,大家知道我們的merge標簽的主要作用是用來將merge標簽下的所有控件合並到其上層布局上,也就是說merge標簽下是沒有根控件的!因為merge標簽下的控件都是並列關系,所以如果merge標簽使用的inflate函數,那我們根本沒有辦法給它返回根視圖,所以肯定是要拋出異常的
如果不是merge標簽,就創建一個空白視圖,返回給temp,這裡的temp就是我們XML所對應的布局!
第三步:獲取root的布局參數,設置到temp中
//第三步:從根結點中,獲取布局參數,設置到temp中
ViewGroup.LayoutParams params = null;
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
在這裡看到,首先獲取到了ROOT的布局參數賦值為params,然後當attachToRoot為FALSE時,將參數設置到temp中;
第四步:初始化temp中的子控件
[java] view plain copy
//第四步:初始化temp中的子控件
rInflate(parser, temp, attrs, true);
//rInflate()其實是一個遞歸函數,用來遞歸建立temp下的所有控件的視圖
第五步:如果root不為空,而且attachToRoot設為TRUE,則將其視圖通過addView添加到root中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
在這裡,就是當root不為空、attachToRoot為TRUE時,將構建好的temp視圖添加到root中,注意這裡的參數仍然從root中獲取的布局參數params!!!所以,無論attachToRoot值為如何,temp的布局參數都是從ROOT裡獲取的!!!!
第六步:如果root為空,或者attachToRoot設為FALSE,那麼就將TEMP視圖做為result返回
if (root == null || !attachToRoot) {
result = temp;
}
從這裡可以看到,如果root為空,獲取attachToRoot為FALSE,就會將temp做為結果返回!
到這裡整個過程就分析結束了,下面我們總結一下:
root的最基本作用,就是給我們傳進去的Layout提供布局參數信息
如果attachToRoot為TRUE,那麼會將Layout產生的布局視圖添加到root中,返回root,如果attachToRoot為FALSE,那麼會將Layout產生的布局視圖直接返回
Dialog在android中的應用(一)
如果做一個如下圖的Dialog,首先要定義樣式: stateUnchanged|adjustResize @null @null
Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
在上一篇文章中,我們分析了Android系統進程間通信機制Binder中的Server在啟動過程使用Service Ma
Android組件:Fragment切換後保存狀態
之前寫的第一篇Fragment實例,和大多數人一開始學的一樣,都是通過FragmentTransaction的replace方法來實現,replace方法相
android實現類似於支付寶余額快速閃動的效果
效果如下: 此圖片不會動,但實際上是會快速跳動的。 之前看到有支付寶的效果非常牛逼。就是進去看到余額呼噜噜的直接上躥下跳到具體數字,效果帥,