相关引用:
《第一行代码 第2版》
https://developer.android.com/guide/topics/ui/notifiers/toasts.html
从上节我们可以知道,Android包含四大组件:
- Activity
- BroadCastReceiver
- Service
- ContentProvider
作为一个嵌入式设备的程序,对于用户来说,最直观的就是界面显示了,所以本篇课程学学Activity的知识
什么是活动(Activity)
JavaSE中,程序的入口是main()函数;而在Android中,若这个程序是被用户可见的,则需用用到Android的组件————Activity,它是一个可以包含用户界面的组件,被用户交互操作。
一个Android程序包含零到多个Activity。
基本用法
怎么创建
若你创建一个Android项目的时候,不更改默认配置,IDE会自动帮你生成带Activity组件的项目(至于如何创建,可自行google)。下面我们准备手动创建一个Activity,选择Add No Activity:
我学Android的时候,流行的IDE还是Eclipse,所以我习惯了它的窗口界面,在开发中,我一般都是将Android Studio改成Project模式:
鼠标右击包名 → New → Activity → Empty Activity,会弹出下图的对话框,不要勾选Generate Layout File和Launcher Activity:
打开FirstActivity.java,由于继承了Actvity(AppCompatActivity是Activity的子类),所以Android Studio自动帮我们重写了onCreate()
方法:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
在Android中,只要看到onXxx()的方法,其实就是理解为 在xxx时候执行,例如onCreate()几时在创建的时候执行,onClick()在组件被点击的时候执行,onDestory()在销毁的时候执行……
加载内容布局
前面说到,创建Activity时不够选Generate Layout File是为了我们自己手动创建布局文件,那什么是布局文件呢???其实布局文件就是用来显示界面内容的。
如何创建
从上节我们知道,Android的布局文件是放在layout目录的,创建目录:
接下来就可以在这个目录下创建我们的布局文件了,并命名为firstactivity_layout:
创建完成后,Android Studio会自动显示可视化布局编辑器窗口:
图片左下角有两个切换卡,左边是Design,右边是Text。前者是可视化布局编辑器,在这里你不仅可以预览当前的布局,还可以通过拖放的方式编辑布局;而Text则是通过XML文件的方式来编辑布局的,一般情况下是用XML方式编辑布局,所以我们切换到Text:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
在这里我们可以添加一个最简单的组件:Button(按钮):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="按钮"/>
</LinearLayout>
xml中各元素属性的含义
这里对Button的几个属性进行讲解:
- android:id是Button这个组件的唯一标识,类似于现实生活中人们的身份证,如果要在java代码中操作某个组件,则组件需要设置id,这样才能在代码中通过id拿到这个组件。注意:定义一个id格式是@+id/xxxxx,不能写成@id/xxxxxx,后者表示引用一个资源
- android:layout_width和android:layout_height,顾名思义,表示组件的宽度和高度,match_parent表示和父元素一样宽,而Button的父元素是LinearLayout,LinearLayout的宽度也为match_parent,那是多少呢??这里你可以简单先理解成和屏幕一样宽,本系列只讲基础知识;wrap_content则表示高度刚好包含Button里面的内容
- android:text指定了Button显示的内容。
我们可以点击右侧工具栏的Preview预览一下效果:
显示布局
布局文件的创建工作已经完成了,现在让我们在Activity中加载它,显示出来吧:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
}
}
上述代码中,是使用setContentView()给当前Activity加载布局的,使用方法是往这个方法传入一个int型的布局文件id。上节中提过,项目中的资源文件都会在R文件中生成一个唯一标识的资源id。因此我们把创建的布局文件id传入即可。
在清单文件中注册(AS自动完成)
上一章说过,Android的四大组件必须在清单文件中注册才能生效,activity自然不例外,只是Android Studio贴心的帮我们完成了这个动作:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.snaptech.activitytest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".FirstActivity"></activity>
</application>
</manifest>
清单文件元素属性的含义
- 标签中android:name是来指定具体注册哪个活动的,这里填入的.FirstAcitivity是什么意思呢?其实这不过就是com.snaptech.activitytest.FirstActivity的缩写而已。由于在最外层的标签中已经通过package属性指定了程序的包名是com.snaptech.activitytest,因此在注册活动时这一部分就可以省略了,直接使用.FirstActivity就足够了。
设置活动为主入口
上一章中说过,Activity要想作为主入口,只需要如下声明即可:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.snaptech.activitytest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".FirstActivity"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
android:label是用来指定活动中标题栏内容的,标题栏是显示在活动最顶部的。label不仅会成为标题栏中的内容,还会成为Launcher中应用程序显示的名称。运行本程序,结果如下:
使用onClick()+Toast打印
对于什么是Toast,Android官网的定义是:
A toast provides simple feedback about an operation in a small popup. It only fills the amount of space required for the message and the current activity remains visible and interactive. Toasts automatically disappear after a timeout.
简单翻译就是,Toast框是一个小的弹出窗口,用于提供有关操作的简单反馈。它只填充消息所需的空间大小,并且当前的Activity仍然是保持可见并且可以和用户交互的(此处涉及到Activity的生命周期,下文会讲解)。Toast框会在设定的时间超时后,自动消失。
另外我们要知道,按钮是可点击的,我们可以通过给按钮Button设置点击监听事件来实现:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
//findViewById()通过在R文件的唯一标识id获取组件,返回的是View对象,而View对象是所有组件的父类,所以要向下转型为Button对象。
Button button = (Button) findViewById(R.id.btn_button);
//通过该方法为Button注册一个点击的监听器
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {//还记得上文所说的诀窍吗??onXxx()就是在Xxx的时候调用,此处Button为点击的时候调用
//通过Toast的静态方法makeText()创建Toast对象,传入三个参数,第一个是上下文对象Context,直接传入Activity本身,第二个是要现实的内容,第三个是显示的时长,有两个常量选择Toast.LENGTH_SHORT和Toast.LENGTH_LONG。记得最后调用.show()方法,不然不会显示!!!
Toast.makeText(FirstActivity.this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
}
});
}
}
点击Button,结果:
点击事件的四种写法
下面介绍组件点击事件的四种方法:
第一种
自定义一个类,并实现onClickListener接口:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
button.setOnClickListener(new ClickListener());
}
class ClickListener implements View.OnClickListener{
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
}
}
}
第二种
定义一个匿名内部类实现onClickListener接口
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
}
});
}
}
第三种
让当前activity实现onClickListener接口
public class FirstActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
}
}
第四种
在xml文件中给Button节点设置onClick属性,然后在activity中定义跟该属性值同名的方法:
public class FirstActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
}
public void onButtonClick(View v){
Toast.makeText(this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
}
}
在Android官网中,对于此种方法是这样介绍的:
For instance, if you specify android:onClick=”sayHello”, you must declare a public void sayHello(View v) method of your context (typically, your Activity).
如果你在xml中设置了这个属性,那么你必须在你的上下文中声明一个public类型的方法,并且形参类型为View。
销毁一个活动
上面学习了如何创建一个活动,那么如果想要销毁一个活动呢?其实只要按一下Back键就可以了。如果你想在代码中实现,可以调用Activity类的finish()方法:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(FirstActivity.this,"button is clicked !!! ",Toast.LENGTH_SHORT).show();
FirstActivity.this.finish();
}
});
}
}
运行效果和按下Back键是一样的
Intent
如果想要在两个Activity之间跳转的话,就要用到Intent,通过设置Intent对象的参数指定要跳转的Activity。
概念
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。
显式Intent
通过设置Acitivity的包名和类名实现的跳转,称为显示意图
隐式Intent
通过指定一系列的action和category,交由系统去分析这个Intent然后做出响应,称为隐式意图
实践
我们可以利用Intent从FirstActivity跳转到SecondActivity。
创建第二个Activity
利用前面介绍的创建Activity的方法创建SecondActivity:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secondactivity_layout);
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button2"/>
</LinearLayout>
还是在SecondActivity的界面上简单的定义了一个Button,内容为Button2。
别忘了在清单文件中检查是否注册了这个Activity:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".FirstActivity"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"></activity>
</application>
由于不是入口Activity,所以不需要配置FirstActivity里的内容。
显式意图跳转
要从FirstActivity显示跳转到SecondActivity:
public class FirstActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
}
public void onButtonClick(View view){
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);
}
}
点击FirstActivity中的“按钮”,程序会经过动画跳转到SecondActvity,并显示后者的布局内容:
隐式意图跳转
还记得我们在清单文件中注册FirstActivity时候,所设置的属性吗?其实这些属性就是被用于其他Activity隐式启动的。
打开清单文件,为SecondActivity设置action和category:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
action的android:name其实可以随便写,但是最好写规范。要想让一个Activity被隐式启动,必须设置action与category!
隐式意图启动Activity成功的条件:Intent参数的值必须与该Activity在清单文件中属性的定义的值同时匹配:
public class FirstActivity extends AppCompatActivity{
private final String INTENT_ACTION = "com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
}
public void onButtonClick(View view){
Intent intent = new Intent(INTENT_ACTION);
//实际上有调用以下代码,只不过是自动帮你添加了
//intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
}
}
等等,前面不是说必须要和同时匹配才能响应吗?怎么没看到指定category?其实这是因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()方法时,会自动将这个category添加到Intent中。运行程序,效果和显示意图启动一致。
我们再修改清单文件中SecondActivity的属性:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY"/>
<action android:name="com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY_TEMP"/>
<category android:name="com.snaptech.activitytest.CATEGORY_SECOND_ACTIVITY"/>
</intent-filter>
</activity>
这里设置了两个,在FirstActivity中将Intent的action指定为其中任何一个:
public class FirstActivity extends AppCompatActivity{
private final String INTENT_ACTION = "com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY";
private final String INTENT_ACTION_TEMP = "com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY_TEMP";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
}
public void onButtonClick(View view){
Intent intent = new Intent(INTENT_ACTION_TEMP);
startActivity(intent);
}
}
重新运行程序,报错:
Tips:程序出现错误,将Tag过滤器切换到Error,出现错误的地方源头一般在log信息最下面
错误提示没有一个Intent可以响应我们的操作,这是因为在Android中,每个Intent只能指定一个action,可以指定多个category
隐式意图的使用场景
一般用于启动不同应用中的Activity,常用的,我们可以启动系统自带的应用。比如你的程序中需要展示一个网页,这时候你不必自己实现这个功能,可以调用系统自带的浏览器来实现:
public class FirstActivity extends AppCompatActivity{
private final String INTENT_ACTION = "com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY";
private final String INTENT_ACTION_TEMP = "com.snaptech.activitytest.START_ACTION_SECOND_ACTIVITY_TEMP";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstactivity_layout);
Button button = (Button) findViewById(R.id.btn_button);
}
public void onButtonClick(View view){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://blog.youkuaiyun.com/wudongjiang333"));
startActivity(intent);
}
}
结果:
上述代码中,可能你会对setData()陌生,它接收一个Uri对象,主要用于指定当前Intetn正在操作的数据,而这些数据通常都是以字符串的形式传入到Uri.parse()方法中解析产生的。与此对应,我们可以在中在配置一个标签,用于更精确地指定当前活动能够响应什么类型的数据。可以配置一下内容:
- android:scheme:用于指定数据的协议部分,如上例中的http部分。
- android:hose:用于指定数据的主机名部分,如上例中的blog.youkuaiyun.com
- android:port:用于指定数据的端口部分,一般紧随在主机名之后。
- android:path:用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
- android:mimeType:用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
如果系统中存在多个Activity的intent-filter同时与你的intent匹配,那么系统会显示一个对话框,列出所有匹配的Activity,由用户选择启动哪一个:
本节暂时介绍到这里,下一节主要介绍Activity之间的通信、Activity的生命周期、Activity的启动模式等内容