第一行代码第二章——探究活动

2.1活动概念

是一种包含用户界面的组件,主要用于和用户进行交互。

2.2活动基本用法

在这里插入图片描述
MainActivity 就是一个活动

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

2.2.1创建活动两种方式:

1.右击com.example.activitytest –> New –> Activity –> Empty Activity ,创建Main2Activity。
创建完成后会在
    res layout 自动生成activity_main2.xml文件
    AndroidManifest.xml中自动生成 activity 的标签

<activity android:name=".Main2Activity"></activity>

2.右击com.example.activitytest –> New –>JacaClass-> 填入:
Name SuperClass
这种方式创建在res 和AndroidManifest.xml中不会自动生成
在这里插入图片描述

2.2.2创建和加载布局

1布局文件

<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        tools:layout_editor_absoluteX="146dp"
        tools:layout_editor_absoluteY="101dp"
        tools:ignore="MissingConstraints" />
</LinearLayout>

向布局文件中添加Button
● android:id="@+id/button 给当前元素定义唯一标识
● android:layout_width=“wrap_content” 宽度
● android:layout_height=“wrap_content” 高度
● wrap_content :当前元素的高度只要能刚好包含里面的内容就行
● match_parent:和当前幅元素一样宽

2.加载布局:
项目中添加的资源文件都会在R文件中生成一个相应的资源id。通过setContentView()方法来加载当前活动的布局,而在setContentView()方法中我们传入的是布局文件的id。

setContentView(R.layout.Main2Activity);

2.2.3 AndroidManifest.xml文件分析

任何一个活动都需要在AndroidManifest中进行注册

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">
    <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=".Main2Activity"></activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

● package指定了程序的包名;
● name指定注册的是哪个活动;
● label指定活动中标题栏的内容,还会成为启动器(Launcher)中应用程序显示的名称;
● intent-filter标签指定当前活动为程序的主活动。

2.2.4活动中使用Toast

Toast是Android系统提供的一种信息提醒方式。信息在一段时间后会自动消失,并且不会占用任何屏幕空间。
通过点击Button 弹出Toast
通过findViewById()方法获取到在布局文件中定义的元素,传入R.id.button_1获取到Button实例。给button1设置一个监听器,然后在onClick()方法中执行监听事件。Toast输入的快捷键是Toast+Tab键。makeText()方法中的三个参数:
第一个参数:上下文Context。
第二个参数:Toast显示的文本内容。
第三个参数:Toast显示的时长,有两个内置常量,分别是Toast.LENGTH_SHORT和Toast.LENGTH_LONG。

扫描二维码关注公众号,回复: 12841192 查看本文章

public class MainActivity extends AppCompatActivity {
    
    
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                Toast.makeText(MainActivity.this,"弹窗",Toast.LENGTH_LONG).show();
            }
        });
    }
}

2.2.5活动中Menu

创建menu步骤:
1.res文件创建menu
2.重写onCreateOptionMenu()方法
2.重写onOptionsItemSelected() 方法

在res目录下新建一个menu文件夹,右击res目录——>New——>Directory,输入文件夹名menu,点击OK。再在menu文件夹下新建一个名为main的菜单文件,右击menu文件夹——>New——>Menu resource file。然后在main.xml中添加如下代码:

<?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>
    <item
        android:id="@+id/remove_item"
        android:title="remove">
    </item>
</menu>

其中:

item标签是用来创建具体的某一个菜单项;
android:id 给菜单项指定一个唯一的标识符;
android:title 给菜单项指定一个名称。
Activity中重写 onCreateOptionMenu() 方法,给当前活动创建菜单:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    
    
        getMenuInflater().inflate(R.menu.menu,menu);
        return  true;
    }

通过getMenuInflater()得到MenuInflater对象,再调用它的inflate()方法就可以给当前活动创建菜单了。

其中:
参数一:指定通过哪个资源文件来创建菜单。
参数二:指定菜单项添加到哪一个Menu对象中。
返回true,表示允许创建的菜单显示出来;返回false,创建的菜单无法显示。
重写onOptionsItemSelected()方法,定义菜单响应事件。添加如下代码:

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    
    
  switch (item.getItemId()) {
    
    
            case  R.id.add_item:
                Toast.makeText(this.getApplicationContext(),"add_item",Toast.LENGTH_LONG).show();
                break;
            case R.id.remove_item:
                Toast.makeText(this.getApplicationContext(),"remove_item",Toast.LENGTH_LONG).show();
                break;
        }
        return true;
    }

2.2.6销毁一个活动

按Back键
调用finish()方法。

2.3 Intent的使用

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

2.3.1 Intent的显式使用

通过intent来启动Activity

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);

其中,Intent中的参数:
参数一:启动活动的上下文;
参数二:指定想要启动的目标活动。
按Back键可以销毁当前活动,返回上一个活动。

2.3.2 Intent的隐式使用

隐式Intent不明确指出要启动的活动,而是指定一系列更为抽象的
actioncategory等信息
然后让系统去分析这个Intent,从而找出合适的活动去启动。
通过在activity标签下配置intent-filter的内容,可以指定当前活动能响应的action和category
action只有1个但是category可以有多个
调用分为2种
1.有默认的category
当有默认的category时,可以不用写

Intent intent = new Intent("action2.activity");
startActivity(intent);

2.自定义category
注意:隐式的Activity xml 中 Category要有 android.intent.category.DEFAULT

Intent intent = new Intent("action2.activity");
intent.addCategory("category2");
startActivity(intent);

AndroidManifest.xml

<activity android:name=".Main2Activity">
   <intent-filter>
        <action android:name="action2.activity" />
        <category android:name="category2" />
       <category android:name="android.intent.category.DEFAULT" /> //这条必须要有
   </intent-filter>
</activity>

2.3.3 Intent更多用法

1.例如可以调用系统的浏览器来打开一个网页。

 Intent intent1 = new Intent(Intent.ACTION_VIEW);
 intent1.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent1);

2.拨打电话

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

2.3.4 向下一个Activity传递数据

Intent不仅可以启动一个活动,还可以在启动活动的时候传递数据,传递数据的思路如下:

在启动页面中通过Intent提供的一系列putExtra()方法,将传递的数据放在Intent中
在被启动的页面中通过getIntent获取Intent对象,再调用getXXXExtra()方法获取值。

发送:
FirstActivity中传一个字符串到SecondActivity中,在FirstActivity中的代码如下:

button1.setOnClickListener(new View.OnClickListener() {
    
    
        @Override
        public void onClick(View v) {
    
    
            String data = "Hello SecondActivity";
            Intent intent = new Intent(FirstActivity.this, SecondActivity.class );
            intent.putExtra("extra_data", data);
            startActivity(intent);
        }
    });

这里采用的是显式Intent的方式来启动SecondActivity,并通过putExtra()方法传递一个字符串数据。putExtra()中的两个参数:

参数一:键。用于在后面从Intent中获取值。
参数二:要传递的数据值。

接受:
SecondActivity

public void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Intent intent = getIntent();
    String data = intent.getStringExtra("extra_data");
}

2.3.4 返回数据给上一个活动

如果在启动另外一个活动后,想得到被启动的活动的数据反馈,那么我们在启动活动的时候就不能再用startActivity()方法了,而应该使用
startActivityForResult()方法,该方法接收两个参数:

参数一:intent
参数二:请求码 用于在之后的回调中判断数据的来源。

firstActivity代码:

button1.setOnClickListener(new View.OnClickListener() {
    
    
   @Override
   public void onClick(View v) {
    
    
   Intent intent = new Intent(FirstActivity.this, SecondActivity.class );//显示调用
   startActivityForResult(intent,1);
        }
    });

请求码要确定是一个唯一值,这里传入1。
因为我们使用的是startActivityForResult()方法来启动的SecondActivity,那么在SecondActivity被销毁后会回调上一个活动的onActivityResult()方法。因为我们需要在
FirstActivity中重写这个方法来得到返回的数据,代码如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    
    
    switch (requestCode) {
    
    
        case 1:
            if (resultCode == RESULT_OK) {
    
    
                String returnData = data.getStringExtra("data_return");
                Log.d(TAG, returnData);
            }
            break;
        default:
    }
}

其中,onActivityResult()方法中带有三个参数:

参数一:requestCode,请求码。就是在活动时传入的请求码。
参数二:resultCode,结果码。就是在返回数据时传入的处理结果。
参数三:data,返回数据的Intent。

由于有可能在一个活动中调用startActivityForResult()方法去启动很多不同的活动,每一个活动返回的数据都会回调onActivityResult()方法,因此我们需要通过requestCode的值来判断数据的来源,再通过resultCode的值来判断处理结果是否成功,最后再从data中取值,这样才算完成了向上一个活动返回数据的工作。

SecondActivity代码:
SecondActivity中给按钮注册点击事件,并在点击事件中添加返回数据的逻辑,代码如下:

public void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    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();
        }
    });
}

这里我们构建了一个Intent,但这里的Intent仅仅只是用于传递数据,不启动其他任何Activity。然后将要反馈的数据放在intent中,然后再调用setResult()方法,该方法是专门用于向上一个活动返回数据的。setResult()方法有两个参数:

参数一:用于向上一个活动返回处理结果。一般只使用RESULT_OK或RESULT_CANCELED。
参数二:带有数据的Intent。

最后调用finish()方法来销毁当前活动。

上面我们是通过点击SecondActivity中的按钮来返回上一个活动,如果我们是直接按Back键返回到FirstActivity,那么数据就没法返回了。为了处理这种情况,我们可以通过重写
SecondActivity中的onBackPressed()方法来解决,代码如下:

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

这样当用户按下Back键,就会去执行onBackPressed()方法中的代码,同样可以将数据返回到FirstActivity中去。

2.4 活动的生命周期

掌握Activity的生命周期对Android开发者来说是至关重要的。

2.4.1 返回栈

Android中的活动是可以层叠的,每启动一个新的活动,就会覆盖在原活动之上,当点击Back键或执行finish操作时,就会销毁最上面的活动,下面的一个活动就会重新显示出来。而Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈又被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它都会进入返回栈,然后处于栈顶的位置。

2.4.2 活动的状态

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

  1. 运行状态

当Activity处于返回栈的栈顶时,这时就处于运行状态。系统最不愿意回收处于运行状态的活动。

  1. 暂停状态

当Activity不再处于栈顶位置,但仍然可见时,这时活动就会进入暂停状态。只有在内存极低的情况下,系统才会去考虑回收这种活动。

  1. 停止状态

当Activity不再处于栈顶位置,且不可见时,这时活动就会进入停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但当其他地方需要内存时,处于停止状态的活动有可能被系统回收。

  1. 销毁状态

当Activity从返回栈中移除后,就是处于销毁状态。系统非常喜欢回收处于这种状态的活动,从而来保证机器的内存充足。

2.4.3 活动的生存期

Activity中定义了7个回调方法来阐述活动生命周期的每一个环节:

oncreate(),该方法在活动第一次被创建的时候调用,主要完成一些初始化操作。例如加载布局、绑定事件等。
onStart() ,该方法在活动由不可见到可见的时候调用。
onResume() ,该方法是活动准备好和用户进行交互的时候调用。此时活动一定处于栈顶,且是运行状态。
onPause(),该方法在系统准备去启动或恢复另一个活动的时候调用。在这个方法中我们一般会快速的释放掉一些耗CPU的资源、保存一些关键的数据。
onStop(),该方法在活动完全不可见时调用。与onPause()的主要区别是,如果新启动的Activity是一个对话框式的活动,那么onPause()会执行,onStop()不会执行。
onDestory(),该方法在活动被销毁之前调用,之后的活动状态变为销毁状态。
onRestart(),该方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动。

上面7种方法除了onRestart()方法,其他的都是两两相对,从而又可以将活动分为3中生存期:
1.完整生存期

onCreate() ——> onDestory()

2. 可见生存期

onstart() ——> onStop()

3. 前台生存期

onResume() ——> onPause()

了更好的理解Activity的生命周期,Android官方提供了一张示意图:
在这里插入图片描述


如果活动处于停止状态时,就有可能因为内存不足被系统回收掉,这是我们就需要在回收之前保存活动的一些临时数据。Activity中提供了一个onSaveInstanceState()回调方法,该方法可以保证在活动回收之前被调用。因此我们可以在该方法中保存一些重要的临时数据。

@Override
protected void onSaveInstanceState(Bundle outState) {
    
    
    super.onSaveInstanceState(outState);
    String tempData = "Something you just typed";
    outState.putString("data_key",tempData);
}

可以看到Bundle保存数据也是通过键值对的形式保存。

数据保存下来了,那该从哪里去取呢?我们发现onCreate()方法中有一个Bundle类型的参数,这个参数保存了所有保存的数据。我们可以通过相应的键来取出对应的值。
在MainActivity的onCreate()方法中取出值:

//获取Bundle中保存的数据
    if (savedInstanceState != null) {
    
    
        String tempData = savedInstanceState.getString("data_key");
        Log.d(TAG, tempData);
    }

2.5 活动的启动模式

启动模式共有4种,分别为standard、singleTop、singleTask和singleInstance。可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式。

2.5.1 standard

standard又称作“标准式”,在不显式指定的情况下,所有的活动都是使用这种启动模式。使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动活动都会创建该活动的一个新实例。

@Override
public void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    Log.d(TAG, "Task id is " + getTaskId());
    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, FirstActivity.class);
            startActivity(intent);
        }
    });
}

在FirstActivity的基础上启动FirstActivity,并添加一条打印信息,用于打印当前活动的实例。连续点击两次按钮,可以看到logcat中打印信息如下所示:

com.example.activitytest.FirstActivity@6eb0acd
com.example.activitytest.FirstActivity@5fbd8e1
com.example.activitytest.FirstActivity@40b5390

可以看出每点击一次就会创建一个新的FirstActivity实例。此时返回栈中就有3个FirstActivity实例,因此我们需要连续按3此Back键才能退出程序。

2.5.2 singleTop

singleTop又称作“栈顶复用式”,就是在启动活动时,如果发现返回栈的栈顶已经是该活动了,那么就直接使用它,不会再创建新的实例
修改AndroidManifest.xml中的FirstActivity的启动模式,如下所示:

<activity
    android:name=".FirstActivity"
    android:launchMode="singleTop"
    android:label="This is FirstActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

之后无论你点击多少次都不会再有新的打印信息出现。因为此时FirstActivity处于栈顶的位置,不会再创建新的实例。但是当FirstActivity不处于栈顶的位置时,这时再启动FirstActivity是会创建新的实例的。

2.5.3 singleTask

singleTask又称作“栈内复用式”,每次启动活动时都会先在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用,并把这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
修改AndroidManifest.xml中的FirstActivity的启动模式,如下所示

<activity
    android:name=".FirstActivity"
    android:launchMode="singleTask"
    android:label="This is FirstActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

2.5.4 singlestance

singlestance又称作“单例式”。指定为singlestance模式的活动会启用一个新的返回栈来管理这个活动,有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈,从而达到了共享活动实例的目的。
修改AndroidManifest.xml中的SecondActivity的启动模式,如下所示:

<activity android:name=".SecondActivity"
    android:launchMode="singleInstance">
    <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>

将SecondActivity的启动模式指定为singleInstance。
分别修改FirstActivity、SecondActivity、ThirdActivity中onCreate()方法中的log打印信息:

Log.d(TAG, "Task id is " + getTaskId());

修改代码,在FirstActivity中启动SecondActivity,在SecondActivity中启动ThirdActivity,查看logcat中的打印信息,如下所示:

com.example.activitytest D/FirstActivity: Task id is 153
com.example.activitytest D/SecondActivity: Task id is 154
com.example.activitytest D/ThirdActivity: Task id is 153

从logcat信息中可以看出,SecondActivity的任务栈id与FirstActivity和ThirdActivity均不同,所以SecondActivity确实是放在一个单独的返回栈中。

2.6 活动最佳实例

2.6.1 知晓当前是在哪一个活动

步骤:
1.创建一个BaseActivity 类,并继承AppCompatActivity,这个类不在AndroidManifest.xml中注册
2.类中重写onCreate()方法

@Override
public void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    Log.d("BaseActivity", getClass().getSimpleName());//打印类名
}

3.新创建的都继承BaseActivity类

例如:MainActivity 继承BaseActivity 类
Main2Activity 继承BaseActivity 类
当启动两个类时,打印:

2020-04-07 22:02:47.958 5069-5069/com.example.activitys D/base: Base: MainActivity
2020-04-07 22:02:59.792 5069-5069/com.example.activitys D/base: Base: Main2Activity

2.6.2 随时随地退出程序

创建一个集合类对所有的活动进行管理,这样就可以实现程序随时随地退出。
定义一个addActivity()方法来向List中添加一个活动;
定义一个removeActivity()方法用于从List中移除活动;
定义一个finishAll()方法将List中存储的活动全部销毁掉。

public class Collection {
    
    
    private static List<Activity> list = new ArrayList<>();

    public static void  addActivity(Activity activity) {
    
    
        list.add(activity);
    }

    public static void removeActivity(Activity activity) {
    
    
        list.remove(activity);
    }

    public static void finishAll() {
    
    
        for (Activity activity:list) {
    
    
            if (!activity.isFinishing()) {
    
    
                activity.finish();
            }
        }
    }
}

其他界面在创建时调用:

Collection.addActivity(this);

如果想要退出:

Collection.finishAll();

你还可以在销毁所有活动的代码后面加上杀掉当前进行的代码,以保证程序完全退出,杀掉进程的代码如下所示:

android.os.Process.killProcess(android.os.Process.myPid());

其中,killProcess()方法用于杀掉一个进程,它接收一个进程id参数,可以通过myPid()方法获取当前进程的id。需要注意的是,killProcess()方法只能用于杀掉当前程序的进程,不能使用这个方法杀掉其他程序。

2.6.3 启动活动的最佳写法

通常情况下我们启动一个活动的写法如下:

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);       
startActivity(intent);

但如果我们不知道启动SecondActivity需要传递哪些数据?那我们就需要去看SecondActivity的源码或问开发SecondActivity的同事,这样就显示比较繁琐。
为了优化这种问题,我们只需要在开发SecondActivity时就定义一个启动方法:

public static void actionStart(Context context, String data1, String data2) {
    
    
    Intent intent = new Intent(context, SecondActivity.class);
    intent.putExtra("parm1", data1);
    intent.putExtra("parm2", data2);
    context.startActivity(intent);
}

直接在SecondActivity方法中定义一个actionStart()方法,将需要的数据通过参数传递过来,然后将它们存储在Intent中,最后再调用startActivity()方法启动SecondActivity。
这样我们在其他地方启动SecondActivity就很方便:

SecondActivity.actionStart(FirstActivity.this,"data1","data2");

猜你喜欢

转载自blog.csdn.net/weixin_41477306/article/details/105030745