活动(Activity)是最吸引用户的地方,它是四大组件之一。是一种可以包含用户界面的组件,主要用于和用户进行交互。一个程序可以包括多个甚至零个活动,但是没用活动的应用程序很少见。
之前创建的应用程序已经包括了活动。
现在我们应该创建一个不包括任何活动的应用程序。
现在打开Android Studio,创建一个无活动的应用程序。
名字使用ActivityTest。点击Finish完成创建。
创建完成的Project目录树如图所示:
在第一章节的学习,你一定知道了,app下的文件是我们需要进行编辑的。
打开app\src\main\java\目录,点击com.example.activitytest,点击鼠标右键选择New—>Activity—>Empty Activity
活动的名字写FirstActivity,注意:名字下面有两个待选框,现在不要选(第一个待选框是生成一线性布局文件,第二待选框是将该活动设置为主活动,就是说当我们的应用程序一启动就会运行的活动)。
你现在需要知道,项目中的任何活动都需要重写onCreate()方法。
当我们创建好这个活动后,Android Studio 将会自动帮我们完成。
安卓程序讲究的是逻辑与视图分离,最好每一个活动都对应一个布局,布局就是用来显示界面内容的,现在我们就手动创建一个布局文件。
点击app/src/main/res目录–>New–>Directory
会弹出来一个新建目录的窗口。这里我们将名字写为layout
然后对着layout目录右击鼠标–>New–>Layout resource file,又会弹出一个新建不就资源的窗口,此时我们将名字写为first_layout。点击OK完成创建。
此时你将会看到以下布局编辑器。
这是Android Studio为我们提供的可视化布局编辑器,在窗口的最左下方有两个切换卡,Design与Text。
现在点击Text切换卡。
之前我们已经选择了LinearLayout为根元素,因此我们的布局文件中已经存在有一个元素,
现在我们需要添加一个按钮在布局文件中。如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
//Button标签为创建一个按钮
LinearLayout>//线性布局
现在我们已经将布局文件写完了,此时我们需要将布局文件用在活动中,完成布局文件的调用。
打开我们的FirstActivity.java文件,修改方法onCreate
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);//此方法来给当前活动加载一个布局,setContentView(布局文件id),项目中所有资源都会在R文件中产生一个相应资源的id。
}
之前讲解过程序中的所有活动都必须在AndroidMainifest文件中进行注册才可以生效,当我们打开AndroidMainifest文件时会发现我们新建的活动已经注册过了,这是因为Android Studio自动帮我们注册的。
但是此时的活动是没有定义主活动的,我们需要添加代码定义主活动。
在之间的地方添加代码
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.activitytest">
<dist:module dist:instant="true" />
<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">
//设置活动的标题栏内容
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>//是将此活动定义为一个主活动
activity>
application>
manifest>
此时活动的创建基本上已经完成了。
这时的应用程序已经简单完成了。但是并没有定义按钮的响应事件。
我们可以在活动中使用Toast(是安卓系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一定时间后自动消失,并且不会占用屏幕空间)。
主要是在FirstActivity文件中的onCreate()方法中添加了以下代码:
Button button1 = (Button) findViewById(R.id.button_1);//获得一个Button按钮的实例,
button1.setOnClickListener(new View.OnClickListener(){//setOnClickListener()为按钮注册了一个监听器,点击按钮就会执行onClick()方法
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this,"You clicked Button1",Toast.LENGTH_SHORT).show();//Toast.makeText()方法创建了一个Toast对象,然后调用了show()方法
}
});
package com.example.activitytest;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class FirstActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.third_activity);
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 Button1",Toast.LENGTH_SHORT).show();
}
});
}
}
首先在res文件下创建一个menu的文件夹,点击res目录—>New—>Directory,输入文件夹名menu,然后点击新建的文件夹—>New—>Menu resource file,文件夹名输入main。点击OK完成创建。
打开新建的main文件,添加代码:
//用于指定菜单项的名称
<item
android:id="@+id/remove_item"
android:title="Remove"/>
标签是创建具体的菜单项,
<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>
创建好菜单项后我们需要将它引用到活动中。
现在打开FirstActivity文件,重写方法onCreateOptionsMenu()方法。将菜单项创建到活动中。
通过getMenuInflater()方法获得一个Menu对象,调用inflate(R.menu.main,menu)方法,创建菜单。最后返回true,允许菜单显示出来,如果返回false,创建的菜单将不会显示出来。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
接着重写 onOptionsItemSelected()方法。给出了菜单项的响应事件。
在这里我们使用了Toast消息。
@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;
}
package com.example.activitytest;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class FirstActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {//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
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
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) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
}
}
活动创建好之后我们需要对活动进行销毁。
答案很简单,按手机的Back键就可以了。
或者使用代码结束活动,Activity类提供了一个finish()方法可以销毁一个活动。
如果你要销毁一个活动,那么就在这个活动的任意合理位置添加该方法既可。
例如:当你想在主活动中点击按钮的时候结束活动,那么就修改按钮监听器中的代码
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
finish();
}
});
package com.example.activitytest;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class FirstActivity extends AppCompatActivity {
@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;
case R.id.exit_item://自己义的Exit退出
finish();
break;
default:
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
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) {
finish();
}
});
}
}
只有一个活动的应用难免让人感到简单,没错你的追求应该更高一点,不管你要创建多少个活动,创建的方法都是之前介绍的那样,但是在启动应用后只会启动该应用的主活动,现在我们需要解决的问题是怎样实现从主活动中跳转到其他活动。
有两种方法可以使用,显式的intent与隐式的intent。
现在我们需要做的是在我们的ActivityTest项目中再建立一个活动,打开app\src\main\java\目录,点击com.example.activitytest,点击鼠标右键选择New—>Activity—>Empty Activity,Activity名字我们就填写SecondActivity,second_layout并勾选Generate Layout File(创建一个线性布局),注意不要勾选Launcher Activity选项(创建的活动非主活动)。
你一定还记得逻辑与视图的分离。
所以现在需要我们对活动对应的布局进行编辑。
打开app\src\res\layout,你会发现已经创建好了一个名字为second_layout的布局文件,
现在我们改写这个布局,加入一个按钮。
<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_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="button2"
/>
LinearLayout>
现在我们已经将布局文件写完了,此时我们需要将布局文件用在活动中,完成布局文件的调用。
因为我们需要实现的是在按下button1时跳转,所以我们更应该将intent代码放在button1的监听事件中,
打开FirstActivity.java文件,添加代码:
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
显式的intent是简单明了的,它在创建对象时传入了两个参数,FirstActivity.this与SecondActivity.class,表示从FirstActivity活动跳转到SecondActivity这个活动,然后使用方法startActivity(intent)启动这个活动。
package com.example.activitytest;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_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);
}
});
}
}
之前讲解过程序中的所有活动都必须在AndroidMainifest文件中进行注册才可以生效,当我们打开AndroidMainifest文件时会发现我们新建的活动已经注册过了,这是因为Android Studio自动帮我们注册的。
我们不需要更改这个文件,之前更改是因为没有主活动,现在已经有主活动了。
相对于显式的Intent,隐式的则相对于就含蓄的多,它并不会直接明确我们要启动的活动,而是指定一系例更为抽象的action与category,然后交给系统去分析这个Intent。
只有和中的内容同时能够匹配上Intent,才可以响应该Intent。
现在我们就使用隐式的Intent来启动活动。
这个是隐式的写法:
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
现在我们先在AndroidMainifest文件中给需要被响应的活动添加代码:
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
现在我们就使用隐式的Intent:
完整的FirstActivity文件
package com.example.activitytest;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
}
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.crazymen.cn"));
startActivity(intent);
将代码放到按钮的点击事件的响应事件中
package com.example.activitytest;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
@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.ACTION_VIEW);
intent.setData(Uri.parse("http://www.crazymen.cn"));
startActivity(intent);
}
});
}
}
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:18829463191"));
startActivity(intent);
修改FirstActivity文件,更改button1的响应事件。
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);//传递了一个数据(字符串)
startActivity(intent);
putExtra()方法第一个参数是一个键,第二个参数才是真正传递的值;
我们使用了显式的Intent传递数据,并启动了SecondActivity;
现在我们把传递到SecondActivity中的数据取出来,代码如下:
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
将上述代码放置到SecondActivity文件中的onCreate()方法中;在日志中就会看到传递的数据。
startActivityForResult()方法也是与启动活动的,但这个方法期望在销毁活动时能够返回一个结果给上一个活动
startActivityForResult()这个方法需要两个参数,第一个参数是Intent,还有一个是请求码。
现在我们使用startActivityForResult()这个方法启动活动,在FirstActivity中的onClick()中使用startActivityForResult()启动SecondActivity。
Intent inten = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);//第一个参数是Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源
在SecondActivity中给按钮注册点击事件,并给出返回数据逻辑。
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();
}
});
}
最后在FirstActivity中添加代码得到返回的数据。我们需要重写onActivityResult()得到返回的数据。
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:
}
}
可以在AndroidMainifest中通过给标签指定android:launchMode属性来选择启动模式。
standard模式是活动的默认模式,所有活动在没有指定启动模式的时候会自动默认为此模式。
对于使用standard模式的活动,每次启动都会创建活动的的一个新的实例
当活动的模式是singleTop时,启动活动时如果返回栈中的栈顶已经是该活动,则会直接使用它,不会创建新的实例。
该模式的特点是当启动一个活动时,在返回栈中如果存在该活动,就会把在这个活动之上的所有活动出栈,且不会创建新的实例。如果不存在就会创建一个新的实例。
如何使用此模式解决活动实例问题?
指定为singleInstance模式的活动,在启动的时候会启用一个新的返回栈来管理这个活动。
我们还是在ActivityTest项目上创建一个BaseActivity类,打开app\src\main\java\目录,点击com.example.activitytest,点击鼠标右键选择New—>Java class,类名为BaseActivity。
然后让其继承APPCompatActivity,并重写onCreate()方法。
代码如下:
public class BaseActivity extends AppCompatActivity{
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());//也可以使用以下方法进行判断
Toast.makeText(this, getClass().getSimpleName(), Toast.LENGTH_SHORT).show();
}
}
我们在onCreate()方法中,获取了当前实例的类名,并通过Log打印了出来。
接下来需要BaseActivity成为FirstActivity,SecondActivity,ThirdActivity的父类。
只需要建立一个集合,将所有的活动都存进去,统一进行管理就可以了。
新建一个ActivityCollection类作为活动管理器,代码如下:
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
public class ActivityCollection{
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for(Activity activity : activities){
if(!activity.isFinishing()){
activity.finish();
}
}
activities.clear();
}
}
之前创建的BaseActivity类现在就很重要了,我们在这里面调用方法addActivity(),将所有未被销毁的活动添加到集合中去。
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class BaseTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(this, getClass().getSimpleName(), Toast.LENGTH_SHORT).show();
ActivityCollection.addActivity(this);
}
protected void onDestory(){
super.onDestroy();
ActivityCollection.removeActivity(this);
}
}
此类的使用细节问题:
使用该类前必须将未被销毁的活动放入集合之中去,然后调用finishAll()方法清除活动。
如果有两个活动,第二个活动需要第一个活动传递两个参数信息,
按照之前的方法,我们会在第一个活动中写下传递信息的代码,然后在第二个活动中写出信息接收的代码。
FirstActivity类的代码:
public class FirstActivity extends BaseActivity{
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
startActivity(intent);
}
思路上是没有一点问题的。但是在代码上是不是过于复杂。
而且存在一个现实问题,第二个活动需要什么类型的参数,数量是几个。这不得不让你犯难了。
现在我们换一种思路进行思考:
在需要参数的活动中我们定义好需要参数的信息,在为其传参的活动中编写代码我是就会容易一大截。
省下很多事情。
SecondActivity类的局部代码:
public class SecondActivity extends BaseActivity{
public static void actionStart(Context context,String data1,String data2){
Intent intent = new Intent(context,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
content.startActivity(intent);
}
}
现在我们在FirstActivity中就只需要我们在启动活动二的时候添加数据就可以了。
代码如下:
emoveActivity(this);
}
}
此类的使用细节问题:
使用该类前必须将未被销毁的活动放入集合之中去,然后调用finishAll()方法清除活动。
# 活动启动的最佳写法:
如果有两个活动,第二个活动需要第一个活动传递两个参数信息,
按照之前的方法,我们会在第一个活动中写下传递信息的代码,然后在第二个活动中写出信息接收的代码。
### 代码如下:
FirstActivity类的代码:
```java
public class FirstActivity extends BaseActivity{
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
startActivity(intent);
}
思路上是没有一点问题的。但是在代码上是不是过于复杂。
而且存在一个现实问题,第二个活动需要什么类型的参数,数量是几个。这不得不让你犯难了。
现在我们换一种思路进行思考:
在需要参数的活动中我们定义好需要参数的信息,在为其传参的活动中编写代码我是就会容易一大截。
省下很多事情。
SecondActivity类的局部代码:
public class SecondActivity extends BaseActivity{
public static void actionStart(Context context,String data1,String data2){
Intent intent = new Intent(context,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
content.startActivity(intent);
}
}
现在我们在FirstActivity中就只需要我们在启动活动二的时候添加数据就可以了。
代码如下: