Android 开发(02)UI布局方式

一、View 和 ViewGroup

1、View

  • View:所有可视化控件的父类,提供组件描绘和事件处理方法
  • 常用的属性:id、background、padding等

2、ViewGroup

  • View类的子类,可以看成是视图的容器,可以控制里面的组件的摆放,后面要讲到的各种布局就是这个类的子类
  • 这是一个抽象类,使用的时候都是用的这个类的子类,这个还有两个重要的内部类,分别是:
    • ViewGroup.LayoutParams:这个内部类控制组件的宽高,控制组件的大小
    • ViewGroup.MarginLayoutParams:这个控制组件的外边距margin,进而控制组件的位置,这个是前者的子类

3、UI布局方式

  • Android里的图形界面都是由View和ViewGroup以及他们的子类构成的
  • Android UI中的控件都是按照这种层次树的结构堆叠得到的
    在这里插入图片描述

4、绘制UI界面的方法

使用 xml 布局文件
  • 创建 xml 布局文件activity_main.xml
  • 在相应的 activity 类中用setContentView(R.layout.activity_main)指定相应的布局文件即可
使用 Java 代码
  • 在相应的 activity 类里面创建布局管理器和需要的UI组件
  • 设置布局管理器属性,设置UI组件属性(含layout属性)并添加事件处理的方法
  • setContentView方法在 activity 里面指定布局管理器
  • 用布局管理器的addView方法添加需要的UI组件
xml和java混合使用
  • xml 定义一些静态的组件,java代码可以动态添加组件和改变已有组件
  • 在java 代码中可以通过 findViewById 这样的方来获取 xml 文件中已经存在的元素
使用自定义的 View
  • 布局文件使用FrameLayout布局管理器
  • 定义自定义的类继承 View,重写 onDraw 方法
  • 最后在activity 中使用自定义的 View

二、RelativeLayout(相对布局)

  • Android中常用的布局管理器:LinearLayout(线性布局)、RelativeLayout(相对布局)、TableLayout(表格布局) 、FrameLayout(帧布局)、AbsoluteLayout(绝对布局基本不用)、GridLayout(网格布局)
  • RelativeLayout + LinearLayout是当前使用最广泛的布局方式
  • Gridlayout 基本是可以完全替代 TableLayout 了,能实现相同的功能,而且更加灵活
  • FrameLayout 在特定的场景还是比较好用的

1、根据父容器定位

在这里插入图片描述
在这里插入图片描述

2、根据兄弟组件定位

在这里插入图片描述

梅花布局示例
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:id="@+id/RelativeLayout1"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent" >    
    
    <!-- 这个是在容器中央的 -->    
        
    <ImageView    
        android:id="@+id/img1"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_centerInParent="true"    
        android:src="@drawable/pic1"/>    
        
    <!-- 在中间图片的左边 -->    
    <ImageView    
        android:id="@+id/img2"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_toLeftOf="@id/img1"    
        android:layout_centerVertical="true"    
        android:src="@drawable/pic2"/>    
        
    <!-- 在中间图片的右边 -->    
    <ImageView    
        android:id="@+id/img3"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_toRightOf="@id/img1"    
        android:layout_centerVertical="true"    
        android:src="@drawable/pic3"/>    
        
    <!-- 在中间图片的上面-->    
    <ImageView    
        android:id="@+id/img4"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_above="@id/img1"    
        android:layout_centerHorizontal="true"    
        android:src="@drawable/pic4"/>    
        
    <!-- 在中间图片的下面 -->    
    <ImageView    
        android:id="@+id/img5"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_below="@id/img1"    
        android:layout_centerHorizontal="true"    
        android:src="@drawable/pic5"/>    
    
</RelativeLayout>

3、Margin 和 Padding

  • 这里的二者的概念基本和 html 中的概念是一致的,margin 可以设置为负数
    在这里插入图片描述

三、LinearLayout(线性布局)

  • 什么时候使用线性布局: 当多个组件需要横向或者纵向排列的时候使用

1、常用属性

在这里插入图片描述

layout_gravity 使用说明
  • 当父组件 android:orientation="vertical"(垂直) 时, layout_gravity 只有水平方向的设置才起作用,垂直方向的设置不起作用,即:left,right,center_horizontal是生效的
  • android:orientation="horizontal"时,layout_gravity 只有垂直方向的设置才起作用,水平方向的设置不起作用,即:top,bottom,center_vertical 是生效的
  • 这一点很重要的,很多时候我们的位置设置不生效,可以排查一下是否是这个原因

2、Weight权重

很重要的说明
  • weight 分割的实际是剩余的空间,而不是指整个父容器的空间
  • 所谓的剩余的空间就是在去除组件的宽度或者高度的情况下还剩下的空间
  • 这里具体是去除宽还是高要看线性布局是横向还是纵向的排列
  • 当想让组件的宽或者高完全按照 weight 的设置来分割空间的话,直接将宽或者高设置为0dp即可,这样分割的就是所有的空间了
权重计算方法
  • 当宽高设置为 wrap_content
    • 除去内容的宽度,以下示例的三个 textView 组件的所占的宽度的比例就是1:2:3
    • 这里的线性布局是横向的,所以这里是 width 设置为 wrap_content
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:id="@+id/LinearLayout1"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent"  
    android:orientation="horizontal" >    
    
    <TextView    
        android:layout_weight="1"    
        android:layout_width="wrap_content"    
        android:layout_height="fill_parent"    
        android:text="one"     
        android:background="#98FB98"    
     />    
     <TextView    
        android:layout_weight="2"    
        android:layout_width="wrap_content"    
        android:layout_height="fill_parent"    
        android:text="two"     
        android:background="#FFFF00"    
     />    
     <TextView    
        android:layout_weight="3"    
        android:layout_width="wrap_content"    
        android:layout_height="fill_parent"    
        android:text="three"     
        android:background="#FF00FF"    
     />    
    
</LinearLayout>  
  • 当宽高设置为 match_parent:示例代码的显示结果是第三个子组件不见了,前面两个组件的比例是2:1,导致这种情况的原因如下:
    • 假设我们的父容器的宽度为 x,fill_parent就意味着我们三个组件的宽度都是 x
    • 那么容器的剩余的宽度就是 x-3x=-2x
    • 那么按照我们分配的权重最后三个子组件的宽度就是:x+(-2x1/6)、x+(-2x2/6)、x+(-2x*3/6)
    • 最后计算的结果三个子组件的宽度依次就是:2/3x 1/3x 0
    • 这样就能解释下面的现象了
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:id="@+id/LinearLayout1"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent" >    
    
    <TextView    
        android:layout_weight="1"    
        android:layout_width="fill_parent"    
        android:layout_height="fill_parent"    
        android:text="one"     
        android:background="#98FB98"    
     />    
     <TextView    
        android:layout_weight="2"    
        android:layout_width="fill_parent"    
        android:layout_height="fill_parent"    
        android:text="two"     
        android:background="#FFFF00"    
     />    
     <TextView    
        android:layout_weight="3"    
        android:layout_width="fill_parent"    
        android:layout_height="fill_parent"    
        android:text="three"     
        android:background="#FF00FF"    
     />    
    
</LinearLayout>

3、divider分割线

  • ktv_line_div 是 drawable 文件夹下的一张准备好的分割线图片
  • showDividers控制分割线的位置,可选的值有:none、beginning、end、middle
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:id="@+id/LinearLayout1"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    
    android:divider="@drawable/ktv_line_div"  
    android:showDividers="middle"  
    android:dividerPadding="10dp"
    
    android:orientation="vertical"        
    tools:context="com.stan.MainActivity" >  
  
    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="按钮1" />  
  
    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="按钮2" />  
  
    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="按钮3" />  
  
</LinearLayout>

四、FrameLayout(帧布局)

  • 帧布局最大的特点是多个子组件存在,后面的会覆盖前面的
  • 帧布局中的组件还是可以通过 layout_gravity 属性指定到合适的位置
  • 前景图像:永远处于帧布局最上面,不会被覆盖的图片,相关属性如下
android:foreground:设置帧布局容器的前景图像
android:foregroundGravity:设置前景图像显示的位置

五、TableLayout(表格布局)

1、行列确定

  • 一个tablerow一行,一个单独的组件一行,这里的单独组件指的是没有被 tableRow 包裹的组件
  • 多少列则是看 tableRow中 的组件个数,组件最多的就是TableLayout的列数
tablerow的宽高
  • layout_width属性默认是fill_parent的,自己设置不会生效
  • layout_height默认是wrap_content的,可以自己设置大小

2、常用属性

列的隐藏、伸展、收缩
  • android:collapseColumns:设置需要被隐藏的列的序号
  • android:shrinkColumns:设置允许被收缩的列的列序号
  • android:stretchColumns:设置运行被拉伸的列的列序号
  • 注意:以上这三个属性的列号都是从0开始算的,可以设置多个,用逗号隔开比如"0,2",如果是所有列都生效,则用"*"号即可
单元格属性
  • android:layout_column=2:指定该单元格在第几列显示
  • android:layout_span=4:指定该单元格占据的列数
  • 注意:这两个属性都是对容器中的组件而言的,而不是 TableLayout 的属性
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:id="@+id/TableLayout1"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent"    
    tools:context=".MainActivity"     
    android:stretchColumns="0,3"    
    android:gravity="center_vertical"    
    android:background="#66FF66"    
    >    
        
    <TableRow>    
        <TextView />    
        <TextView     
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:text="用户名:"/>    
        <EditText     
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:minWidth="150dp"/>    
        <TextView />    
    </TableRow>    
        
    <TableRow>    
        <TextView />    
        <TextView     
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:text="密  码:"        
        />    
        <EditText     
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:minWidth="150dp"        
        />    
        <TextView />    
    </TableRow>    
        
    <TableRow>    
        <TextView />    
        <Button     
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:text="登陆"/>    
        <Button    
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:text="退出"/>    
        <TextView />    
    </TableRow>    
        
</TableLayout>

六、GridLayout(网格布局)

  • table和grid比较:使用 GridLayout 比 TableLayout 更加灵活
    • grid 可以跨行跨列,而table只能跨列
    • grid 中的组件一行放不下的话会自动换行,而table的话超出的内容就不显示了(当然是可以通过收缩列来显示完全的)

1、属性总结

基本的属性(GridLayout)
  • orientation:就是一个网格是水平展开还是垂直展开
  • rowCount:设置 orientation 为 vertical 的话指定多少行换列
  • columnCount:设置 orientation 为 horizontal 的话就指定多少列换行
行列的设置(子组件)

在这里插入图片描述

  • 前面三个属性分别是设置子组件所在的列,横跨几个列,和列的权重,最后三个就是设置相应的行了
  • 如果设置了组件横跨几行或者几列,需要设置 android:layout_gravity = "fill" 进行填充,否则是看不到跨行跨列的效果的

2、计算器布局的实现

<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/GridLayout1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:columnCount="4"
    android:orientation="horizontal"
    android:rowCount="6">

    <TextView
        android:layout_columnSpan="4"
        android:layout_gravity="fill_horizontal"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:background="#FFCCCC"
        android:text="0"
        android:textSize="50sp" />

    <Button
        android:layout_columnSpan="2"
        android:layout_gravity="fill_horizontal"
        android:text="回退" />

    <Button
        android:layout_columnSpan="2"
        android:layout_gravity="fill_horizontal"
        android:text="清空" />

    <Button android:text="+" />

    <Button android:text="1" />

    <Button android:text="2" />

    <Button android:text="3" />

    <Button android:text="-" />

    <Button android:text="4" />

    <Button android:text="5" />

    <Button android:text="6" />

    <Button android:text="*" />

    <Button android:text="7" />

    <Button android:text="8" />

    <Button android:text="9" />

    <Button android:text="/" />

    <Button android:text="." />

    <Button android:text="0" />

    <Button android:text="=" />

</GridLayout>

3、布局管理器的嵌套原则

  • 有且只有一个根布局管理器
  • 根布局管理器必须有 xmlns 属性(名称空间)
  • 嵌套太多的话影响性能呀,所以少嵌套为好

七、ConstraintLayout(约束布局)

1、为什么使用ConstraintLayout

  • 现在使用最频繁的布局的方案是 LinearLayout 和 Relativelayout
  • 布局复杂的界面的时候使用 LinearLayout 往往需要多层的嵌套,在纵向和横向进行布局
  • 相对于 Relativelayout ,ConstraintLayout使用起来比更灵活,性能更出色,还有一点就是ConstraintLayout可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型
  • 实际使用的时候呢,ConstraintLayout使用起来比较繁琐需要设置很多的属性,没有特殊的需求的话还是使用线性和相对布局比较简单

2、使用 ConstraintLayout 进行布局

相对定位属性
  • 需要注意的是 baseline 相关的属性,使用这个属性可以让两个组件的文字在同一水平线上
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf

layout_constraintBaseline_toBaselineOf

在这里插入图片描述

角度定位:圆心、角度、半径
<TextView
	android:id="@+id/TextView1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content" />

<TextView
	android:id="@+id/TextView2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:layout_constraintCircle="@+id/TextView1"
	app:layout_constraintCircleAngle="120"
	app:layout_constraintCircleRadius="150dp" />
  • 上面代码的意思是TextView2的中心在TextView1的中心的120度,距离为150dp,效果如下:
    在这里插入图片描述
边距:margin、goneMargin
  • margin:跟别的布局没有什么大的差别,但实际上控件在ConstraintLayout里面要实现margin,必须先约束该控件在ConstraintLayout里的位置;下面的代码如果去除了最后的两个约束的属性的话,margin 的设置是不会生效的
<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp" 
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>
  • goneMargin:主要用于控件可见性被设置为gone的时候使用的margin值,也就是可见的时候这个 margin 不会生效
居中和偏移
  • 居中
    ConstraintLayout 中居中的写法如下,如果只想水平居中设置 left 和 right 即可,垂直居中同理,感觉还是 relativeLayout 方便
app:layout_constraintBottom_toBottomOf="parent" 
app:layout_constraintLeft_toLeftOf="parent" 
app:layout_constraintRight_toRightOf="parent" 
app:layout_constraintTop_toTopOf="parent"
  • 偏移
    可以通过设置 margin 实现,也可以通过设置如下的属性实现。这里的layout_constraintHorizontal_bias
    设置为1的话就是最右边,0是最左边,0.5的话就是居中。对应当然有一个垂直方向上的属性
<TextView
        android:id="@+id/TextView1"
        ...
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
尺寸约束
  • 使用 wrap_content
    使用 android:minWidth 等属性可以约束宽高的最大最小值,当ConstraintLayout为1.1版本以下时,使用这些属性需要加上强制约束,比如:app:constrainedWidth=”true”

  • 使用 0dp 替代 match_parent

  • 设置宽高比app:layout_constraintDimensionRatio="H,2:3"指的是 高:宽=2:3

链式结构
  • 下面的写法构成了一条横向的链式结构,可以在链头也就是第一个组件设置链的风格,也就是相应的chainStyle属性,还有weight 相关的属性也是可以设置分布的权重的
<TextView
	android:id="@+id/TextView1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="text1"
	app:layout_constraintBottom_toBottomOf="parent"
	app:layout_constraintHorizontal_chainStyle="packed"
	app:layout_constraintLeft_toLeftOf="parent"
	app:layout_constraintRight_toLeftOf="@+id/TextView2"
	app:layout_constraintTop_toTopOf="parent" />

<TextView
	android:id="@+id/TextView2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="text2"
	app:layout_constraintBottom_toBottomOf="parent"
	app:layout_constraintLeft_toRightOf="@+id/TextView1"
	app:layout_constraintRight_toLeftOf="@+id/TextView3"
	app:layout_constraintTop_toTopOf="parent" />

<TextView
	android:id="@+id/TextView3"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="text3"
	app:layout_constraintBottom_toBottomOf="parent"
	app:layout_constraintLeft_toRightOf="@+id/TextView2"
	app:layout_constraintRight_toRightOf="parent"
	app:layout_constraintTop_toTopOf="parent" />
  • chainStyle 的属性的风格如图所示
    在这里插入图片描述

3、辅助工具使用

屏障Barrier
  • 下面的代码能够确保 textView3 在 textView1 和 textView2 的右边,barrier 就是起了一个屏障的作用
<TextView
	android:id="@+id/TextView1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content" />

<TextView
	android:id="@+id/TextView2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:layout_constraintTop_toBottomOf="@+id/TextView1" />

<android.support.constraint.Barrier
	android:id="@+id/barrier"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:barrierDirection="right"
	app:constraint_referenced_ids="TextView1,TextView2" />

<TextView
	android:id="@+id/TextView3"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:layout_constraintLeft_toRightOf="@+id/barrier" />
分组Group
  • 下面的代码将 t1 和 t3 分为一组并隐藏
<TextView
	android:id="@+id/TextView1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	tools:ignore="MissingConstraints"
	android:text="t1"/>

<TextView
	android:id="@+id/TextView2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:layout_constraintLeft_toRightOf="@+id/TextView1"
	tools:ignore="MissingConstraints"
	android:text="t2"/>

<TextView
	android:id="@+id/TextView3"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	app:layout_constraintLeft_toRightOf="@id/TextView2"
	tools:ignore="MissingConstraints"
	android:text="t3"/>

<android.support.constraint.Group
	android:id="@+id/group"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:visibility="invisible"
	app:constraint_referenced_ids="TextView1,TextView3" />
辅助线Guideline
  • Guildline像辅助线一样,在预览的时候帮助你完成布局,应用运行时不会显示
  • guideline1为水平辅助线,开始位置是距离顶部50dp,guideline2位垂直辅助线,开始位置为屏幕宽的0.5
<android.support.constraint.Guideline
	android:id="@+id/guideline1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:orientation="horizontal"
	app:layout_constraintGuide_begin="50dp" />

<android.support.constraint.Guideline
	android:id="@+id/guideline2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:orientation="vertical"
	app:layout_constraintGuide_percent="0.5" />

参考文章
https://www.jianshu.com/p/17ec9bd6ca8a
https://www.cnblogs.com/angrycode/p/9739513.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值