一个Activity的使用主要可以分为Manifest文件、xml布局文件和Java代码的书写三部分。为了使得逻辑较为清晰,大致可以采用先写xml布局文件,再写Java代码,最后在manifest文件中进行注册的方式,因此本文将按照上述顺序对Activity的使用做一些简单的整理。
本文主体是对《Android第一行代码》的笔记整理,同时也会尽量的参考官方文档、技术博客。这里主要是自己的学习笔记,贴在这边仅供大家讨论和批评。
布局文件
Activity是用来提供用户交互的组件,因此在写Activity之前就应该大致的了解自己所要提供的ui,而Activity的ui部分往往是写在xml文件中,因此先书写xml布局文件是顺理成章的。
An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map.
此时我们往往面临着两个问题:
- 可视化编辑好还是代码形式好?
- 写在xml中好还是Activity中直接添加好?
//TODO
五种基本布局
LinearLayout
一个典型的LinearLayout文件如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
这里比较重要的值是android:orientation
,默认情况下它的值为horizontal
,但是建议手动设置它的排列方式。
当排列方向为
horizontal
时,内部控件就不能将宽度设为match_parent
,否则该控件就会将水平方向全部占满,同理,vertical
的时候也是一样。因此,为了避免这样的错误发生,建议手动设置排列方式,同时在内部控件的布局上要记得考虑外部布局的排列方式。
显然,内部控件的标签应该具备相应的控制属性,以使得内部控件更加符合LinearLayout的排列规律。
android:layout_gravity
当排列方向为horizontal时,则只有垂直方向上的对齐方式才会生效,反之亦成立。
android:layout_weight
控件将按照设置的权值进行分布。对应方向长度设为0dp。
应用:
我们可以通过指定部分控件的
layout_weight
来实现更好的效果。比如,在一个左侧为EditText,右侧为发送Button的水平LinearLayout布局中,将Button的宽度设为wrap_content
,而将EditText的weight设为1(任意正数即可),则EditText会占满屏幕所有的剩余空间。一些考虑:
每个控件都可以设置它的width和height两个属性,但是weight却只有一个(而没有width_weight或者height_weight),这也间接的证明了LinearLayout会忽略指定排列方向上的布局。
RelativeLayout
此处属性比较多,但是有一定的规律。
相对于父布局的四个属性:
属性名 | 描述 |
---|---|
android:layout_alignParentTop | 和父布局的顶端对齐 |
android:layout_alignParentBottom | 和父布局的底端对齐 |
android:layout_alignParentLeft | 和父布局的左侧对齐 |
android:layout_alignParentRight | 和父布局的右侧对齐 |
注意:
可以同时指定多个属性,当两个属性不冲突时(如左和上),则该控件会和父布局的左上角对齐;如果两个属性冲突,则该控件则矛盾的两个方向上拉伸。
相对于其他控件的四个属性:
属性名 | 描述 |
---|---|
android:layout_above | 将控件置于指定控件之上 |
android:layout_below | 将控件置于指定控件之下 |
android:layout_toLeftOf | 将右边缘和指定控件的左边缘对齐 |
android:layout_toRightOf | 将左边缘和指定控件的右边缘对齐 |
注意:
此处需要提供指定控件的id,因此id的引用需要出现在定义之后。
此时,这些控件之间互斥(我的左边是你的右边,我的上边是你的下边),那么是否存在“我的左边也是你的左边”的属性呢?
相对于其他控件的另外四个属性:
属性名 | 描述 |
---|---|
android:layout_alignTop | 将上边缘和指定控件的上边缘对齐 |
android:layout_alignBottom | 将下边缘和指定控件的下边缘对齐 |
android:layout_alignLeft | 将左边缘和指定控件的左边缘对齐 |
android:layout_alignRight | 将右边缘和指定控件的右边缘对齐 |
讨论:
上述的属性和相对于父布局的属性较为相似,都是以align开头,表示将自己的某一边缘同指定控件的相应边缘对齐。由此而见,很多属性的设置意图都可以从字面意思上得到。
FrameLayout
应用场景相对较少,也没有任何的定位方式,所有的控件都会摆放在布局的左上角。
TableLayout
不是很常用,稍微了解一下即可。
贴上一段代码:
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow>
<TextView
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="1_1"/>
<TextView
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="1_2"/>
</TableRow>
<TableRow>
<TextView
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="2_1"/>
<TextView
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="2_2"/>
</TableRow>
<TableRow>
<Button
android:layout_height="wrap_content"
android:layout_span="2"
android:text="commit"/>
</TableRow>
</TableLayout>
android:layout_width
在每一个TableRow子标签中,控件是水平排列的(可以认为是一个LinearLayout?),此时不需要指定
android:layout_width
,但是可以指定android:layout_weight
。指定android:layout_weight
会改变控件在水平方向上的排列,但是不会改变单元格的宽度。android:layout_span
可以指定
android:layout_span="2"
来指定该控件将占据两列的宽度。不管span的值如何指定,控件长度将不小于1,不大于所有列的长度。android:strethColumns
需要写在父标签中,例如android:strethColumns=”1”,可以达到自适应屏幕宽度的作用,列号从开始。与指定layout_weight不同,它会改变列的实际宽度。
AbsoluteLayout
传说中的第五个基本布局,但是官方已经不推荐使用了。
引入布局
可以在一个布局文件中引用另外一个布局文件的布局,使用方法非常简单,只需要在需要引用该布局的地方加上<include layout="@layout/layout_tobe_included" />
即可。这样可以复用布局文件,避免重复代码。
一些常见的控件
TextView
android:gravity
指定文字的对齐方式(推荐),可选值有top、bottom、left、right和center等,可以用”|“来同时指定多个值
android:textSize
- android:textColor
Button
可配置的属性同TextView差不多。
在较新的版本中,Button中的字符会自动显示为大写,解决方法是设置
android:textAllCaps="false"
。
EditText
android:hint
设置提示文本,只有text为空时才显示。
android:maxLines
防止输入内容行数过多使得控件不断拉伸,导致界面难看。
ImageView
- android:src
设置图片的来源,如
android:src="@drawable/ic_launcher"
。
ProgressBar
android:visiblity
可选值为visible、invisible和gone。gone表示不仅不可见,还不占用任何屏幕控件。
style
指定ProcessBar的不同样式。
android:max
给进度条设置最大值。
AlertDialog
对话框应该是在运行时才会出现,因此主要在Activity代码中添加。它能够屏蔽掉其他控件的交互能力。
ProgressDialog
与AlertDialog类似,但是可以显示一个进度条。
ListView
并不是特别的复杂
自定义控件
需要添加自定义控件的完整类名。
Activity类代码
生命周期
首先需要管理的是Activity的生命周期。
Activity有四个重要的状态:
1. active or running:
处于前台,或者说在back stack的顶部。
2. paused:
失去focus,但是仍然可见。它将保持所有的状态和成员信息,仍然attach在window manager上,只有在系统内存极低时才会被销毁。
3. stopped:
完全被其他Activity遮盖。仍然保持所有的状态和成员信息,但是不再可见,并且经常在其他地方需要内存时被销毁。
4. 当activity处于paused或者stopped状态时,系统在需要内存时就会请求activity自己finish或者简单的杀死它的进程。当用户再次看到这个Activity时,需要重新启动并且重新获取它之前的状态。
在此插入一个生命周期图:
Activity有三个重要的循环:
* 完整生命周期:
处于onCreate和onDestroy之间。在onCreate方法中做全局的准备工作,在onDestroy方法中释放资源。
* 可见期:
处于onStart和相应的onStop之间。此时用户可以看见Activity,但是可能不处于前台,也不能同用户进行交互。此时可以持有一些同显示相关的资源。
* 前台期:
处于onResume和onPause之前。此时该Activity处于其他所有Activity前面,并且可以与用户交互。Activity可能在resumed和paused之间频繁切换,因此它们中的代码应该尽量保持轻量。
理论的东西讲的太多了,还是直接讲讲生命周期相关的回调函数应该写些什么东东吧。
- onCreate(Bundle savedInstanceState)
再第一次被创建时调用,一般可以在这个方法中完成Activity的初始化动作,比如说加载布局(比如setContentView方法),绑定事件等。 - onPause()
我们通常在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据(通常是交给ContentProvider持有),但是执行速度一定要快,不然会影响到新的栈顶Activity的使用。
注意:
在实现这些生命周期相关的回调函数时,必须首先调用超类的实现。
被回收了怎么办?
在onSaveInstanceState(Bundle outState)方法中保存临时数据,而在onCreate或者onRestoreInstanceState方法中恢复数据。
注意:
onSaveInstanceState并不在生命周期的回调中,因此并非是在所有情况下都会被调用,因此一些数据应该尽量在onPause方法中进行存储。
与下一个Activity传递数据
需要详细了解Intent的用法,此处不赘述。
向下一个Activity传递数据
在Intent中放入数据,然后通过startActivity方法或者startActivityForResult方法启动下一个Activity,下一个Activity被启动后,通过getIntent方法得到启动该Activity的Intent,请取出其中的数据。
返回数据给上一个Activity
上一个Activity
需要通过startActivityForResult方法启动下一个Activity,第一个参数是Intent,第二个参数是请求码,请求码在之后可以用来判断是从哪次请求中返回的数据(不一定只有一种请求)。
此外,需要复写onActivityResult方法,先根据返回的requestCode判断请求类型,然后再判断resultCode是否是正常返回(否则从Intent中取数据可能会导致异常),最后再从Intent中获取数据。下一个Activity
在需要返回数据时,将数据放于Intent中,然后通过setResult方法返回,该方法第一个参数是返回码,RESULT_OK应该表示返回成功,而RESULT_CANCELED会在其他情况下被返回(如按下back键被按下时)。注意:
数据只会通过Intent被返回一次,而且是在Activity结束之后,因此只能通信一次,整个Activity就像被调用的一个函数一样。
创建一个菜单
首先需要在menu目录下创建menu的布局文件,它的代码可以如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/add_item"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:title="Remove" />
</menu>
然后需要在onCreateOptionSMenu方法中创建menu:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity, menu);
return true;
}
返回true表示允许创建的菜单显示出来,若为false则无法显示。
最后为了能够响应菜单事件,需要重写onOptionsItemSelected方法:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_item:
Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();
}
return true;
}
如果希望将菜单点击事件传递下去,继续触发其他处理,则返回false;如果认为已经处理完毕,不需要将事件传递下去则返回true。如果采用的是return super.onOptionsItemSelected(item)
,系统缺省返回false。
注意:
现在很多手机已经取消了menu按键,此时可以通过openOptionsMenu方法打开menu,也可以通过closeOptionsMenu方法关闭menu。
处理返回按键
重写onBackPressed方法:
public void onBackPressed() {
// do anything you want.
//super.onBackPressed();
}
默认的实现简单的finish当前的Activity,重写该方法可以自定义back按键的动作。
处理Fragment
这将专门的在”Fragment的使用“中阐述。
Manifest文件
为了能够被Context.startActivity()方法启动,所有的Activity都需要在Manifest文件中注册。
Device Compatibility
System Permissons
Intent filter
详细的查看Intent用户手册。
* 注意:*
如果Activity支持隐式Intent启动,则需要添加DEFAULT的category,这是因为startActivity和startActivityForResult方法都会默认的添加DEFAULT的category。
启动模式
在<activity>
标签下指定android:launchMode属性来选择启动模式。不指定则默认为standerd模式。
- standard
每次启动一个新的Activity,它就会在back stack中入栈,并处于栈顶的位置。系统不会在乎这个Activity是否已经存在于back stack中,每次启动都会创建一个新的实例。 - singleTop
如果待启动Activity已经在back stack栈顶,则直接使用它而不重新创建。 - singleTask
如果待启动Activity在back stack中(不仅仅是在栈顶),则弹出该Activity之上所有的Activity,否则创建一个新的Activity。 - singleInstance
用来实现其他程序和我们的程序共享这个Activity的实例。因此,在这个模式下会有一个单独的back stack来管理这个Activity(实际上singleTask模式指定了不同的taskAffinity也会启动一个新的back stack),不管是哪个应用程序访问这个活动,都共用同一个back stack。
// TODO