iT邦幫忙

DAY 23
2

以「寶寶聯絡簿」為例,適合 Android 初學者的學習筆記系列 第 23

寶寶生活記錄 App (Day23 「新增寶寶」實作-2)

昨天我們已經建立了 AddBabyActivity 這個新 Activity,今天我們要來實作裡面的細節,其中會特別介紹 Listener 和 Callback Method 的概念,此外由於這個 Activity 不只能讓使用者新增寶寶,還能編輯寶寶資料,因此決定改名為 BabyEditorActivity,改名的方法是反白 AddBabyActivity 後按右鍵選 Refactor -> Rename,這樣做的好處是 AndroidManifest.xml 的內容會跟著更改 (甚至其它相關 Java 檔的內容都會跟著修改),不需要再去修改 AndroidManifest.xml 的內容,現在就讓我們來看 BabyEditorActivity.java 的內容 (尚未全部實作完成):

package lincyu.babylog.babymanager;

import android.app.ActionBar;
import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import java.util.Calendar;

import lincyu.babylog.Constants;
import lincyu.babylog.R;
import lincyu.babylog.Util;
import lincyu.babylog.db.Baby;
import lincyu.babylog.db.BabyDB;


public class BabyEditorActivity_20141015 extends ActionBarActivity {

    private Baby baby;

    private Button btn_cancel, btn_complete, btn_birthday;
    private ImageView iv_headshot;
    private EditText et_name, et_nickname;
    private RadioGroup rg_gender;
    private RadioButton rb_girl, rb_boy;
    private int year, month, day;

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

        Intent intent = getIntent();
        int babyid = intent.getIntExtra(Constants.EXTRA_BABYID, -1);
        if (babyid == -1) {
            baby = new Baby();
        } else {
            baby = BabyDB.getBabyById(this, babyid);
        }
        viewInitialization();
    }

    private void viewInitialization() {
        ActionBar actionbar = getActionBar();
        actionbar.setDisplayHomeAsUpEnabled(true);
        actionbar.setDisplayShowHomeEnabled(false);

        btn_complete = (Button)findViewById(R.id.btn_complete);
        btn_complete.setOnClickListener(complete_listener);

        btn_cancel = (Button)findViewById(R.id.btn_cancel);
        btn_cancel.setOnClickListener(cancel_listener);

        iv_headshot = (ImageView)findViewById(R.id.iv_headshot);

        et_name = (EditText)findViewById(R.id.et_name);
        et_name.setText(baby.name);
        et_name.setOnFocusChangeListener(et_focuschange);

        et_nickname = (EditText)findViewById(R.id.et_nickname);
        et_nickname.setText(baby.nickname);
        et_nickname.setOnFocusChangeListener(et_focuschange);

        btn_birthday = (Button)findViewById(R.id.btn_birthday);
        if (baby.birthday == 0) {
            baby.birthday = Calendar.getInstance().getTimeInMillis();
        }
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(baby.birthday);
        year = c.get(Calendar.YEAR);
        month = c.get(Calendar.MONTH);
        day = c.get(Calendar.DAY_OF_MONTH);
        btn_birthday.setText(Util.getDateString(baby.birthday));
        btn_birthday.setOnClickListener(birthday_listener);

        rb_girl = (RadioButton)findViewById(R.id.rb_girl);
        rb_boy = (RadioButton)findViewById(R.id.rb_boy);
        if (baby.gender == Baby.GENDER_FEMALE) {
            rb_girl.setChecked(true);
            rb_boy.setChecked(false);
        } else {
            rb_girl.setChecked(false);
            rb_boy.setChecked(true);
        }
        rg_gender = (RadioGroup)findViewById(R.id.rg_gender);
        rg_gender.setOnCheckedChangeListener(gender_listener);

    }

    private View.OnFocusChangeListener et_focuschange = new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View view, boolean focus) {
            if (focus) {
                view.setBackgroundResource(R.drawable.edittext_focus);
            } else {
                view.setBackgroundResource(R.drawable.edittext_notfocus);
            }
        }
    };

    private RadioGroup.OnCheckedChangeListener gender_listener = new
            RadioGroup.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId) {
                case R.id.rb_girl:
                    baby.gender = Baby.GENDER_FEMALE;
                    break;
                case R.id.rb_boy:
                    baby.gender = Baby.GENDER_MALE;
                    break;
            }
        }
    };

    private View.OnClickListener birthday_listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            new DatePickerDialog(BabyEditorActivity_20141015.this, new DatePickerDialog.OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker view, int year, int month, int day) {
                    BabyEditorActivity_20141015.this.year = year;
                    BabyEditorActivity_20141015.this.month = month;
                    BabyEditorActivity_20141015.this.day = day;
                    Calendar c = Calendar.getInstance();
                    c.set(Calendar.YEAR, year);
                    c.set(Calendar.MONTH, month);
                    c.set(Calendar.DAY_OF_MONTH, day);
                    c.set(Calendar.HOUR_OF_DAY, 0);
                    c.set(Calendar.SECOND, 0);
                    c.set(Calendar.MILLISECOND, 0);
                    baby.birthday = c.getTimeInMillis();
                    btn_birthday.setText(Util.getDateString(baby.birthday));
                }
            }, year, month, day).show();

        }
    };

    private View.OnClickListener complete_listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            save();
            finish();
        }
    };

    private View.OnClickListener cancel_listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            finish();
        }
    };

    private void save() {
        baby.name = et_name.getEditableText().toString();
        baby.nickname = et_nickname.getEditableText().toString();
        BabyDB.addBaby(this, baby);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}

筆者將分段解說,首先來看 onCreate 裡面的內容,43 ~ 49 行是用來判斷使用者是要新增寶寶還是要編輯寶寶,首先利用 Activity 類別的 getIntent 取得啟動此 Activity 的 Intent (讀者要慢慢練習到 Android Developers 網站查詢說明文件,getIntent 的說明為:Return the intent that started this activity.),接著取出 Intent 所攜帶的資料,攜帶的資料跟先前介紹過的偏好設定 (SharedPreferences) 一樣,採用 Key-Value 的概念來儲存 (筆者自己定義了一個 Key 為 “EXTRA_BABYID”),getIntExtra 是要取出一個整數資料,第一個參數為 Key,第二個參數則是預設值 (Default Value),各位如果還記得昨天我們於 CalendarActivity 透過 Explicit Intent 啟動 BabyEditorActivity 時,並沒有呼叫 putExtra 放入任何資料,這時候預設值就會被使用,預設值 -1 代表我們要新增寶寶 (-1 是筆者自訂的,請自行靈活應用),若 babyid 不是 1,則會從資料庫讀取特定 babyid 的寶寶資料,代表我們要編輯該寶寶資料。

通常我們會在 onCreate 做 View 的初始化,筆者通常會在 View 的初始化做兩件事,第一件事當然是產生 View 物件,由於我們使採用 XML 來設計這個 Activity 的版面,因此是透過 Activity 類別的 findViewById 來產生 View 物件,第二件事是若這個 View 有需要設定 Listener (例如 Button 會需要透過設定 Listener 來處理被按下後的動作),今天就來說明一下如何處理按鈕按下的動作。

設定按鈕按下後的動作是透過 setOnClickListener 這個 Method,這是 View 類別的 Method [1],其說明為「Register a callback to be invoked when this view is clicked.」,所謂的 Callback Method,以我們的例子來說就是 BabyEditorActivity 並不會自己去呼叫該 Method,BabyEditorActivity 只是將這Callback Method 內容寫好讓別的程式去呼叫 (有一個程式偵測到按鈕被按下時會呼叫那個 Callback Method)。

setOnClickListener 需要一個 Listener (View.OnClickListener [2]) 物件當參數,Listener 通常實作成一個 Interface,裡面會定義一個抽象方法,對於 BabyEditorActivity 來說,這個抽象方法就是一個 Callback Method,需要覆寫這個抽象方法,以 View.OnClickListener 來說,這個抽象方法為 onClick,請讀者自行去閱讀「寶寶生日」、「完成」、「取消」按鈕的 Listener,若讀者遇到沒碰過的類別,例如 Calendar, DatePickerDialog,也請自行至 Android Developers 查詢,如此才能自我成長。

為了讓讀者看更多 Listener 的例子,筆者也幫 EditText 加上 OnFocusChangeListener,onFocusChange(View view, boolean focus) 也是一個 Callback Method,當 View 的焦點 (Focus)改變時,這個 Callback Method 會被呼叫,筆者會嘗試更換 EditText 的背景。

最後有個小地方提醒一下讀者,執行時會發現 Action Bar 有個「返回箭頭」,如下所示:

相關程式碼如下 (第 2 行是讓箭頭出現,第 3 行是讓 Icon 消失):

ActionBar actionbar = getActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setDisplayShowHomeEnabled(false);

同時也要修改 onOptionsItemSelected ,內容如下:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

參考資料

[1] View | Android Developers, http://developer.android.com/reference/android/view/View.html

[2] View.OnClickListener | Android Developers, http://developer.android.com/reference/android/view/View.OnClickListener.html


上一篇
寶寶生活記錄 App (Day22 「新增寶寶」實作-1)
下一篇
寶寶生活記錄 App (Day24 AdapterView初探)
系列文
以「寶寶聯絡簿」為例,適合 Android 初學者的學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
lulubear
iT邦新手 5 級 ‧ 2014-10-16 09:41:08

哇~好認真

我要留言

立即登入留言