2 探究活动

接下来,让我们先从看得见的入手,具体学习一下四大组件之一 —— 活动吧!

1 活动的基本用法

现在让我们手动创建一个活动,这样对活动的认识会更加深刻。

  • 创建一个活动
  • 建一个空工程(Add No Activity),取名 ActivityTest
  • 项目结构手动改为 Project 模式
  • 手动创建活动:app/src/main/java/com.example.activitytest → New → Activity → Empty Activity
    在这里插入图片描述
  • 创建一个布局

app/src/main/res 创建一个文件夹 layout,在里面创建布局文件:first_layout.xml,并将布局修改为线性布局,并添加一个按钮

<?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>创建一个按钮
        ② 若需要在 XML 文件中定义一个 id,则需要使用“@+id/name”语法 
        ③ layout_width / layout_height:元素宽/高
        ④ match_parent 同父元素一样 
        ⑤ wrap_content 刚好包含里里面内容即可
        ⑥ fill_parent 等同于④,官方推荐使用 match_parent
    -->
    <Button
        android:id="@+id/button_1"  
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1"/>

</LinearLayout>
  • 在 AndroidManifest 文件中注册

可以看到Android Studio已经帮我们注册了:
在这里插入图片描述
现在我们来声明一个 FirstActivity为主活动。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.activitytest">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ActivityTest"
        tools:targetApi="31">

        <!-- 
        	 android:exported:是否支持其它应用调用当前组件。
             如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。
        -->
        <activity
            android:name=".FirstActivity"
            android:exported="true"
            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>
  • 在 FirstActivity.java 引入布局
package com.example.activitytest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class FirstActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
    }
}
  • 这样我们就成功创建好了一个活动,点击运行你就可以看到一个有一个按钮的界面
    在这里插入图片描述

注意:如果你的应用程序中没有声明任何一个活动作为主活动,虽然这个程序是可以安装的,但你却无法在启动器中看到或者打开这个程序。

2 认识相关控件

2.1 Toast

Toast 是 Android 系统提供的一种非常好的提醒方式,可以提醒一些短小的信息通知给用户,会在一段时间后自动消失不会占用任何屏幕空间

/*
	makeTest(Context, 显示的内容, 显示时长)需要3个参数。
	第三个参数有两个内置常量:Toast.LENGTH_SHORT / Toast.LENGTH_LONG
 */
Toast.makeText(FirstActivity.this, "You Clicked Button 1", Toast.LENGTH_SHORT).show();

在这里插入图片描述

2.2 Menu

Android 提供了一种让菜单展示的同时,还能不占用任何屏幕空间的方式。

/res/ 创建 menu 文件夹,新建一个文件:main.xml
在这里插入图片描述

  • <item> 标签:用来创建具体的菜单项
  • 在活动中重写 onCreateOptionsMenu() 方法,并重写 onOptionsItemSelected() 方法来定义菜单相应事件。
<?xml version="1.0" encoding="utf-8"?>
<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>
@Override
public boolean onCreateOptionsMenu(Menu menu) {
	// 参数①:指定通过哪个资源文件创建菜单
	// 参数②:指定将菜单项添加到哪个Menu对象中
    getMenuInflater().inflate(R.menu.main,menu);	// 给当前活动创建菜单
    return true;    // 表示允许创建的菜单显示出来。
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch(item.getItemId()){	// item.getItemId() 获取点击的菜单项
        case R.id.add_item:
            Toast.makeText(this,"Add",Toast.LENGTH_SHORT).show();
            break;
        case R.id.remove_item:
            Toast.makeText(this,"remove",Toast.LENGTH_SHORT).show();
            break;
        default:
    }
    return true;
}

在这里插入图片描述

3 销毁一个活动

主要有两个方法:

  • 按 Back 键
  • 通过代码销毁:finish()

4 使用 Intent 在活动之间穿梭

Intent 是 Android 程序中各组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据Intent 一般可以被用于启动活动、启动服务以及广播等场景

Intent可分为:显示Intent 和 隐式Intent。

4.1 显示 Intent

// 参数1:启动活动的上下文
// 参数2:要启动的活动
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
// 上述代码意图:在活动 FirstActivity 中启动 SecondActivity

4.2 隐式 Intent

隐式 Intent 并不指出我们想要启动哪一个活动,而是指定了一系列更为抽象的 actioncategory 等信息,然后交由系统去分析这个 Intent,并帮我们找到合适的活动去启动。

  • 修改 AndroidManifest.xml :在要启动的活动中添加如下代码
<!-- 
   <action>标签中指明当前活动可以相应 com.example.activitytest.ACTION_START 这个 action
   <category>标签则包含一些附加信息,更加精确的指明了当前活动能相应的 Intent 还可能带有的 category
-->
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>

只有<action><category>中的内容同时能够匹配上 Intent 中指定的 action 和 category 时,这个活动才能相应 Intent。

4.2.1 默认 category

Intent intent = new Intent("com.example.activitytest.ACTION_START");
// 这里没有指明 category,系统默认为 android.intent.category.DEFAULT
startActivity(intent);

4.2.2 显示指定 category

每个 Intent 中只能指定一个 action,但却能够指定多个 category

使用 addCategory() 来添加一个 category。
【注意:此时将无默认的 category !!!】

Intent intent = new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("android.intent.category.MY_CREATION");
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.MY_CREATION"/>

<!-- 注意:如果没有
	<category android:name="android.intent.category.DEFAULT"/>
	将会报错!
-->

4.3 更多隐式 Intent 的用法

使用隐式 Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得 Android 多个应用程序之间的功能共享成为可能。

例1:在活动中调用系统浏览器启动百度网页:
Intent.ACTION_VIEW 系统内置的动作,其常量值为 android.intent.action.view

Intent intent = new Intent(Intent.ACTION_VIEW);
// Uri.parse()将网址字符串解析成Uri对象,再通过setData传递进去。
// setData() 主要用于指定当前 Intent 正在操作的数据。
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

例2:在程序中调用系统拨号界面
Intent.ACTION_DIAL

Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);


同时,我们还可以在<intent-filter>标签中再配置一个<data>标签,更加精确地指定当前活动能够相应什么类型的数据。一般在<data>标签不会指定过多内容。

<data>标签主要可以配置以下内容:

  • android:scheme:指定数据的协议部分,如 http、geo(显示地理位置)、tel(拨打电话)
  • android:host:指定数据的主机名部分,如 www.baidu.com
  • android:port:指定数据的端口号
  • android:path:指定主机名和端口之后的部分
  • android:mimeType:指定可以处理的数据类型,允许使用通配符的方式进行指定。
<data android:scheme="http"/>

只有<data>标签中指定的内容与Intent中携带的Data完全一致时,当前活动才能响应 Intent。

4.4 向下一个活动传递数据

上面我们学习了如何通过Intent来启动活动,现在我们来学习一下:如何通过Intent在启动活动时传递数据。

思路:将要传递的数据暂存在 Intent,到另一个活动中再从 Intent 中取出来。

  • putExtra():intent.putExtra(“extra_data”, data);
    • 接收两个参数:(键,传递的数据)
  • getStringExtra():intent.getStringExtra(“extra_data”);
    • 接收一个参数:(要取得数据对应的键)
    • getIntExtra()getBooleanExtra()…… 根据要取的数据的数据类型选择

4.5 返回数据给上一个活动

既然可以传递数据给下一个活动,同样也可以返回数据给上一个活动!
之前我们都是通过 startActivity() 启动活动的,其实 Activity 还有一个启动活动的方法:

  • startActivityForResult():在活动销毁时能够返回一个结果给上一个活动
    • 接收两个参数:(Intent,请求码)
    • 其中请求码用于之后回调中判断数据的来源,是个唯一值即可。
  • putExtra():两个参数(键,要返回的数据)
  • setResult():向上一个活动返回数据
    • 接收两个参数:(处理结果,intent)
    • 处理结果有两个内置常量:RESULT_OK、RESULT_CANCELED
  • getStringExtra():一个参数(键)

SecondActivity 销毁活动后会调用前一个活动的 onActivityResult()方法,所以要在 FirstActivity 重写 onActivityResult 方法,以获得返回的数据。

// FirstActivity
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent,1);
// SecondActivity
Intent intent = new Intent();
intent.putExtra("data_return","Hello");
setResult(RESULT_OK,intent);
finish()
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    switch (requestCode) {
        case 1:
            if (resultCode == RESULT_OK) {
                String returnedData = data.getStringExtra("data_return");
                System.out.println(returnedData);
                Toast.makeText(FirstActivity.this,returnedData,Toast.LENGTH_SHORT).show();
            }
            break;
        default:
    }
}

此时也许你会问:如果我是按 back 键返回的呢?
答:这也有解决方法,在 SecondActivity 重写 onBackPressed()

@Override
public void onBackPressed() {
    Intent intent = new Intent();
    intent.putExtra("return_data","Hello");
    setResult(RESULT_OK,intent);
    finish();
}

注意:目前 startActivityForResult() 已被弃用,现用 registerForActivityResult() 取代。

5 活动生命周期

掌握活动的生命周期对于任何 Android 开发者是非常重要的。

Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Task)。

5.1 活动状态

每个活动在其生命周期最多可能有4中状态。

  • 运行状态:位于栈顶的活动。系统最不愿意回收。
  • 暂停状态:活动不位于栈顶但可见。系统不愿回收,除非处于内存极低状态。
  • 停止状态:活动不位于栈顶也不可见。可能会被系统回收。
  • 销毁状态:活动从栈中移除。系统倾向回收。

5.2 活动的生存期

Activity 类中定义了七个方法,覆盖了活动生命周期的每一个环节。

  • onCreate:活动第一次被创建时调用。完成活动的初始化(如加载布局,绑定事件)。
  • onStart:活动由 不可见 → 可见。
  • onResume:活动准备好和用户进行交互时候调用。此时活动处于栈顶,且处于运行状态。
  • onPause:系统准备取启动或恢复另一个活动时调用。
  • onStop:活动完全不可见时调用。
  • onDestroy:活动被销毁之前调用。
  • onRestart:活动由停止状态变为运行状态时调用。
    在这里插入图片描述

5.3 活动被回收了怎么办?

一个活动进入停止状态时被系统回收,那么如果该活动存储着用户的临时数据,那么这些数据将会丢失,给用户造成不好的体验。那么如何避免这些问题呢?

Activity 提供了一个 onSaveInstanceState() 回调方法。这个方法保证在活动被回收之前一定会调用,因此我们可以通过这个方法来解决上述问题。

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    String tempData = "Hello";
    outState.putString("data",tempData);
}

onSaveInstanceState() 需要一个 Bundle 参数,
Bundle 提供一系列方法用于保存数据,如:putString、putInt,均需要参数(键,值)。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.first_layout);
    
	if(savedInstanceState != null){
	    String tempData = savedInstanceState.getString("data");
	}
}

onSaveInstanceState() 在 Activity 被暂时停止时(被其他程序中断或锁屏)调用,而 Activity 在完全关闭时(调用 finish() 函数)时不会被调用的。当暂停的 Activity 被恢复时,系统会调用 onRestoreInstance() 函数。

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if(savedInstanceState != null){
	    String tempData = savedInstanceState.getString("data");
	}
}

Intent 还可以结合 Bundle 一起用于传递数据。可以把数据存于Bundle,再将Bundle存于Intent,之后再从Intent取出Bundle,再从Bundle取出数据。

6 活动的启动模式

启动模式一共有4种,在实际项目中我们应该根据特定的需求为每个活动指定恰当的模式。

通过在 AndroidManifest.xml 中给 &lt;activity> 标签指定 android:launchMode 属性来选择启动模式。

  • standard:默认的启动方式。这种方式下系统不会在乎这个活动是否已经在栈中存在,每次启动都会创建该活动的一个新的实例。
  • singleTop:在启动活动时如果发现返回栈的栈顶是该活动,则直接使用它,不会再创建新的活动实例。若不处于栈顶,则创建新的活动实例。
  • singleTask:如果返回栈中存在这个活动,则会将在这个活动之上的所有活动通通出栈,并将该活动放于栈顶直接使用。只要栈中没有该活动时才会创建新实例。
  • singleInstance:这个模式比较复杂。此模式下活动会启动一个新的返回栈来管理这个活动,不管是哪个应用程序来返回这个活动,都共用一个返回栈。
    在这里插入图片描述

7 知晓当前是处于哪一个活动

  • 创建一个 BaseActivity 类(不是活动,是Java Class 类),继承 AppCompatActivity,重写 onCreate
package com.example.myapplication;

import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
    }
}

接下来,只需要让 BaseActivity 成为所有活动的父类即可。

8 随时随地退出程序

当活动创建很时,退出需要重复按多次back键,很不方便,那如何解决这个问题呢?

只需要创建一个专门对所有活动进行管理的集合类即可:ActivityCollector

package com.example.myapplication;

import android.app.Activity;

import java.util.ArrayList;
import java.util.List;

public class ActivityCollector {
    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 代码:

package com.example.myapplication;

import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

当要进行彻底退出程序时,需要调用:

ActivityCollector.finishAll();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值