是一种非常常用的布局,这个布局会将它所包含的控件在线性方向(垂直,水平)上依次排列。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top" android:text="Button 1"/> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="button 2"/> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:text="Button 3"/> </LinearLayout>android:orientation 属性指定排列的方向,垂直为vertical,水平为horizontal(默认) 注意: 如果LinearLayout的排列方向是horizontal,内部的控件就绝对不能将宽度指定为match_parent,因为这样的话,单独一个控件就会将整个水平方向占满,其他控件就没有可放置的位置了。 同样的道理,如果LinearLayout的排列方向是vertial的控件就不能将高度指定为match_parent
android:layout_gravity 属性 和android:gravity属性看起来有些类似,android:gravity属性指定文字在控件中的对齐方式,而android:layout_gravity用于指定控件在布局中的对齐位置,可选值和android:gravity差不多,top,bottom,left,right,center等,可以用 | 来同时指定多个值 注意: 当LinearLayout的排列是horizontal时,只有垂直方向上的对齐方式才会生效,因为此时水平方向上的长度是不固定的。 同样的道理,当LinearLayout的排列方向是vertical时,只有水平方向上的对齐方式才会生效。
android:layout_weight 属性
这个属性允许我们使用比例的方式来指定控件的大小,他在手机屏幕的适配性方面可以起到非常重要的作用。 列如,我们正在编写一个消息发送页面,需要一个文本编辑框和一个按钮。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/input_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type something"/> <Button android:id="@+id/send" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Send"/> </LinearLayout>我们使用了android:layout_weight属性,此时控件的宽度就不应该在由android:layout_width来决定了,这里指定0dp是一种比较规范的写法,另外,dp是Android中用于指定控件大小,间距等属性的单位。
要点: 1、子控件并未占满父控件的所有空间 2、layout_weight的值用于指定空闲空间的分配比例
是将剩余空间分配,如果想要按比例分配,就将宽度/高度设置为0dp,意味着不占用空间,剩余的就是全部空间了,那样就可以按自己想要分配的比例分配了。
这里EditText和Button里都将android:layout_weight属性设置为1,这表示EditText和Button将在水平方向平分宽度。
原理:系统会先把LinearLayout下所有控件指定的layout_weight的额值相加得到一个总值,然后每个控件所占大小的比例就是用 该控件的layout_weight值 除以 总值。因此,如果想让EditText占据屏幕宽度的3/5,Button占据屏幕宽度的2/5,只需要将EditText的layout_weight改为3,Button的layout_weight改为2。
我们还可以通过指定部分控件的layout_weight值来实现更好的效果
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/input_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type something"/> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send"/> </LinearLayout>这里仅仅指定EditText的android:layout_weight属性,所以EditText会占满屏幕所有剩余的空间。
它可以通过相对定位的方式让控件出现在布局的任何位置 属性 第一组(外切) android:layout_below android:layout_above android:layout_toLeftOf android:layout_toRightOf 第二组(内切) android:layout_alignLeft android:layout_alignRight android:layout_alignTop android:layout_alignBottom 第三组 基准线的概念,就像英语信纸中(四线)第三条线的作用,用来规范字母,使其整齐。 android:layout_alignBaseline 以上属性是相对于控件布局,值为引用一个控件的id@id/idName。下面的属性是相对于父控件布局,值为trueorfalse,因为每个控件只有一个直接父控件。 第四组(内切) android:layout_alignParentLeft android:layout_alignParentRight android:layout_alignParentTop android:layout_alignParentBottom 第五组(中央) android:layout_centerInParent android:layout_centerHorizontal android:layout_centerVertical
相对父布局定位
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Button 1"/> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:text="Button 2"/> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Button 3"/> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:text="Button 4"/> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:text="Button 5"/> </RelativeLayout>相对控件定位
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/button3" android:layout_toLeftOf="@id/button3" android:text="Button 1"/> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/button3" android:layout_toRightOf="@id/button3" android:text="Button 2"/> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Button 3"/> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/button3" android:layout_toLeftOf="@id/button3" android:text="Button 4"/> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/button3" android:layout_toRightOf="@id/button3" android:text="Button 5"/> </RelativeLayout>注意:当一个控件引用另一个控件的id时,一定要定义在引用控件的后面不然会出现找不到id的情况。(尽管我没有这样做也成功运行)
这种布局没有方便的定位方式,所有控件都会默认摆放在布局的左上角。由于定位方式欠缺,导致应用场景很少。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is TextView"/> <ImageView android:id="@+id/image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> </FrameLayout>由于ImageView是在TextView 之后添加的,因为图片压在文字之上。我们可以通过layout_gravity属性来指定对齐方式。
在这种布局中,我们可以不再使用wrap_content,match_parent等方式来指定控件的大小,而是允许直接指定控件在布局中所占的百分比,这样的话就能轻松实现平分布局甚至是任意比例分割布局的效果。
由于LinearLayout本身已经支持按比例指定控件的大小了,因此百分比布局职位FrameLayout 和 RelativeLayout 进行扩展,提供PercentFrameLayout和 PercentRelativeLayout两个全新布局。
百分比布局定义在support库,使用时需要在项目的build.gradle中添加百分比布局库的依赖。
implementation 'androidx.percentlayout:percentlayout:1.0.0' <?xml version="1.0" encoding="utf-8"?> <androidx.percentlayout.widget.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn1" android:layout_gravity="left|top" android:text="btn1" android:textAllCaps="false" app:layout_heightPercent="50%" app:layout_widthPercent="50%" /> <Button android:id="@+id/btn2" android:layout_gravity="right|top" android:text="btn2" android:textAllCaps="false" app:layout_heightPercent="50%" app:layout_widthPercent="50%" /> <Button android:id="@+id/btn3" android:layout_gravity="left|bottom" android:text="btn3" android:textAllCaps="false" app:layout_heightPercent="50%" app:layout_widthPercent="50%" /> <Button android:id="@+id/btn4" android:text="btn4" android:layout_gravity="right|bottom" app:layout_widthPercent="50%" app:layout_heightPercent="50%"/> </androidx.percentlayout.widget.PercentFrameLayout>下面制作一个类似iPhone的标题栏,对于引用布局来说,最大的优势在于可以减少代码的重复,重复的布局内容只需要在新建的布局中建立好,然后需要使用时,在引用的XML文件使用include将布局引入即可 新建一个布局 title.xml,代码如下所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/title_back" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Back" android:textColor="#fff"/> <TextView android:id="@+id/title_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="Title text" android:textSize="24sp"/> <Button android:id="@+id/title_edit" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Edit" android:textColor="#fff"/> </LinearLayout>现在标题栏布局已经编写完成了,剩下的就是如何在程序中使用这个标题栏了,修改 activity_main.xml中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/title"/> </LinearLayout>没错!我们只需要通过一行 include语句将标题栏布局引入进来就可以了。
最后别忘了在 MainActivity中将系统自带的标题栏隐藏掉,代码如下所示:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ActionBar actionBar = getSupportActionBar(); if(actionBar != null){ actionBar.hide(); } }引入布局的技巧确实解决了重复编写布局代码的问题,但是如果布局中有一些控件要求能够响应事件,我们还需要在每个活动中为这些控件单独编写一次事件注册的代码。比如说标题栏中返回按钮,其实不管在哪一个活动中,这些按钮的功能都是相同的,即销毁当前活动。而如果每一个活动都需要注册一遍返回按钮的点击事件,无疑会增加很多重复代码,这种情况下最好是使用自定义控件的方式来解决。
新建一个类TitleLayout 继承自LinearLayout,让它成为我们自定义的标题栏控件,代码如下
public class TitleLayout extends LinearLayout { public TitleLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.title,this); } }首先重写LinearLayout带有两个参数的构造函数,在布局中引入TitleLayout控件就需要这个构造函数,然后需要在构造函数中对标题栏布局进行动态加载,这需要借助LayoutInflater 来实现,通过LayoutInflater 的from()方法就可以构建出一个LayoutInflater对象,然后调用其inflate()方法就可以动态加载一个布局文件,inflate()方法接受两个参数,第一个参数是要加载的布局文件的id,这里我们传入R.layout.title,第二个参数是给加载好的布局在添加一个父布局,这里我们想要指定为TitleLayout,于是直接传入this.
现在自定义控件已经创建好了,然后我们需要在布局文件中添加这个自定义控件,修改activity_main.xml中的代码,如下所示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.uicustomviews.TitleLayout android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>添加自定义控件和添加普通控件的方式基本一样,只不过在添加自定义控件的时候,我们需要指明控件的完整类名,包名在这里不可省略。
重新运行程序发现,和直接引入布局的效果是一样的。然后我们为标题栏中的按钮注册点击事件,修改TitleLayout中的代码,如下所示
public class TitleLayout extends LinearLayout { public TitleLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.title,this); Button titleBack = (Button) findViewById(R.id.title_back); Button titleEdit = (Button) findViewById(R.id.title_edit); titleBack.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { ((Activity)getContext()).finish(); } }); titleEdit.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getContext(),"Your clicked Edit button.",Toast.LENGTH_SHORT).show(); } }); } }这样的话,每当我们在一个布局中引入TitleLayout时,返回按钮和编辑按钮的点击事件就已经自动实现好了,这就省却了很多编写重复代码的工作。