天天看点

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();
    }
}