最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【整理】Android UI知识学习与总结之:Layout布局

Android crifan 3732浏览 0评论

【背景】

折腾:

【记录】Android中在Tab页面的Group中显示各种类型的控件

时,发现对于很多复杂点的Android的UI中的概念都不太懂。

所以决定去Android官网:

User Interface | Android Developers

中系统的去学习一下UI方面的知识。

【Android UI知识】

 

UI Overview | Android Developers

1.Android中所有的UI元素都是用View或ViewGroup创建的。

2.ViewGroup下面可以包含一个或多个View或ViewGroup:

android view viewgroup hierachy

3.View和ViewGroup中都有很多的子类,用于支持各种其下的控件(input control)和布局(layout)

4.每个View就是一个不可见的容器,用于组织和管理子View。

5.创建View的话,可以动态通过代码创建,也可以静态的通过xml去定义布局;

6.这类xml布局文件,在app初始化时会被加载,然后将xml转换为对应的对象,然后就可以通过代码去操作了。

7.自己不必从无到有去创建所有的UI,可以利用Android中已有的一些UI组件达到你要的效果。

比如已有的:

 

Layouts | Android Developers

1.布局layout是去定义显示出来的结构如何,用于:activityapp widget

2.可以通过两种方式去定义Layout:

  • xml:静态的,通过xml文件去定义
  • 运行时动态实例化:也就是写代码动态的生成和定义

3.静态的xml定义layout的好处是:

将UI的控制和代码脱离->以实现改动UI而不改动代码去达到更改显示效果的目的

4.动态的用代码创建UI的话,对应的类是:

5.xml中UI定义的名字和属性名,大部分都是和对应的类的名字和属性名是一一对应的。

所以基本上是可以通过xml中的名字推测出对应的类和属性,

也可以从类的名字和属性推测出在xml中定义的名字和属性名

当然,大部分不等于所有,还是会有个别不一样的。

比如:

元素EditText有个text属性,对应的是EditText.setText()

6.每个xml布局文件只有一个根节点,对应的是View或ViewGroup对象。

7.关于Layout中各种属性参数的含义详见:

Layout Resource | Android Developers

8.在已经写好了xml布局配置文件,放在:

/res/layout/main_layout.xml

后,标准的做法是:

在onCreate()中去加载:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

9.对于xml中布局中的id属性:

android:id="@+id/my_button"
  • @:表示XML解析器会自动解析@后面的那对字符串,作为对应的资源的ID
  • +:表示这是一个新的资源名,需要被创建并加到R.java文件中去

10.Android中还有其他很多种类型的ID。

比如:

android:id="@android:id/empty"
  • android:id:表示是android系统中已有的id
  • empty:指的是引用某个已有的,名称叫做empty的那个id

11.标准的xml中定义,代码中引用的做法是:

在xml中定义对应的View控件:

<Button android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/my_button_text"/>

然后代码中用findViewById去引用:

Button myButton = (Button) findViewById(R.id.my_button);

12.给View定义ID,对于RelativeLayout尤其有用:

当时RelativeLayout时,一个view想要定义相对于同级节点的相对位置时,就可以通过对应的ID去引用而找到对应的节点,再去定义相对位置了。

13.view的ID在整个树中未必一定要是唯一的,但是在你所要查找的子树中一定要是唯一的。

如果可以,最好确保ID是唯一的。

 

Layout Parameters

14.XML布局属性名是layout_xxx,是布局的参数定义。

15.每个ViewGroup都实现了一个扩展自

ViewGroup.LayoutParams | Android Developers

的嵌套类。此子类包含了为每个子类去定义尺寸的类型。

图示如下:

android linearlayout layoutparams hierarchy

16.View对象的必备的属性是layout_width和layout_height

其他如margin,border等属性是可选的。

17.宽度和高度的属性值有三种:

  • 绝对值:多少px等等
    • 一般最好不要指定绝对的像素值pixel,而用相对值(单位:pd==density-independent pixel)
    • 其他资源类型内容详见:Resource Types | Android Developers
  • wrap_content:匹配内容
  • fill_parent (API Level 8中已重命名为match_parent) :(在父亲View所允许的显示范围内)尽可能的大

 

Layout Position

18.布局的位置:

一个View的形状是长方形,其各种属性有:

位置:left和top表示;

    • 对应和就是X和Y的坐标
    • 表示对应的,相对于父的View的坐标
    • 可以用getLeft()getTop() 来获得对应的left和top属性。
  • 尺寸:用width宽,height高表示;

单位都是像素pixel

 

Size, Padding and Margins

19.一个视图的尺寸用width和height表示。

一个视图View实际上拥有两组宽和高:

  • 第一组宽和高:可测量的宽(measured width)和可测量的高(measured height
  • 第二组宽和高:宽(width)和高(height)
    • 有时候也叫做:绘制宽(drawing width)和绘制高(drawing height)
    • 决定了,在布局设定后,真正画出来的,显示在界面上的,实际上的宽和高
    • 此宽和高,实际上,可能会和可测量的宽和可测量的高,不太一样
    • 分别通过getWidth()getHeight()获得

20.如果要计算尺寸的话,需要计算填充(padding)大小。

padding用像素表示,用于表示左,上,右,下,部分的填充大小;

padding可以用于去表示一个view的相对的平移量;

设置padding用:setPadding(int, int, int, int)

获得padding用:

尽管一个视图支持padding,但是却不提供对于边距(margin)的支持;

与之对应的是,视图组合ViewGroup支持对应的边距margin。

Common Layouts

21.常见的布局:

每个ViewGroup的子类都提供了一种特别的方式去显示其中的子视图;

下面列出一些Android平台中常见的布局类型:

 

注意:

尽管你可以在一种布局中,内嵌另外一种布局,去实现你的界面设计,但是你应该尽量使得你的布局层次尽量的少,不要嵌套太多;

因为内嵌的布局层次越少,UI绘制的速度就越快;

相对来说,宽的视图,比一个嵌套层次很深的视图,要更好->绘制更快,界面更流畅;

  1. 线性布局Linear Layout
    • android linearlayout small
    • 子视图是单行的,要么横向排列,要么竖着排列
    • 当显示内容长度超过页面显示的部分的时候,支持滚动条
  2. 相对布局Relative Layout
    • android relativelayout small
    • 支持指定一个子视图的位置是相对于另外一个子视图的
    • 以及支持设置不同的对齐方式
  3. 网页视图Web View
    • android webview small
    • 显示web页面

 

Building Layouts with an Adapter

22.当你要显示的内容是动态生成的,而不是可以预先就能确定的,那么可以使用继承自AdapterView的子类,然后动态生成显示布局。

AdapterView的子类可以使用Adapter去绑定数据到对应的布局上面;

Adapter就像一个介于,数据源,和,AdapterView布局,之间的中间人;

  • Adapter从数据源(比如是一个数组或数据库)去检索获得数据,然后将每项转换为对应的视图,然后加到AdapterView布局中

23.最常见的Adapter有:

Filling an adapter view with data

如何给Adapter填入数据

可以从ListViewGridView中生成AdapterView,绑定AdapterView到一个Adapter的实例,其中Adapter负责从外部的数据源中获得数据并创建对应的View去显示;

Android中提供了几种不同的Adapter的子类,以方便的去从不同类型的数据源中去检索数据并建立对应的视图并显示,最常见的两种是:

  • ArrayAdapter
    • 数据源是数组时可以使用此类;
    • 默认情况下,ArrayAdapter会去为每一项去调用toString()然后将其内容放到TextView
    • 例如,如果你有一个字符数组想要显示在一个ListView中,则就可以去用ArrayAdapter的构造函数去初始化,指定对应的布局
    • 代码如下:
    • ArrayAdapter adapter = new ArrayAdapter<String>(this, 
              android.R.layout.simple_list_item_1, myStringArray);
    • 对应参数含义是:
      • 你的程序的上下文Context
      • 用于数组中的每个字符的,包含了TextView的布局
      • 字符数组
    • 然后就再去调用ListViewsetAdapter()即可
    • ListView listView = (ListView) findViewById(R.id.listview);
      listView.setAdapter(adapter);
    • 想要自定义每一项的显示,则可以去重写对应的toString()函数,或者另外创建一个不是TextView的其他视图,专门用于显示对应的字符
      • 比如,你可以为每一项创建对应的ImageView去显示对应的图片
    • 扩展ArrayAdapter类,重写getView()然后返回对应的视图
  • SimpleCursorAdapter
    • 如果你的数据源是光标的话,则可以使用此SimpleCursorAdapter类。
    • 当使用SimpleCursorAdapter时,你必须为Cursor内的每一行指定一个布局,且对应所在的列也应当被添加到对应的布局中
    • 比如,如果你想要创建一个列表,其中包含了人名和电话号码,你可以调用一个查询,然后返回一个Cursor,包含了一行一列,分别包含了每个人名和电话号码;
    • 然后再创建一个字符数组,用于指定你想要布局中的Cursor中的哪一列,以及一个整型数组,用于指定对应每列应当显示的视图
    • String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, 
                              ContactsContract.CommonDataKinds.Phone.NUMBER};
      int[] toViews = {R.id.display_name, R.id.phone_number};
    • 当初始化SimpleCursorAdapter时,传入对应的每一个结果的布局,即Cursor包含了所需结果和对应的两列:
    • SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, 
              R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
      ListView listView = getListView();
      listView.setAdapter(adapter);
    • 然后用SimpleCursorAdapter,使用所提供的布局,去创建在Cursor中的每一行的视图,插入每一个fromColumns项到对应的toViews视图。

 

在程序的生命周期内,如果你改变了你的adapter所依赖的底层数据,那么就要调用notifyDataSetChanged()去通知所依附的视图,让他知道数据有变化,应该要刷新显示了。

 

Handling click events处理点击事件

如果你想要去响应AdapterView中的每一项的点击事件,那么则可以去实现对应的AdapterView.OnItemClickListener接口,比如:

// Create a message handling object as an anonymous class.
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click
    }
};

listView.setOnItemClickListener(mMessageClickedHandler); 

 

 

Activities | Android Developers

1.Activity提供了一个屏幕,可以和用户交互:感觉可以理解为:

Activity==活动窗口==相当于Windows中的WinForm

2.每个Activity会提供一个窗口,用于画对应的图形界面

3.窗口一般都是填满显示的,当然也可能是比屏幕小的,或者是浮动在其他窗口之上。

4.一个App往往包含多个Activity,多个Activity之间一般关系都不是很紧密。

5.典型的例子是,一个App中包含一个MainActivity,往往指的是App第一次启动时候所显示出来的界面

6.为了实现各种不同的操作,一个Activity可以去启动其他另外一个Activity。

7.每次一个新的Activity被启动,则就会停止掉之前那个Activity -> Android系统会把(所有的)Activity(都)放到一个堆栈Stack(back stack)中

所以新启动一个Activity后,该Activity也会被放到back stack中,然后获得焦点。

8.此back stack采取的策略是:后进先出 -> 当用于在当前的Activity操作完毕后,点击Back后,就会:当前的Activity就会被从back stack中弹出(并销毁),然后回到(back stack中的)之前的那个Activity,也就是之前的那个Activity恢复resume了。

9.关于back stack,详见:

Tasks and Back Stack | Android Developers

 

【总结】

目前基本整理完毕,关于Layout布局方面的基本知识了。

转载请注明:在路上 » 【整理】Android UI知识学习与总结之:Layout布局

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
83 queries in 0.175 seconds, using 22.19MB memory