用户界面View(一)

 没有目标的人永远为有目标的人去努力。


本讲内容:View


一、LayoutInflater 加载布局

在Activity中调用setContentView()方法来加载布局。其实setContentView()方法的内部也是使用LayoutInflater来加载布局的。


1、LayoutInflater的基本用法:首先获取到LayoutInflater的实例,有两种方法可以获取到

LayoutInflater layoutInflater = LayoutInflater.from(context);  
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  


第一种就是第二种的简单写法,只是Android给我们做了一下封装而已。得到了LayoutInflater的实例之后就可以调用它的inflate()方法来加载布局了,如下所示:

layoutInflater.inflate(resource, root);  
inflate(resource, root, attachToRoot)
inflate()方法一般接收两个参数,第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。这样就成功成功创建了一个布局的实例,之后再将它添加到指定的位置就可以显示出来了。


下面来讲解这三种情况的区别

1、inflate(layoutId, null )
2、inflate(layoutId, root, false )
3、inflate(layoutId, root, true ) 

示例一:下面是res/layout/activity_main.xml 布局文件:(一个空布局)

 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_layout"
    android:orientation="vertical" >

</LinearLayout>


下面是res/layout/button_layout.xml 布局文件:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="250dp"
    android:layout_height="150dp" 
    android:text="Button">

</Button>


下面是MainActivity.java主界面文件:

public class MainActivity extends Activity {
	private LinearLayout mainLayout;

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mainLayout=(LinearLayout) findViewById(R.id.main_layout);
		LayoutInflater layoutInflater=LayoutInflater.from(this);
		View buttonLayout=layoutInflater.inflate(R.layout.button_layout, null);//1
//		View buttonLayout=layoutInflater.inflate(R.layout.button_layout,mainLayout,false);//2
//		View buttonLayout=layoutInflater.inflate(R.layout.button_layout,mainLayout,true);//3
		mainLayout.addView(buttonLayout);
	}
}

分别看效果图:(由上面三行代码的变化)

  
               图1                                       图2

图3(报错)

FATAL EXCEPTION: main  
02.java.lang.UnsupportedOperationException:   
03.addView(View, LayoutParams) is not supported in View  


可以看到

inflater(resId, null )不能正确处理宽高的值,但是inflater(resId,parent,false)可以处理。而inflater(resId,parent,true)报错了


下面我们通过源码解析:

这三个方法,最终都会执行下面的代码:

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


看25-30:

if (root != null) {  
        params = root.generateLayoutParams(attrs);  
            if (!attachToRoot) {  
                temp.setLayoutParams(params);  
           }  
   } 

可以看到,当root不为null,attachToRoot为false时,为temp设置了LayoutParams


看32-34:

if (root != null && attachToRoot) {  
       root.addView(temp, params);  
    } 

当root不为null,attachToRoot为true时,将tmp按照params添加到root中。


看35-37:

if (root == null || !attachToRoot) {  
       result = temp;  
    }  

如果root为null,或者attachToRoot为false则,将temp赋值给result。


从上面的分析已经可以看出:

Inflate(resId , null ) 只创建temp ,返回temp

Inflate(resId , parent, false )创建temp,然后执行temp.setLayoutParams(params);返回temp

Inflate(resId , parent, true ) 创建temp,然后执行root.addView(temp, params);最后返回root


由上面已经能够解释:

Inflate(resId , null )不能正确处理宽和高是因为:layout_width,layout_height是相对了父级设置的(所以不叫做width,heigth),必须与父级的LayoutParams一致。而此temp的getLayoutParams为null

Inflate(resId , parent,false ) 可以正确处理,因为temp.setLayoutParams(params);这个params正是root.generateLayoutParams(attrs);得到的。

Inflate(resId , parent,true )不仅能够正确的处理,而且已经把resId这个view加入到了parent,并且返回的是parent,和以上两者返回值有绝对的区别。上面这里报错原因是:因为源码中调用了root.addView(temp, params);而此时的root是我们的LinearLayout,LinearLayout为View的子类:

java.lang.UnsupportedOperationException:   
02.addView(View, LayoutParams) is not supported in View  


针对1、Inflate(resId , null )中,如果button_layout.xml改成下面这样

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

    <Button
        android:layout_width="250dp"
        android:layout_height="150dp"
        android:text="Button" >
    </Button>

</RelativeLayout>

这里我们又加入了一个RelativeLayout,此时的Button存在与RelativeLayout之中,layout_width和layout_height属性也就有作用了。当然,处于最外层的RelativeLayout,它的layout_width和layout_height则会失去作用。现在重新运行一下程序,结果如下图所示:


对此我们会疑惑?平时在Activity中指定布局文件的时候,最外层的那个布局是可以指定大小的,layout_width和layout_height都是有作用的。确实,这主要是因为,在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout,所以layout_width和layout_height属性才会有效果。最外层的LinearLayout父布局是一个FrameLayout,而这个FrameLayout就是由系统自动帮我们添加上的。任何一个Activity中显示的界面其实主要都由两部分组成,标题栏和内容布局。标题栏就是在很多界面顶部显示的那部分内容,比如刚刚我们的那个例子当中就有标题栏,可以在代码中控制让它是否显示。而内容布局就是一个FrameLayout,这个布局的id叫作content,我们调用setContentView()方法时所传入的布局其实就是放到这个FrameLayout中的,这也是为什么这个方法名叫作setContentView(),而不是叫setView()。

Activity窗口的组成图:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值