Android 7编程入门经典:使用Android Studio 2(第4版)
上QQ阅读APP看书,第一时间看更新

3.1 理解Activity

本章首先展示如何创建Activity。要创建Activity,首先要新建一个继承Activity基类的Java类。

        package com.jfdimarzio.chapter1helloworld;


              import android.support.v7.app.AppCompatActivity;
              import android.os.Bundle;


        public class MainActivity extends AppCompatActivity {


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

Activity类从res/layout文件夹中加载由XML文件定义的用户界面(UI)组件。在这个示例中,是从main.xml文件中加载UI的:

              setContentView(R.layout.activity_main);

应用中的每一个Activity必须在AndroidManifest.xml文件中声明,如下所示:

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


            <application
              android:allowBackup="true"
              android:icon="@mipmap/ic_launcher"
              android:label="@string/app_name"
              android:supportsRtl="true"
              android:theme="@style/AppTheme">
                <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>

Activity基类定义了一系列事件,管理着Activity的生命周期。图3-1展示了一个Activity的生命周期。

其他应用需要内存进程被销毁用户重新启动该activity被关闭Activity Activity不再显示新的Activity启动Activity在运行Activity启动Activity回到前台Activity回到前台

图3-1

Activity类定义了以下事件:

● onCreate()——当首次创建Activity时被调用

● onStart()——当Activity显示在用户面前时被调用

● onResume()——当Activity能够开始与用户交互时被调用

● onPause()——在当前的Activity将暂停并且前一个Activity将要被重新开始时被调用

● onStop()——当Activity不再显示在用户面前时被调用

● onDestroy()——当Activity被系统销毁前调用(手动销毁或者系统回收内存时)

● onRestart()——当Activity停止后并重新启动时被调用

在默认情况下,新建的Activity都包含onCreate()事件。这个事件处理程序中的代码将有助于在屏幕上显示UI元素。

图3-2展示了一个Activity的生命周期以及它经历的各个阶段—— 从Activity启动到结束。

用户重新启动该Activity进程被销毁其他应用需要内存被关闭Activity该Activity不再显示另一个Activity切换到了该Activity的前面Activity在运行Activity启动该Activity回到前台该Activity回到前台

图3-2

理解一个Activity各个阶段的最佳方法是创建一个新项目,实现各种事件,根据各种用户交互观察Activity的不同状态。

试一试:了解Activity的生命周期(Activity101.zip)

(1) 使用Android Studio创建一个新Android项目,命名为Activity101。

(2) 在Activity101Activity.java文件中,添加如下突出显示的代码(注意:在这个示例中,确保把所有对com.jfdimarzio的引用修改成你自己项目使用的包名)。

        package com.jfdimarzio.activity101;


        import android.support.v7.app.AppCompatActivity;
        import android.os.Bundle;
        import android.util.Log;


        public class MainActivity extends AppCompatActivity
        {
            String tag = "Lifecycle Step";
            @Override
            protected void onCreate(Bundle savedInstanceState)
            {
              super.onCreate(savedInstanceState);

              setContentView(R.layout.activity_main);
              Log.d(tag, "In the onCreate() event");
          }


          public void onStart()
          {
              super.onStart();
              Log.d(tag, "In the onStart() event");
          }


          public void onRestart()
          {
              super.onRestart();
              Log.d(tag, "In the onRestart() event");
          }


          public void onResume()
          {
              super.onResume();
              Log.d(tag, "In the onResume() event");
          }


          public void onPause()
          {
              super.onPause();
          }
              Log.d(tag, "In the onPause() event");


          public void onStop()
          {
              super.onStop();
              Log.d(tag, "In the onStop() event");
          }


          public void onDestroy()
          {
              super.onDestroy();
              Log.d(tag, "In the onDestroy() event");
          }
        }

(3) 单击快捷键Shift+F9或者选择菜单Run | Debug开始调试应用。在弹出窗口中选择一个Android虚拟设备。

(4) 当Activity被首次加载时,应该可以在logcat控制台中看到非常相似的信息(如图3-3所示)。如果没有看到logcat控制台,单击Android Studio窗口底端的Android Monitor。

        11-16 06:25:59.396: D/Lifecycle Step(559): In the onCreate() event
        11-16 06:25:59.396: D/Lifecycle Step(559): In the onStart() event
        11-16 06:25:59.396: D/Lifecycle Step(559): In the onResume() event

图3-3

(5) 如果在Android模拟器中单击Back按钮,将会看到如下信息:

        11-16 06:29:26.665: D/Lifecycle Step(559): In the onPause() event
        11-16 06:29:28.465: D/Lifecycle Step(559): In the onStop() event
        11-16 06:29:28.465: D/Lifecycle Step(559): In the onDestroy() event

(6) 单击Home按钮,单击Overview图标,选择Activity101应用,将会看到如下信息:

        11-16 06:31:08.905: D/Lifecycle Step(559): In the onCreate() event
        11-16 06:31:08.905: D/Lifecycle Step(559): In the onStart() event
        11-16 06:31:08.925: D/Lifecycle Step(559): In the onResume() event

(7) 在Android模拟器中单击Home按钮,然后单击Phone按钮,将Activity推到后台。观察logcat窗口中的输出:

        11-16 06:32:00.585: D/Lifecycle Step(559): In the onPause() event
        11-16 06:32:05.015: D/Lifecycle Step(559): In the onStop() event

(8) 注意onDestory()事件还没有被调用,说明这个Activity仍然在内存中。单击Back按钮退出电话拨号应用。Activity又重新显示出来。观察logcat窗口中的输出:

        11-16 06:32:50.515: D/Lifecycle(559): In the onRestart() event
        11-16 06:32:50.515: D/Lifecycle(559): In the onStart() event
        11-16 06:32:50.515: D/Lifecycle(559): In the onResume() event

此时onRestart()事件被激活,紧接着onStart()和onResume()方法被依次激活。

示例说明

在这个示例中可以看到,当单击Back按钮时Activity会被销毁。理解这一点非常关键,因为无论Activity现在处于什么状态都会丢失。也就是说你需要在Activity中写一些代码,当它被销毁时保存它的状态(第4章中会讨论如何实现)。此刻,注意onPause()方法在下列两种情况下都会被调用:

● 当Activity被发送到后台时

● 当用户单击Back按钮关闭Activity时

当一个Activity启动时,onStart()和onResume()方法总会被调用,无论这个Activity是从后台被恢复的还是新建的。当一个Activity首次创建时,会调用onCreate()方法。

从之前的示例中,可总结出以下知识点:

● 使用onCreate()方法新建和实例化在应用中使用的对象。

● 使用onResume()方法启动那些当Activity在前台时需要运行的服务和代码。

● 使用onPause()方法停止那些当Activity不在前台时不需要运行的服务和代码。

● 使用onDestory()方法在Activity销毁前释放资源。

注意:即使一个应用只有一个Activity并且这个Activity已经被杀死,这个应用仍然在内存中运行。

3.1.1 在Activity上应用样式和主题

在默认情况下,Activity使用默认的Android主题。然而,近几年一直在推广使用一个称为Material的新主题。Material主题具有更加现代和干净的外观。

Material主题有两个版本可供Android开发者选择:Material Light和Material Dark。在AndroidManifest.xml中可以指定使用任意一种。

要在Activity上应用某一个Material主题,可以非常简单地在AndroidManifest.xml文件中修改<Application>元素的android:theme默认值(请确保将所有“com.jfdimarzio”实例修改成你自己项目使用的包名)。

        <? 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.jfdimarzio.activity101">


            <application
              android:allowBackup="true"
              android:icon="@mipmap/ic_launcher"
              android:label="@string/app_name"
              android:supportsRtl="true"
              android:theme="@android:style/Theme.Material">
              <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>

把默认主题修改为@android:style/Theme.Material,如以上代码段中突出显示的代码,应用Material Dark主题会使应用具有深色的外观,如图3-4所示。

图3-4

3.1.2 隐藏Activity的标题

如果需要的话,也可以隐藏Activity的标题(比如说,你只想为用户显示一个状态更新)。要隐藏标题,可以调用requestWindowFeature()方法并传入Window.FEATURE_NO_TITLE常量,如下所示:

        import android.support.v7.app.AppCompatActivity;
        import android.os.Bundle;
        import android.view.Window;


        public class MainActivity extends AppCompatActivity {
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              requestWindowFeature(Window.FEATURE_NO_TITLE);
            }


        }

接着需要修改AndroidManifest.xml文件中的主题,设置一个没有标题栏的主题。请确保把所有com.jfdimarzio实例修改成你自己项目使用的包名。

        package com.jfdimarzio.activity101;

    <?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.jfdimarzio.activity101">


        <application
          android:allowBackup="true"
          android:icon="@mipmap/ic_launcher"
          android:label="@string/app_name"
          android:supportsRtl="true"
          android:theme="@android:style/Theme.NoTitleBar">
          <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>

图3-5显示了没有标题栏的主题。

图3-5

3.1.3 显示对话框

在应用中,需要显示对话框获得用户确认的情况非常多。这种情况下,可重写在Activity基类中定义的保护类型方法onCreateDialog()来显示对话框。在以下的“试一试”部分中将演示代码是如何实现的。

试一试:使用Activity显示一个对话框(Dialog.zip)

(1) 使用Android Studio创建一个新的Android项目,命名为Dialog。在选项界面中,把主Activity命名为DialogActivity。

(2) 在AndroidManifest.xml文件中添加以下粗体标出的主题值。请确保将所有com.jfdimarzio实例改成你自己项目使用的包名。

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


            <application
              android:allowBackup="true"
              android:icon="@mipmap/ic_launcher"
              android:label="@string/app_name"
              android:supportsRtl="true"
              android:theme="@style/AppTheme" >
              <activity
                  android:name=".DialogActivity"
                  android:label="@string/app_name"
                  android:theme="@style/Theme.AppCompat.Dialog" >
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />


                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            </application>
        </manifest>

(3) 检查你的DialogActivity.java文件中的代码是否和以下代码一致:

        package com.jfdimarzio.dialog;


        import android.os.Bundle;
        import android.support.design.widget.FloatingActionButton;
        import android.support.design.widget.Snackbar;
        import android.support.v7.app.AppCompatActivity;
        import android.support.v7.widget.Toolbar;
        import android.view.View;
        import android.view.Menu;
        import android.view.MenuItem;

          public class DialogActivity extends AppCompatActivity {


              @Override
              protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_dialog);
                Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
                setSupportActionBar(toolbar);


                FloatingActionButton fab = (FloatingActionButton)
                    findViewById(R.id.fab);
                fab.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Snackbar.make(view, "Replace with your own action",
                          Snackbar.LENGTH_LONG)
                              .setAction("Action", null).show();
                    }
                });
              }


              @Override
              public boolean onCreateOptionsMenu(Menu menu) {
                // Inflate the menu; this adds items to the action bar if it is present.
                getMenuInflater().inflate(R.menu.menu_dialog, menu);
                return true;
              }


              @Override
              public boolean onOptionsItemSelected(MenuItem item) {
                // Handle action bar item clicks here. The action bar will
                // automatically handle clicks on the Home/Up button, so long
                // as you specify a parent activity in AndroidManifest.xml.
                int id = item.getItemId();


                //noinspection SimplifiableIfStatement
                if (id == R.id.action_settings) {
                    return true;
                }


                return super.onOptionsItemSelected(item);
              }
          }

(4) 单击快捷键Shift+F9在Android模拟器中调试应用。单击按钮显示对话框(如图3-6所示)。

图3-6

示例说明

Android使用AppCompat.Dialog主题把标准Activity绘制成一个自由漂浮的对话框。如果你想在对话框中提供一个OK或者Cancel选择,在对话框中添加类似的按钮是非常容易的。

需要注意,主题只是应用在Activity上,而不是项目上。因此,可以在一个项目中有多个Activity,但只需要在一个Activity上应用对话框主题。

3.1.4 显示进度对话框

当一个应用在执行一个耗时很长的任务时,在Android上通常能看到一个Please wait对话框。例如,应用可能需要登录服务器以后用户才能使用,或者在向用户显示结果以前需要做一个计算。这种情况下,显示一个对话框是非常有用的,这个对话框称为进度对话框,所以用户可以继续等待。

Android提供了一个ProgressDialog类,当你想要向用户显示一个进度条时可以调用这个类。在Activity中调用ProgressDialog非常方便。

在以下的“试一试”部分中将演示如何显示进度对话框。

试一试:显示进度(请等待)对话框

(1) 使用本章之前创建的Activity101项目,确保在AndroidManifest.xml文件中使用的是Material主题。请确保将所有com.jfdimarzio实例修改成你自己项目使用的包名。

        <? 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.jfdimarzio.activity101">


            <application
              android:allowBackup="true"
              android:icon="@mipmap/ic_launcher"
              android:label="@string/app_name"
              android:supportsRtl="true"
              android:theme="@android:style/Theme.Material">
              <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>

(2) 在MainActivity.java文件中加入以下示例代码中的粗体显示部分。

        package com.jfdimarzio.activity101;
        import android.app.Activity;
        import android.app.ProgressDialog;
        import android.os.CountDownTimer;
        import android.os.Bundle;




        public class MainActivity extends Activity{


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


            }


            public void onStart()
            {
              super.onStart();
                progressDialog = ProgressDialog.show(this, "Please Wait",
                  "Processing...", true);
                CountDownTimer timer = new CountDownTimer(3000,1000) {
                  @Override

                  public void onTick(long millisUntilFinished) {


                  }


                  @Override
                  public void onFinish() {
                      progressDialog.dismiss();
                  }
              }.start();
          }
        }

(3) 单击快捷键Shift+F9在Android模拟器中调试应用。你能看到一个进度对话框,如图3-7所示。它会在三秒后消失。

图3-7

示例说明

要创建一个进度对话框,需要创建ProgressDialog类的一个实例,并且调用show()方法:

        progressDialog = ProgressDialog.show(this, "Please Wait", "Processing...", true);

这会显示如图3-7所示的进度对话框。因为这个是模态对话框,所以在它关闭前会遮住整个UI。要关闭这个对话框,需要创建一个计时器并在三秒后调用dismiss()方法(第12章会介绍线程,讨论当一个进度对话框显示时,如何从外部线程中调用方法完成其他工作)。

        CountDownTimer timer = new CountDownTimer(3000,1000) {
                  @Override
                  public void onTick(long millisUntilFinished) {


                  }


                  @Override
                  public void onFinish() {
                      progressDialog.dismiss();
                  }
              }.start();

三秒钟后,调用dismiss()方法关闭了对话框。

下一节将解释如何使用Intent,使用Intent有助于在多个Activity之间导航。