第二堂(4)建立与使用 Activity 元件

大部份的Android应用程式,都需要一些画面提供使用者执行操作或浏览资料。Android系统使用Activity元件,负责提供应用程式一个画面的所有相关工作。一个画面就是一个继承自“android.app.Activity”的Java类别,所以通常会把它称为Activity元件,也有人叫它“活动”元件。Activity元件几乎是Android应用程式中最常使用的,应用程式的功能如果比较复杂,需要提供比较多的操作和浏览资料的画面,就会包含很多Activity元件。

每一个Activity元件除了撰写需要的Java原始程式码,也需要在应用程式设定档加入相关的设定,在application的开始和结束标签里面,使用activity标签为每一个Activity元件加入设定,所以从应用程式设定档的内容,也可以知道一个应用程式有几个Activity元件。

这一章介绍Activity元件的开发与设定方式,并了解关于Activity元件的生命周期概念,还有Activity元件之间的互动与资料传输。

8-1 记事本应用程式

之前已经建立好的应用程式主画面,提供基本的资料浏览与操作功能,现在要为它加入两个Activity元件,一个用来显示关于应用程式的资讯,另一个用来新增一笔记事本资料。

依照之前的说明,为应用程式设计需要的Activity元件,应该要先规划好画面与资源的需求,而且也要想好操作的流程,所以你可以简单的画一个像这样的图型:

AndroidTutorial5_02_04_01

在规画这些元件的时候,就可以整理好需要建立的Activity与画面配置档:

  • 应用程式资讯:AboutActivity.java,activity_about.xml
  • 新增记事本:ItemActivity.java,activity_item.xml

使用者点击主画面下方的应用程式名称,应用程式启动资讯元件,画面的设计会比较简单一些,只有两个TextView和一个Button元件,画面需要的文字资源也要先建立好。

AndroidTutorial5_02_04_02

使用者选择功能表的新增项目,应用程式启动新增记事本元件,在这个画面让使用输入标题与内容,为了后续加入的功能,先在画面中提供记事本功能按钮,例如录音与地图。

AndroidTutorial5_02_04_03

8-2 建立与启动Activity元件

现在开始建立显示应用程式资讯的Activity元件,不过要先了解Activity元件的基本运作。应用程式可以呼叫“startActivity”方法启动其它Activity元件,呼叫“finish”方法可以结束Activity元件:

AndroidTutorial5_02_04_04

现在开始新增应用程式资讯元件,在Android Studio开启MyAndroidStudio应用程式。开启“res/values/strings.xml”,加入下列的文字资源:

<string name="version">版本:AndroidTutorial_0.2.4</string>

开启“res/values/colors.xml”,加入下列的颜色资源:

<color name="dark_text">#111111</color>

在最顶端的“app”目录按鼠标左键,选择“New -> Activity -> Blank Activity”,元件与画面配置档名称依照上面的规划。开启画面配置档“activity_about.xml”,修改为下面的内容:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/retangle_drawable"
    tools:context="net.macdidi.myandroidtutorial.AboutActivity">
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_margin="@dimen/default_margin"
        android:padding="@dimen/default_padding"
        android:text="@string/about"
        android:textColor="@color/dark_text" />
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_margin="@dimen/default_margin"
        android:padding="@dimen/default_padding"
        android:text="@string/version"
        android:textColor="@color/dark_text" />
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_margin="@dimen/default_margin"
        android:padding="@dimen/default_padding"
        android:text="@android:string/ok"
        android:onClick="clickOk" />
 
</LinearLayout>

开启“AboutActivity.java”,加入取消应用程式标题的叙述,还有在负责执行按钮工作的方法中加入结束Activity元件的叙述,删除其它不需要的程式码:

package net.macdidi.myandroidtutorial;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
 
public class AboutActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 取消元件的应用程式标题
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_about);
    }
 
    // 结束按钮
    public void clickOk(View view) {
        // 呼叫这个方法结束Activity元件
        finish();
    }       
 
}

Android应用程式的每一个Activity元件,都需要在应用程式设定档加入对应的设定,使用Android Studio建立Activity元件会自动帮你加入默认的设定。开启“mainfests/AndroidManifest.xml”,找到ADT为你加入的设定,把它改为下面的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.macdidi.myandroidtutorial" >
 
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <!-- 关于应用程式的资讯 -->
        <!-- 因为使用对话框的样式,所以不用设定标题 -->            
        <activity
            android:name="net.macdidi.myandroidtutorial.AboutActivity"
            android:theme="@android:style/Theme.Dialog" />
    </application>
 
</manifest>

因为这个Activity元件的内容比较简单,使用整个萤幕显示画面的话,看起来会比较空旷一些,所以可以在设定档加入“android:theme="@android:style/Theme.Dialog"”的设定,让这个Activity元件使用对话框的样式。

最后的工作就是执行启动这个Activity元件,先检查应用程式主画面的画面配置档“activity_main.xml”,看看主画面下方显示应用程式名称元件有没有加入需要的设定:

<LinearLayout ...>  
    ...   
    <!-- 加入“android:clickable="true"”的设定,TextView元件才可以点击 -->
    <!-- 加入“android:onClick="方法名称"”的设定 -->
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_margin="@dimen/default_margin"
        android:padding="@dimen/default_padding"
        android:background="@drawable/retangle_drawable"
        android:text="@string/app_name"
        android:clickable="true"
        android:onClick="aboutApp" />
</LinearLayout>

开启“MainActivity.java”,找到aboutApp方法,把程式码改为下面的内容:

package net.macdidi.myandroidtutorial;
 
import android.content.Intent;
...
 
public class MainActivity extends Activity {
 
    ...
 
    // 点击应用程式名称元件后呼叫的方法
    public void aboutApp(View view) {
        // 建立启动另一个Activity元件需要的Intent物件
        // 建构式的第一个参数:“this”
        // 建构式的第二个参数:“Activity元件类别名称.class”
        Intent intent = new Intent(this, AboutActivity.class);
        // 呼叫“startActivity”,参数为一个建立好的Intent物件
        // 这行叙述执行以后,如果没有任何错误,就会启动指定的元件
        startActivity(intent);
    }
}

执行应用程式,看看点击主画面下方应用程式名称元件后,会不会启动与显示新的画面。在启动的画面点击确定按钮,应用程式会回到主画面。这是在应用程式启动与结束一个Activity元件的基本作法。

8-3 在结束Activity元件时传送资料

在一般的应用程式运作的时候,经常需要启动另一个Activity元件执行选择、输入或修改资料的功能,完成工作以后,再把资料回传给原来的Activity元件使用。以记事本应用程式来说,主画面负责显示所有的记事资料,需要新增资料的时候,启动一个让使用者输入资料的Activity元件,完成新增的工作回到主画面,这个新增的记事资料就要加入主画面。

如果应用程式在启动的Activity元件结束并返回后,还要执行一些特定的工作,就要使用“startActivityForResult”启动Activity元件。这是新增记事本的元件流程:

AndroidTutorial5_02_04_05

决定应用程式的流程以后,现在开始设计新增记事用的Activity元件。在最顶端的“app”目录按鼠标左键,选择“New -> Activity -> Blank Activity”,元件与画面配置档名称依照上面的规划,元件名称为“ItemActivity”,画面资源名称为“activity_item.xml”。开启activity_item.xml,把它改为下面的内容:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="1"
    tools:context="net.macdidi.myandroidtutorial.ItemActivity">
 
    <TableRow>
 
        <TextView
            android:text="@string/title"
            android:background="@drawable/retangle_drawable"
            android:padding="6sp"
            android:layout_margin="2sp"
            android:textAppearance="?android:attr/textAppearanceMedium" />
 
        <EditText
            android:id="@+id/title_text"
            android:hint="@string/enter_title"
            android:background="@drawable/retangle_drawable"
            android:padding="6sp"
            android:layout_margin="2sp"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </TableRow>
 
    <TableRow>
 
        <TextView
            android:text="@string/content"
            android:layout_height="200sp"
            android:layout_gravity="center_vertical"
            android:background="@drawable/retangle_drawable"
            android:padding="6sp"
            android:layout_margin="2sp"
            android:textAppearance="?android:attr/textAppearanceMedium" />
 
        <EditText
            android:id="@+id/content_text"
            android:hint="@string/enter_content"
            android:layout_gravity="top"
            android:layout_height="200sp"
            android:background="@drawable/retangle_drawable"
            android:padding="6sp"
            android:layout_margin="2sp"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </TableRow>
 
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:stretchColumns="*">
 
        <TableRow>
 
            <ImageButton
                android:id="@+id/take_picture"
                android:src="@drawable/take_picture_icon"
                android:onClick="clickFunction" />
 
            <ImageButton
                android:id="@+id/record_sound"
                android:src="@drawable/record_sound_icon"
                android:onClick="clickFunction" />
 
            <ImageButton
                android:id="@+id/set_location"
                android:src="@drawable/location_icon"
                android:onClick="clickFunction" />
 
            <ImageButton
                android:id="@+id/set_alarm"
                android:src="@drawable/alarm_icon"
                android:onClick="clickFunction" />
 
            <ImageButton
                android:id="@+id/select_color"
                android:src="@drawable/select_color_icon"
                android:onClick="clickFunction" />
        </TableRow>
    </TableLayout>
 
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:stretchColumns="*">
 
        <TableRow>
 
            <Button
                android:id="@+id/cancel_item"
                android:text="@android:string/cancel"
                android:onClick="onSubmit"
                android:padding="6sp"
                android:layout_margin="2sp"
                android:textAppearance="?android:attr/textAppearanceMedium" />
 
            <Button
                android:id="@+id/ok_teim"
                android:text="@android:string/ok"
                android:onClick="onSubmit"
                android:padding="6sp"
                android:layout_margin="2sp"
                android:textAppearance="?android:attr/textAppearanceMedium" />
        </TableRow>
    </TableLayout>
 
</TableLayout>

开启“ItemActivity.java”,修改为下面的内容。为了以后需要扩充的功能,加入一些控制按钮执行工作的程式码:

package net.macdidi.myandroidtutorial;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
 
public class ItemActivity extends Activity {
 
    private EditText title_text, content_text;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item);
 
        processViews();
    }
 
    private void processViews() {
        title_text = (EditText) findViewById(R.id.title_text);
        content_text = (EditText) findViewById(R.id.content_text);
    }
 
    // 点击确定与取消按钮都会呼叫这个方法
    public void onSubmit(View view) {
        // 确定按钮
        if (view.getId() == R.id.ok_teim) {
            // 读取使用者输入的标题与内容
            String titleText = title_text.getText().toString();
            String contentText = content_text.getText().toString();
 
            // 取得回传资料用的Intent物件
            Intent result = getIntent();
            // 设定标题与内容
            result.putExtra("titleText", titleText);
            result.putExtra("contentText", contentText);
 
            // 设定回应结果为确定
            setResult(Activity.RESULT_OK, result);
        }
 
        // 结束
        finish();
    }
 
    // 以后需要扩充的功能
    public void clickFunction(View view) {
        int id = view.getId();
 
        switch (id) {
        case R.id.take_picture:
            break;
        case R.id.record_sound:
            break;
        case R.id.set_location:
            break;
        case R.id.set_alarm:
            break;
        case R.id.select_color:
            break;
        }
 
    }
 
}

开启“AndroidManifest.xml”,找到Android Studio为你加入的设定,把它改为下面的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.macdidi.myandroidtutorial" >
 
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".AboutActivity"
            android:theme="@android:style/Theme.Dialog" />
        <!-- 记事项目元件 -->
        <activity
            android:name="net.macdidi.myandroidtutorial.ItemActivity" />
    </application>
 
</manifest>

开启“MainActivity.java”,把程式码改为下面的内容:

package net.macdidi.myandroidtutorial;
 
...
 
public class MainActivity extends ActionBarActivity {
 
    ...
 
    public void clickMenuItem(MenuItem item) {
        int itemId = item.getItemId();
 
        switch (itemId) {
        case R.id.search_item:
            break;
        // 使用者选择新增选单项目  
        case R.id.add_item:
            // 建立启动另一个Activity元件需要的Intent物件
            Intent intent = new Intent(this, ItemActivity.class);
            // 呼叫“startActivityForResult”,第二个参数“0”目前没有使用
            startActivityForResult(intent, 0);
            break;
        case R.id.revert_item:
            break;
        case R.id.delete_item:
            break;
        case R.id.googleplus_item:
            break;
        case R.id.facebook_item:
            break;
        }
 
    }
 
    ...
}

使用“startActivityForResult”启动Activity元件,结束并返回以后,Android会呼叫“onActivityResult”方法一次。所以覆写这个方法,在里面执行需要的判断与工作。同样在“MainActivity.java”,因为原来使用字串阵列提供资料给ListView元件,现在要把它换成“ArrayList”物件,这样可以修改ListView包装的资料项目。把程式码改为下面的内容:

package net.macdidi.myandroidtutorial;
 
import java.util.ArrayList;
 
...
 
public class MainActivity extends ActionBarActivity {
 
    private ListView item_list;
    private TextView show_app_name;
 
   // 换掉原来的字串阵列
    private ArrayList<String> data = new ArrayList<>();
    private ArrayAdapter<String> adapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        processViews();
        processControllers();
 
        // 加入范例资料
        data.add("关于Android Tutorial的事情");
        data.add("一只非常可爱的小狗狗!");
        data.add("一首非常好听的音乐!");
 
        int layoutId = android.R.layout.simple_list_item_1;
        adapter = new ArrayAdapter<String>(this, layoutId, data);
        item_list.setAdapter(adapter);
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 如果被启动的Activity元件传回确定的结果
        if (resultCode == Activity.RESULT_OK) {
            // 读取标题
            String titleText = data.getStringExtra("titleText");
            // 加入标题项目
            this.data.add(titleText);
            // 通知资料已经改变,ListView元件才会重新显示
            adapter.notifyDataSetChanged();
        }
    }
 
    ...
 
    private void processControllers() {
        AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() {
 
            @Override
            public void onItemClick(AdapterView<?> parent, View view, 
                    int position, long id) {
                // 换掉“data[position]”
                Toast.makeText(MainActivity.this, 
                        data.get(position), Toast.LENGTH_LONG).show();
            }
        };
 
        item_list.setOnItemClickListener(itemListener);
 
        AdapterView.OnItemLongClickListener itemLongListener = new AdapterView.OnItemLongClickListener() {
 
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, 
                    int position, long id) {
                // 换掉“data[position]”
                Toast.makeText(MainActivity.this, 
                        "Long: " + data.get(position), Toast.LENGTH_LONG).show();
                return false;
            }
        };
 
        ...
}

执行应用程式,点击功能表的新增项目,在启动的画面输入标题后,选择确定按钮,回到主画面后,看看有没有多一笔你刚才输入的资料。

8-4 在启动Activity元件时传送资料

以这个记事应用程式来说,除了已经写好的新增记事资料功能,通常也需要让使用者修改记事资料。在主画面点击一笔需要修改的记事项目以后,应用程式开启修改记事的元件,让使用者执行修改资料的工作。这个修改记事的元件其实跟新增记事的功能是差不多的,所以通常就不会另外设计一个新的Activity元件,让已经设计好的“ItemActivity”同时提供新增与修改两种功能。

为了让一个Activity元件可以执行两种工作,通常会帮这类元件另外取不同的“Action”名称。开启“AndroidManifest.xml”,把它改为下面的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
 
    ...
 
    <application ... >
        ...
 
        <!-- 记事项目元件 -->
        <activity
            android:name="net.macdidi.myandroidtutorial.ItemActivity">
            <intent-filter>
                <!-- 新增用的名称 -->
                <action android:name="net.macdidi.myandroidtutorial.ADD_ITEM"/>
                <!-- 修改用的名称 -->
                <action android:name="net.macdidi.myandroidtutorial.EDIT_ITEM"/>
                <!-- 一定要加入,内容固定不变 -->
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
 
    </application>
 
</manifest>

开启“ItemActivity.java”,修改为下面的内容:

package net.macdidi.myandroidtutorial;
 
...
 
public class ItemActivity extends Activity {
 
    private EditText title_text, content_text;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item);
 
        processViews();
 
        // 取得Intent物件
        Intent intent = getIntent();
        // 读取Action名称
        String action = intent.getAction();
 
        // 如果是修改记事
        if (action.equals("net.macdidi.myandroidtutorial.EDIT_ITEM")) {
            // 接收与设定记事标题
            String titleText = intent.getStringExtra("titleText");
            title_text.setText(titleText);
        }
    }
 
    ...
 
}

开启“MainActivity.java”,把程式码改为下面的内容:

package net.macdidi.myandroidtutorial;
 
...
 
public class MainActivity extends Activity {
 
    private ListView item_list;
    private TextView show_app_name;
 
    private ArrayList<String> data = new ArrayList<>();
    private ArrayAdapter<String> adapter;
 
    ...
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            String titleText = data.getStringExtra("titleText");
 
            // 如果是新增记事
            if (requestCode == 0) {
                // 加入标题项目
                this.data.add(titleText);
                // 通知资料已经改变,ListView元件才会重新显示
                adapter.notifyDataSetChanged();
            }
            // 如果是修改记事
            else if (requestCode == 1) {
                // 读取记事编号
                int position = data.getIntExtra("position", -1);
 
                if (position != -1) {
                    // 设定标题项目
                    this.data.set(position, titleText);
                    // 通知资料已经改变,ListView元件才会重新显示
                    adapter.notifyDataSetChanged();
                }
            }
        }
    }
 
    private void processViews() {
        item_list = (ListView)findViewById(R.id.item_list);
        show_app_name = (TextView) findViewById(R.id.show_app_name);
    }
 
    private void processControllers() {
        OnItemClickListener itemListener = new OnItemClickListener() {
 
            @Override
            public void onItemClick(AdapterView<?> parent, View view, 
                    int position, long id) {
                // 使用Action名称建立启动另一个Activity元件需要的Intent物件
                Intent intent = new Intent("net.macdidi.myandroidtutorial.EDIT_ITEM");
 
                // 设定记事编号与标题
                intent.putExtra("position", position);
                intent.putExtra("titleText", data.get(position));
 
                // 呼叫“startActivityForResult”,第二个参数“1”表示执行修改
                startActivityForResult(intent, 1);
            }
        };
 
        ...     
    }   
 
    ...
 
    public void clickMenuItem(MenuItem item) {
        int itemId = item.getItemId();
 
        switch (itemId) {
        case R.id.search_item:
            break;
        case R.id.add_item:
            // 使用Action名称建立启动另一个Activity元件需要的Intent物件
            Intent intent = new Intent("net.macdidi.myandroidtutorial.ADD_ITEM");
            // 呼叫“startActivityForResult”,,第二个参数“0”表示执行新增
            startActivityForResult(intent, 0);
            break;
        case R.id.revert_item:
            break;
        case R.id.delete_item:
            break;
        case R.id.googleplus_item:
            break;
        case R.id.facebook_item:
            break;
        }
 
    }
 
    ...
}

执行应用程式,试试看新增记事功能是否可以正常运作。在主画面点选一个记事项目,修改记事标题并确定后,看看主画面的记事资料会不会更新。目前完成的功能并没有处理记事资料的内容,也还没有储存到数据库,所以不会保存新增与修改后的资料。

文章导航