Android四大组件之Activity
Android 开发的四大组件分别是:活动(Activity),用于表现功能;服务(Service),后台运行服务,不提供界面呈现;广播接受者(Broadcast Receive),用于接收广播;内容提供者(Content Provider),支持多个应用中存储和读取数据,相当于数据库。
定义:Activity是用户操作的可视化界面,它为用户提供了一个完成操作指令的窗口。在Android App 中只要能看见的几乎都要依托于Activity,所以Activity是在开发中使用最频繁的一种组件。
手动创建活动:选择Add No Activity→右击app/src/main/java/com.example.activitytest包→New→Activity→Empty Activity,我们将活动命名为FirstActivity,注意不要勾选Generate Layout File和Launcher Activity这两个选项。
勾选Generate Layout File表示会自动为FirstActivity创建一个对应的布局文件;勾选Launcher Activity表示会自动将FirstActivity设置为当前项目的主活动;勾选Backwards Compatibility表示会为项目启用向下兼容的模式,这个选项要勾上。
项目中的任何活动都应该重写Activity的onCreate()方法,Android Studio已经帮我们写好了。
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
可以看到,onCreate()方法非常简单,就是调用了父类的onCreate()方法。
创建和加载布局:右击app/src/main/res目录→New→Directory,创建一个名为layout的目录,然后对着layout目录右键→New→Layout resource file,将布局命名为first_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/button_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 1"/>
</LinearLayout>
在活动中加载布局
回到FirstActivity,在onCreate()方法中加入如下代码:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
}
}
这里调用了setContentView()方法给当前的活动加载一个布局。在setContentView()方法中,我们会传入一个布局文件的id,只需要调用R.layout.first_layout就可。
在AndroidManifest文件中注册:
所有活动都要在AndroidManifest文件中注册才能生效,实际上FirstActivity已经在AndroidManifest.xml注册过了,打开app/src/main/AndroidManifest.xml文件可看到:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.firstactivity">
<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>
可以看到,活动的注册声明要放在<application>标签内,通过<activity>标签来对活动进行注册。由于最外层的<manifest>标签中已经通过package属性指定了程序的包名是com.example.firstactivity,所以我们直接将.FirstActivity填入name即可。
不过,这样只是注册了活动,还没有给程序配置主活动,也就是说,当程序运行起来的时候,不知道要首先开启哪个活动。
配置主活动只需要在<activity>标签内加入内部标签<intent-filter>,在<intent-filter>中加入如下两句即配置完成。我们还可以使用android:label指定活动中标题栏的内容,标题栏是显示在活动最顶部的,给主活动指定的label不仅会成为标题栏的内容,还会成为启动器(Launcher)中应用程序显示的名称。
<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>
在活动中使用Toast:
Toast是Android系统提供的一种非常友好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();
}
});
}
在活动中,可以通过findViewById()方法获取到在布局文件中定义的元素,findViewById()方法返回的是一个View对象,我们将它转成Button对象。通过调用setOnClickListener()方法为按钮注册一个监听器,点击按钮时就会执行监听器中的onClick()方法,所以弹出Toast的功能在onClick()方法中编写。
Toast的用法非常简单,使用方法makeText()方法创建一个Toast对象,然后调用show()将Toast显示出来即可。makeText()方法需要传入3个参数:①Context,也就是Toast在哪里弹出,由于活动本身就是一个Context对象,所以直接传入FirstActivity.this即可。②Toast显示的文本内容③Toast显示的时长。
在活动中使用Menu:(android.app.Activity)
在res目录下新建一个menu文件夹,右击res目录→New→Directory,输入文件名menu。接着在这个文件夹下再新建一个名为main的菜单文件,右击menu文件夹→New→Menu resource file。
打开main.xml:
<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="Romove"/>
</menu>
<item>标签就是用来创建具体的某一个菜单项。接着回到FirstActivity中重写onCreateOptionsMenu()方法。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
通过getMenuInflater()方法能够得到MenuInflater对象,再调用它的inflate()方法就可以给当前活动创建菜单了。inflate()方法接收两个参数:①用于指定我们通过哪一个资源文件来创建菜单②用于指定我们的菜单项将添加到哪一个Menu对象当中,可以直接使用onCreateOptionsMenu()方法中传入的menu参数,然后给这个方法返回true,表示允许创建的菜单显示出来。
接着,为了让菜单可以用,我们定义菜单的响应事件。在FirstActivity中重写onOptionsItemSelected()方法。
@Override
public boolean onOptionsItemSelected(@NonNull 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();
break;
default:
}
return true;
}
通过调用item.getItemId()来判断我们点击的是哪一个菜单项,然后给每个菜单项加入自己的逻辑处理。
package com.example.activitytest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class FirstActivity extends AppCompatActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull 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();
break;
default:
}
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();
}
});
}
}
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 2"/>
</LinearLayout>
使用显式Intent:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
首先,我们构建了一个Intent,传入FirstActivity.this作为上下文,传入SecondActivity.class作为目标活动,接着通过startActivity()方法来执行Intent。
使用隐式Intent:
隐式Intent并不明确指出我们想要启动哪一个活动,而是指定一系列抽象的action和category等信息,然后交由系统去分析。
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
通过在<activity>标签下配置<intent-filter>,指定当前活动能够响应的action和category。
修改FirstActivity中按钮的点击事件:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()方法时会自动将其添加到Intent中。
每个Intent中只能指定一个action,但可以指定多个category。
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.activitytest.MY_CATEGORY"/>
</intent-filter>
</activity>
public void onClick(View v) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
可以调用Intent中的addCategory()方法来添加一个category。
更多隐式Intent的用法:
使用隐式Intent,不仅可以启动自己程序内的活动,还可以启动其他程序的活动。
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
首先指定了Intent的action是Intent.ACTION_VIEW,这是一个Android系统内置的动作,其常量值为android.intent.action.VIEW。然后通过Uri.parse()方法将一个网址字符串解析成一个Uri对象,再调用Intent的setData()方法将Uri对象传进去。
新建ThirdActivity
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ThirdActivity">
<Button
android:id="@+id/button_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 3"/>
</LinearLayout>
<activity android:name=".ThirdActivity">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"/>
</intent-filter>
</activity>
或
<activity android:name=".ThirdActivity">
<intent-filter >
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
</intent-filter>
</activity>
下面代码展示如何在我们的程序中调用系统拨号界面:
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
首先指定了Intent的action是Intent.ACTION_DIAL,这也是一个Android系统的内置动作,在data部分指定协议是tel,号码是10086。
向下一个活动传递数据:
在FirstActivity中:
public void onClick(View v) {
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);//第一个参数是键,用于后面从Intent取值
startActivity(intent);
}
在SecondActivity中(接收参数):
public class SecondActivity extends android.app.Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
}
}
通过getIntent()方法获取到用于启动SecondActivity的Intent,调用getStringExtra()方法,传入相应的键值,即可得到传递的数据。
传递的是整型数据,使用getIntExtra()方法;传递的是布尔型数据,使用getBooleanExtra()方法。
返回数据给上一个活动:
Activity中还有一个startActivityForResult()方法也是用于启动活动的,这个方法期望在活动销毁的时候能够返回一个结果给上一个活动。
在FirstActivity中:
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);//第一个参数是Intent,第二个参数是请求码
}
在SecondActivity中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return","Hello FirstActivity");
setResult(RESULT_OK,intent);
finish();
}
});
}
由于我们是使用startActivityForResult()方法来启动SecondActivity的,在SecondActivity被销毁后会回调上一个活动的onActivityResult()方法,所以我们要在FirstActivity中重写这个方法。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case 1:
if (resultCode == RESULT_OK){
String returnedData = data.getStringExtra("data_return");
Log.d("FirstActivity",returnedData);
}
break;
default:
}
}
按下Back键回到FirstActivity返回数据:通过重写onBackPressed()中的代码即可。
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("data_return","Hello FirstActivity");
setResult(RESULT_OK,intent);
finish();
}