天天看點

Android LayoutInflater動态添加子View的屬性生效問題

  

Android LayoutInflater動态添加子View的屬性生效問題

  最近正在做動态添加Button的效果。結果動态添加的按鈕的樣式設定過了但是margin屬性卻總是被忽略,不起作用。這個還是得從LayoutInflater的使用開始說起。

  LayoutInflater有三種加載方式,之前看過網上的很多關于LayoutInflater的擷取方式,但是想要使動态加載的view的屬性生效,實際上取決于我們使用的LayoutInflater的方法。(不要感覺下面的LayoutInflater 的獲得方式沒用,重點是第二部分動态加載view的方式)

獲得 LayoutInflater 的三種方式

這裡簡單介紹一下LayoutInflater 獲得的三種方式,如果你已經很清楚了就不需要看了

方式一:

//調用Activity的getLayoutInflater() 
 LayoutInflater inflater = getLayoutInflater();      

方式二:

LayoutInflater inflater = LayoutInflater.from(context);        

方式三:

.getSystemService(Context.LAYOUT_INFLATER_SERVICE);      

動态加載方式

這部分才是重點

既然要動态加載那就需要獲得我們的view,view的獲得方式,根據方法的重載,傳遞的參數不同,調用的方法也不同。下面是LayoutInflater的源碼,結合源碼來看一下。

LayoutInflater獲得View有三種方法,主要使用的下面兩中方式:

代碼執行個體:

這種調用方式會忽略View的屬性

.inflate(R.layout.activity_travel_starttravel,null);      

這種方式才是我們所想要的,不會使屬性失效的方式

.inflate(R.layout.activity_travel_starttravel,mLinearLayoutEndTravel,false)      

下面看下源碼中他們的差別

方式一:

from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     * 
     * @param resource ID for an XML layout resource to load (e.g.,
     *        <code>R.layout.main_page</code>)
     * @param root Optional view to be the parent of the generated hierarchy.
     * @return The root View of the inflated hierarchy. If root was supplied,
     *         this is the root View; otherwise it is the root of the inflated
     *         XML file.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return      

注意:可以看出來方式一調用的是方式二的三個參數的傳遞方法,這就是關鍵了,是以我們直接來看方式二

方式二:

from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     * 
     * @param resource ID for an XML layout resource to load (e.g.,
     *        <code>R.layout.main_page</code>)
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    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,也就是下面的方法

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
                }
                //有關XmlPullParser的部分先不管
                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("<merge /> 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;
                    //直接看這一部分,如果傳入了view所要添加到的布局則會産生布局屬性
                    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);
                        //如果将attachToRoot設定為false則進行設定屬性,否則不設定
                        //前面方式一與方式二預設傳入的是root!=null,也就是傳入了attachToRoot=true,是以不會設定屬性了
                        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      

重點了解部分:

Android LayoutInflater動态添加子View的屬性生效問題

是以,如果想要使你動态添加的按鈕有效則必須使用方式二來添加按鈕。下面附一個使用執行個體,實作的效果是部落格開篇圖檔展示的效果。

使用執行個體

界面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
<include layout="@layout/activity_chart_subscribe_header"></include>
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/light_grey"></View>
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"

    android:orientation="horizontal"
    android:gravity="center_vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="已添加"
        android:textSize="18sp"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="(點選删除)"
        android:textSize="14sp"/>

</LinearLayout>
        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@color/light_grey"></View>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        <Button
            android:id="@+id/button_nullsubscribe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:drawableTop="@mipmap/formdate_null_icon"
            android:textColor="@color/grey"
            android:visibility="gone"
            android:background="@null"/>
        <GridLayout
            android:id="@+id/gridlayoyt_blank_chartsubscribe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="10dp"
            android:columnCount="3"
            android:rowCount="3"
            android:minHeight="10dp"
            android:minWidth="10dp"

            android:layout_marginLeft="10dp"
            <Button
                    android:id="@+id/button_gridlayout_blank_locate"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="随時定位"
                    style="@style/SubscribeBlankButtonStyle"

                <Button
                    android:id="@+id/button_gridlayout_blank_cusvisit"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="客戶拜訪"
                    style="@style/SubscribeBlankButtonStyle"
                <Button
                    android:id="@+id/button_gridlayout_blank_addnewcus"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="新增客戶"
                     style="@style/SubscribeBlankButtonStyle"

            <Button
                android:id="@+id/button_gridlayout_blank_cusall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/SubscribeBlankButtonStyle"
                android:text="客戶總量"/>
        </GridLayout>
    </FrameLayout>
</LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/light_grey"></View>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="可添加"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="10dp"
            android:paddingBottom="10dp"
            android:paddingTop="10dp"
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/light_grey"></View>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        <GridLayout
            android:id="@+id/gridlayoyt_canadd_chartsubscribe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:rowCount="2"
            android:columnCount="3"
            android:minHeight="10dp"
            android:minWidth="10dp"
            android:layout_marginLeft="10dp"
        </GridLayout>
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/light_grey"></View>
</LinearLayout>      
Android LayoutInflater動态添加子View的屬性生效問題

按鈕的兩種樣式設定

name="SubscribeBlankButtonStyle">
        <item name="android:textColor">@color/red</item>
        <item name="android:background">@drawable/subscribe_button_red</item>
        <item name="android:textSize">14sp</item>
        <item name="android:layout_weight">1</item>
        <item name="android:layout_columnWeight">1</item>
        <item name="android:layout_marginTop">10dp</item>
        <item name="android:layout_marginLeft">10dp</item>
        <item name="android:layout_gravity">fill_horizontal</item>
    </style>
    <style name="SubscribeAddButtonStyle">
        <item name="android:textColor">@color/deep_grey</item>
        <item name="android:textSize">14sp</item>
        <item name="android:maxWidth">80dp</item>
        <item name="android:layout_weight">1</item>
        <item name="android:layout_marginTop">10dp</item>
        <item name="android:layout_columnWeight">1</item>
        <item name="android:layout_marginLeft">10dp</item>
        <item name="android:background">@drawable/subscribe_button_grey</item>
        <item name="android:layout_gravity">fill_horizontal</item>
    </style>      

Button按鈕的兩個子View,編寫在了xml檔案中

紅色按鈕樣式

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="@style/SubscribeAddButtonStyle"      
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="@style/SubscribeBlankButtonStyle"      
public class ActivityChartSubscribe extends BaseActivity implements View.OnClickListener{
    //傳回報表界面的按鈕注釋
    private Button mButtonBackToChart;
    //已添加下面沒有任何東西時的button
    private Button mButtonBlank;


    //已添加下面的GridLayout
    private GridLayout mGridLayoutBlank;

    //可添加下面的GridLayout
    private GridLayout mGridLayoutAdd;

    //已添加下面的按鈕
    private Button mButtonGridBlankLocate;
    private Button mButtonGridBlankCusVisit;
    private Button mButtonGridBlankAddNewCus;
    private Button mButtonGridBlankCusAll;



    private int TYPE_LOCATE=0x11;
    private int TYPE_CUSVISIT=0x22;
    private int TYPE_CusALL=0x33;
    private int TYPE_AddNewCus=0x44;

    private int mButtonCount=4;

    private List<String> mButtonType;
    private List<String> mButtonTypeAdd;

    private SharedPreferences mSharedPreferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chart_subscribe);
        chartsubscribefindview();
    }

    @Override
    public void onClick(View view) {
        switch ( view.getId()){
            case R.id.button_backtochart_chartsubscribe:
                //傳回報表界面
                SharedPreferences.Editor mEdit1= mSharedPreferences.edit();
                mEdit1.putInt("Button_type", mButtonType.size()); /*sKey is an array*/

                for(int i=0;i<mButtonType.size();i++) {
                    mEdit1.remove("Button_" + i);
                    mEdit1.putString("Button_" + i, mButtonType.get(i));
                }
                 mEdit1.commit();
                finish();
                break;
            case R.id.button_gridlayout_blank_locate:
                deletebuttonfromgridblank(view, TYPE_LOCATE);
                                break;
            case R.id.button_gridlayout_blank_cusvisit:
                deletebuttonfromgridblank(view ,TYPE_CUSVISIT);
                break;

            case R.id.button_gridlayout_blank_cusall:
                deletebuttonfromgridblank(view, TYPE_CusALL);
                break;
            case R.id.button_gridlayout_blank_addnewcus:
                //從BlankGridlayout部分删除,新增客戶
                deletebuttonfromgridblank(view, TYPE_AddNewCus);

                break;
        }
    }

    /**
     *該方法用于删除紅色的按鈕同時添加灰色的按鈕
     * @param
    private void deletebuttonfromgridblank(View v,final int type) {
        mGridLayoutBlank.removeView(v);
        Button button=(Button)v;
        mButtonCount--;

        isnull();
        LayoutInflater inflater=LayoutInflater.from(this);
        //使用三個參數的方式加載Button
        Button buttongridaddlocate=(Button)inflater.inflate(R.layout.button_subscribe_grey, mGridLayoutAdd,false);
        for (int i=mButtonType.size()-1;i>=0;i--){
            if (mButtonType.get(i).equals(button.getText().toString()))
            {
                mButtonType.remove(i);
            }
        }
        for (int i=mButtonTypeAdd.size()-1;i>=0;i--){
            if (mButtonTypeAdd.get(i).equals(button.getText().toString()))
            {
                mButtonTypeAdd.add(button.getText().toString());
            }
        }
        if(type==TYPE_LOCATE){
            buttongridaddlocate.setText("随時定位");

        }
        if (type==TYPE_CUSVISIT){
            buttongridaddlocate.setText("客戶拜訪");

        }
        if (type==TYPE_CusALL){
            buttongridaddlocate.setText("客戶總量");
        }
        if (type==TYPE_AddNewCus){
            buttongridaddlocate.setText("新增客戶");
        }
        mGridLayoutAdd.addView(buttongridaddlocate);
        buttongridaddlocate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View  view) {

                addbuttontogridblank(view, type);
            }
        });


    }
    /**
     * 該方法用于删除灰色的按鈕同時添加紅色的按鈕
     * @param
    private void addbuttontogridblank(View v,final int type) {
        mGridLayoutAdd.removeView(v);
        Button button= (Button) v;
        mButtonCount++;
        isnull();
        LayoutInflater inflater=LayoutInflater.from(this);
        Button buttongridblanklocate=(Button)inflater.inflate(R.layout.button_subscribe_red,mGridLayoutBlank,false);
        for (int i=mButtonType.size()-1;i>=0;i--){
            if (mButtonType.get(i).equals(button.getText().toString()))
            {
                mButtonType.add(button.getText().toString());
            }
        }

        for (int i=mButtonTypeAdd.size()-1;i>=0;i--){
            if (mButtonTypeAdd.get(i).equals(button.getText().toString()))
            {
                mButtonTypeAdd.remove(button.getText().toString());
            }
        }
        if(type==TYPE_LOCATE){
            buttongridblanklocate.setText("随時定位");

        }
        if (type==TYPE_CUSVISIT){
            buttongridblanklocate.setText("客戶拜訪");

        }
        if (type==TYPE_CusALL){
            buttongridblanklocate.setText("客戶總量");

        }
        if (type==TYPE_AddNewCus){
            buttongridblanklocate.setText("新增客戶");

        }
        mGridLayoutBlank.addView(buttongridblanklocate);
        buttongridblanklocate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                deletebuttonfromgridblank(view, type);
            }
        });
    }
    private void  chartsubscribefindview(){
        //傳回按鈕
        mButtonBackToChart= (Button) findViewById(R.id.button_backtochart_chartsubscribe);
        //已添加部分沒有任何資訊時需要顯示的按鈕
        mButtonBlank= (Button) findViewById(R.id.button_nullsubscribe);
        mButtonBackToChart.setOnClickListener(this);
        mButtonBlank.setOnClickListener(this);

        //已添加的布局
       mGridLayoutBlank= (GridLayout) findViewById(R.id.gridlayoyt_blank_chartsubscribe);
       //可添加的布局
       mGridLayoutAdd= (GridLayout) findViewById(R.id.gridlayoyt_canadd_chartsubscribe);

        mButtonGridBlankLocate= (Button) findViewById(R.id.button_gridlayout_blank_locate);
        mButtonGridBlankCusVisit= (Button) findViewById(R.id.button_gridlayout_blank_cusvisit);
        mButtonGridBlankAddNewCus= (Button) findViewById(R.id.button_gridlayout_blank_addnewcus);
        mButtonGridBlankCusAll= (Button) findViewById(R.id.button_gridlayout_blank_cusall);

        mButtonGridBlankCusAll.setOnClickListener(this);
        mButtonGridBlankLocate.setOnClickListener(this);
        mButtonGridBlankCusVisit.setOnClickListener(this);
        mButtonGridBlankAddNewCus.setOnClickListener(this);
        mButtonType=new ArrayList<>();
        mButtonTypeAdd=new ArrayList<>();
        mSharedPreferences= getSharedPreferences("buttontype",MODE_PRIVATE);





    }

    private void isnull(){
        if(mButtonCount==0){
            mButtonBlank.setVisibility(View.VISIBLE);
        }else{
            mButtonBlank.setVisibility(View.GONE);
        }
    }

    @Override
    protected void onResume() {
        int size = mSharedPreferences.getInt("Button_type", 0);
        for(int i=0;i<size;i++) {
            mButtonType.add(mSharedPreferences.getString("Button_" + i, null));
        }
        List<String> mButtonTypeAddCopy=mButtonTypeAdd;
        for (int i=mButtonTypeAddCopy.size()-1;i>=0;i++){
            switch (mButtonTypeAddCopy.get(i)){
                case "随時定位":deletebuttonfromgridblank(mButtonGridBlankLocate, TYPE_LOCATE);
                    break;
                case "客戶拜訪":deletebuttonfromgridblank(mButtonGridBlankCusVisit, TYPE_CUSVISIT);break;
                case "新增客戶":deletebuttonfromgridblank(mButtonGridBlankAddNewCus, TYPE_AddNewCus);break;
                case "客戶總量":deletebuttonfromgridblank(mButtonGridBlankCusAll, TYPE_CusALL);break;
            }
        }

        super.onResume();
    }
}