Android LayoutInflater(布局服務(wù))

2023-03-31 14:24 更新

本節(jié)引言:

 本節(jié)繼續(xù)帶來的是Android系統(tǒng)服務(wù)中的LayoutInflater(布局服務(wù)),說到布局,大家第一時(shí)間 可能想起的是寫完一個(gè)布局的xml,然后調(diào)用Activity的setContentView()加載布局,然后把他顯示 到屏幕上是吧~其實(shí)這個(gè)底層走的還是這個(gè)LayoutInflater,用的Android內(nèi)置的Pull解析器來解析 布局。一般在Android動(dòng)態(tài)加載布局或者添加控件用得較多,本節(jié)我們就來學(xué)習(xí)下他在實(shí)際開發(fā)中 的一些用法~

官方API文檔LayoutInflater


1.LayoutInflater的相關(guān)介紹


1)Layout是什么鬼?

答:一個(gè)用于加載布局的系統(tǒng)服務(wù),就是實(shí)例化與Layout XML文件對(duì)應(yīng)的View對(duì)象,不能直接使用, 需要通過getLayoutInflater( )方法或getSystemService( )方法來獲得與當(dāng)前Context綁定的 LayoutInflater實(shí)例!


2)LayoutInflater的用法

①獲取LayoutInflater實(shí)例的三種方法

LayoutInflater inflater1 = LayoutInflater.from(this);  
LayoutInflater inflater2 = getLayoutInflater();  
LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); 

PS:后面兩個(gè)其實(shí)底層走的都是第一種方法~

②加載布局的方法

public View inflate (int resource, ViewGroup root, boolean attachToRoot) 該方法的三個(gè)參數(shù)依次為:

①要加載的布局對(duì)應(yīng)的資源id

②為該布局的外部再嵌套一層父布局,如果不需要的話,寫null就可以了!

③是否為加載的布局文件的最外層套一層root布局,不設(shè)置該參數(shù)的話, 如果root不為null的話,則默認(rèn)為true 如果root為null的話,attachToRoot就沒有作用了! root不為null,attachToRoot為true的話,會(huì)在加載的布局文件最外層嵌套一層root布局; 為false的話,則root失去作用! 簡(jiǎn)單理解就是:是否為加載的布局添加一個(gè)root的外層容器~!


③通過LayoutInflater.LayoutParams來設(shè)置相關(guān)的屬性:

比如RelativeLayout還可以通過addRule方法添加規(guī)則,就是設(shè)置位置:是參考父容器呢? 還是參考子控件?又或者設(shè)置margin等等,這個(gè)由你決定~


2.純Java代碼加載布局

我們?cè)缫蚜?xí)慣了使用XML生成我們需要的布局,但是在一些特定的情況下,我們 需要使用Java代碼往我們的布局中動(dòng)態(tài)的添加組件或者布局!

但是不建議大家完全地使用Java代碼來編寫Android頁(yè)面布局,首先一點(diǎn)就是代碼會(huì)多, 一多久容易亂,而且不利于業(yè)務(wù)的分離,我們還是建議使用xml來完成布局,然后通過 Java代碼對(duì)里面的組件進(jìn)行修改,當(dāng)然有些時(shí)候可能需要使用Java動(dòng)態(tài)的來添加組件!

純Java代碼加載布局的流程


——Step 1

創(chuàng)建容器:LinearLayout ly = new LinearLayout(this);

創(chuàng)建組件:Button btnOne = new Button(this);

——Step 2:

可以為容器或者組件設(shè)置相關(guān)屬性: 比如:LinearLayout,我們可以設(shè)置組件的排列方向:ly.setOrientation(LinearLayout.VERTICAL); 而組件也可以:比如Button:btnOne.setText("按鈕1"); 關(guān)于設(shè)置屬性的方法可參見Android 的API,通常xml設(shè)置的屬性只需在前面添加:set即可,比如 setPadding(左,上,右,下);

——Step 3:

將組件或容器添加到容器中,這個(gè)時(shí)候我們可能需要設(shè)置下組件的添加位置,或者設(shè)置他的大?。?我們需要用到一個(gè)類:LayoutParams,我們可以把它看成布局容器的一個(gè)信息包!封裝位置與大小 等信息的一個(gè)類!先演示下設(shè)置大小的方法:(前面的LinearLayout可以根據(jù)不同容器進(jìn)行更改)

LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(  
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 

很簡(jiǎn)單,接著就到這個(gè)設(shè)置位置了,設(shè)置位置的話,通常我們考慮的只是RelativeLayout! 這個(gè)時(shí)候用到LayoutParams的addRule( )方法!可以添加多個(gè)addRule( )哦! 設(shè)置組件在父容器中的位置,

比如設(shè)置組件的對(duì)其方式:

RelativeLayout rly = new RelativeLayout(this);  
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(  
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
lp2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);  
Button btnOne = new Button(this);  
rly.addView(btnOne, lp2);

參照其他組件的對(duì)其方式: (有個(gè)缺點(diǎn),就是要為參考組件手動(dòng)設(shè)置一個(gè)id,是手動(dòng)!!!!) 比如:設(shè)置btnOne居中后,讓BtnTwo位于btnOne的下方以及父容器的右邊!

public class MainActivity extends Activity {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        RelativeLayout rly = new RelativeLayout(this);  
        Button btnOne = new Button(this);  
        btnOne.setText("按鈕1");  
        Button btnTwo = new Button(this);  
        btnTwo.setText("按鈕2");  
        // 為按鈕1設(shè)置一個(gè)id值  
        btnOne.setId(123);  
        // 設(shè)置按鈕1的位置,在父容器中居中  
        RelativeLayout.LayoutParams rlp1 = new RelativeLayout.LayoutParams(  
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
        rlp1.addRule(RelativeLayout.CENTER_IN_PARENT);  
        // 設(shè)置按鈕2的位置,在按鈕1的下方,并且對(duì)齊父容器右面  
        RelativeLayout.LayoutParams rlp2 = new RelativeLayout.LayoutParams(  
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
        rlp2.addRule(RelativeLayout.BELOW, 123);  
        rlp2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);  
        // 將組件添加到外部容器中  
        rly.addView(btnTwo, rlp2);  
        rly.addView(btnOne, rlp1);  
        // 設(shè)置當(dāng)前視圖加載的View即rly  
        setContentView(rly);  
    }  
}  

——step 4:

調(diào)用setContentView( )方法加載布局對(duì)象即可! 另外,如果你想移除某個(gè)容器中的View,可以調(diào)用容器.removeView(要移除的組件);

運(yùn)行截圖


3.Java代碼動(dòng)態(tài)添加控件或xml布局

第二點(diǎn)我們講解了使用純Java代碼來加載布局,實(shí)際當(dāng)中用得并不多,更多的時(shí)候是動(dòng)態(tài) 的添加View控件以及動(dòng)態(tài)的加載XML布局!

1)Java代碼動(dòng)態(tài)增加View

動(dòng)態(tài)添加組件的寫法有兩種,區(qū)別在于是否需要先setContentView(R.layout.activity_main); 下面演示下兩種不同寫法添加一個(gè)Button的例子:

先寫個(gè)布局文件先:activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/RelativeLayout1"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent" >  

    <TextView   
        android:id="@+id/txtTitle"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="我是xml文件加載的布局"/>  

</RelativeLayout>  

第一種不需要setContentView()加載布局文件先:

public class MainActivity extends Activity {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        Button btnOne = new Button(this);  
        btnOne.setText("我是動(dòng)態(tài)添加的按鈕");  
        RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(    
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    
        lp2.addRule(RelativeLayout.CENTER_IN_PARENT);    
        LayoutInflater inflater = LayoutInflater.from(this);  
        RelativeLayout rly = (RelativeLayout) inflater.inflate(  
                R.layout.activity_main, null)  
                .findViewById(R.id.RelativeLayout1);  
        rly.addView(btnOne,lp2);  
        setContentView(rly);  
    }  
} 

第二種不需要setContentView()加載布局文件先:

public class MainActivity extends Activity {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        Button btnOne = new Button(this);  
        btnOne.setText("我是動(dòng)態(tài)添加的按鈕");  
        RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(    
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    
        lp2.addRule(RelativeLayout.CENTER_IN_PARENT);    
        RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);  
        rly.addView(btnOne,lp2);  
    }  
} 

分析總結(jié)

代碼很簡(jiǎn)單,創(chuàng)建按鈕后,我們又創(chuàng)建了一個(gè)LayoutParams對(duì)象,用來設(shè)置Button的大小, 又通過addRule()方法設(shè)置了Button的位置!

第一種方法:通過LayoutInflate的inflate()方法加載了activity_main布局,獲得了外層容器, 接著addView添加按鈕進(jìn)容器,最后setContentView();

第二種方法:因?yàn)槲覀円呀?jīng)通過setContetView()方法加載了布局,此時(shí)我們就可以通過 findViewById找到這個(gè)外層容器,接著addView,最后setContentView()即可!

另外,關(guān)于這個(gè)setContentView( )他設(shè)置的視圖節(jié)點(diǎn)是整個(gè)XML的根節(jié)點(diǎn)!


2)Java代碼動(dòng)態(tài)加載xml布局

接下來的話,我們換一個(gè),這次加載的是xml文件!動(dòng)態(tài)地添加xml文件! 先寫下主布局文件和動(dòng)態(tài)加載的布局文件:

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <Button
        android:id="@+id/btnLoad"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="動(dòng)態(tài)加載布局"/>
</RelativeLayout>  

inflate.xml

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:gravity="center"  
    android:orientation="vertical"  
    android:id="@+id/ly_inflate" >  

    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="我是Java代碼加載的布局" />  

    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="我是布局里的一個(gè)小按鈕" />  

</LinearLayout> 

接著到我們的MainActivity.java在這里動(dòng)態(tài)加載xml布局:

public class MainActivity extends Activity {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        //獲得LayoutInflater對(duì)象;  
        final LayoutInflater inflater = LayoutInflater.from(this);    
        //獲得外部容器對(duì)象  
        final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);  
        Button btnLoad = (Button) findViewById(R.id.btnLoad);  
        btnLoad.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                //加載要添加的布局對(duì)象  
                LinearLayout ly = (LinearLayout) inflater.inflate(  
                        R.layout.inflate_ly, null, false).findViewById(  
                        R.id.ly_inflate);  
                //設(shè)置加載布局的大小與位置  
                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(    
                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    
                lp.addRule(RelativeLayout.CENTER_IN_PARENT);    
                rly.addView(ly,lp);  
            }  
        });  
    }  
} 

運(yùn)行截圖

代碼分析

①獲取容器對(duì)象:

final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);

②獲得Inflater對(duì)象,同時(shí)加載被添加的布局的xml,通過findViewById找到最外層的根節(jié)點(diǎn)

final LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout ly = (LinearLayout) inflater.inflate(R.layout.inflate_ly, null, false)
   .findViewById(R.id.ly_inflate);

③為這個(gè)容器設(shè)置大小與位置信息:

RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(  
               LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
       lp.addRule(RelativeLayout.CENTER_IN_PARENT); 

④添加到外層容器中:

rly.addView(ly,lp);

4.LayoutInflater的inflate()方法源碼

最后提供下LayoutInflater的inflate()方法的源碼吧,有興趣的可以看看~,其實(shí)就是Pull解析而已~

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {    
    synchronized (mConstructorArgs) {    
        final AttributeSet attrs = Xml.asAttributeSet(parser);    
        mConstructorArgs[0] = mContext;    
        View result = root;    
        try {    
            int type;    
            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 (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);    
            } else {    
                View temp = createViewFromTag(name, attrs);    
                ViewGroup.LayoutParams params = null;    
                if (root != null) {    
                    params = root.generateLayoutParams(attrs);    
                    if (!attachToRoot) {    
                        temp.setLayoutParams(params);    
                    }    
                }    
                rInflate(parser, temp, attrs);    
                if (root != null && attachToRoot) {    
                    root.addView(temp, params);    
                }    
                if (root == null || !attachToRoot) {    
                    result = temp;    
                }    
            }    
        } catch (XmlPullParserException e) {    
            InflateException ex = new InflateException(e.getMessage());    
            ex.initCause(e);    
            throw ex;    
        } catch (IOException e) {    
            InflateException ex = new InflateException(    
                    parser.getPositionDescription()    
                    + ": " + e.getMessage());    
            ex.initCause(e);    
            throw ex;    
        }    
        return result;    
    }    
} 

本節(jié)小結(jié):

本節(jié)給大家講解了一下Android中的LayoutInflater(布局服務(wù)),以及動(dòng)態(tài)加載View和控件 相關(guān)的東西,相信對(duì)初學(xué)控件的朋友帶來幫助~好的,就說這么多,謝謝~ 

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)