Chủ Nhật, 11 tháng 3, 2018

Lưu và phục hồi trạng thái của Activity trong Android

Phần này tạo một ứng dụng đơn giản tên là SaveAndRestoreState để tìm hiểu việc lưu lại trạng thái của Activity và phục hồi trạng thái của Activity trong vòng đời hoạt động của nó.

https://xuanthulab.net/luu-va-phuc-hoi-trang-thai-cua-activity-trong-android.html

Tạo Project - SaveAndRestoreState

Mở Android Studio chọn tạo Project mới, nhập tên ứng dụng là SaveAndRestoreState, khi đến hộp thoại Add an Activity to Mobile ta chọn mẫu có tên Basic Activity, giữ nguyên gợi ý trong hộp thọa Config Activity (Activity name là MainActivity)
Dự án mới tạo ra, phần code Java lưu tại: MainActivity.java có nội dung (do Android Studio tự phát sinh cơ bản):
package net.xuanthulba.saveandrestorestate;

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 MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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_main, 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);
    }
}
Bạn thấy hàm onCreate của Activity trên nạp layout bằng phương thức setContentView(R.layout.activity_main);, bạn mở layout này ra (mở file res\layout\activity_main.xml), nội dung của nó như sau:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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="net.xuanthulba.saveandrestorestate.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout> 
Đọc qua nội dung trên, bạn thấy nó dùng một View có tên CoordinatorLayout làm phần tử chính để bố trí các View con khác trong nó, các View con có đầu tiên là AppBarLayout, Toolbar là phần thanh ngang phía trên đầu, tiếp theo nó chèn một số phần tử khác từ content_main bằng cú pháp: <include layout="@layout/content_main" />, ở cuối có một phần tử là FloatingActionButton hiện thị nút bấm hình tròn.
Bạn mở file res/layout/content_main.xml ra, bạn thấy nó có phần tử tên TextView là loại View hiện thị chữ, giờ với mục đích có thể nhập chữ đổi phần tử này thành EditText và gán cho nó một ID là mytext (trong Code Java có thể lấy giá trị này bằng R.id.mytext)
Như vậy content_main.xml có nội dung:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="net.xuanthulba.saveandrestorestate.MainActivity"
    tools:showIn="@layout/activity_main">

    <EditText
        android:id="@+id/mytext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
Và kết quả quan sát ở chế độ soạn thảo Layout: activity_main

Bạn có thể nhấn Shift + F10 để thạy chử trên máy ảo, ứng dụng hoạt động tốt

Lưu lại và phục hồi trạng thái trong Activity

Trong code trên, phần tử EditText có gán ID là mytext đo đó nếu bạn chạy ứng dụng và nhập vào một dòng chữ, nếu lúc đó bạn xoay màn hình thì như đã biết trong vòng đời Activity sẽ gọi lại onCreate, nhưng dòng chữ đó vẫn được giữ nguyên trên màn hình. Có được điều này bởi vì Activity cùng với TextView đã thi hành chức năng ghi lại và phục hồi trạng thái (nếu TextView không gán ID thì chức năng này của TextView không thi hành, bạn có thể thử xóa bỏ ID để kiểm tra)
Giải thích cơ chơ lưu trữ và phục hồi trong Activity
Trước tiên, bạn nên biết các View hiện thị trong Layout như TextView, Button ... đều có phương thức là onSaveInstanceState()onRestoreInstanceState(Parcelable state), mục đích 2 của phương thước đó là lưu lại trạng thái và phục hồi trạng thái của View nếu cần thiết. Tùy thược vào đối tượng kế thừa overrided lại 2 phương thức này thế nào mà dữ liệu ghi / phục hồi có khác. Ví dụ TextView quá tải để lưu lại nội dung dòng chũ, đến EditText quá tải để lưu lại nội dung dòng chữ và đoạn text đang lựa chọn. Sau này bạn cũng tự xây dựng View và quá tải 2 phương thức này nếu muốn tùy ý muốn lưu lại và phục hồi trạng thái theo cách của bạn.
Điều thứ 2, trong các Activity cũng có hai phương thức là onSaveInstanceState(Bundle outState)onRestoreInstanceState(Bundle savedInstanceState) được gọi để lưu lại và phục hồi trạng thái của Activity.
Trong vòng đời của Activity thì onPause gọi onSaveInstanceStateonResume gọi onRestoreInstanceState
Bạn có thể quá tải hai phương thức này trong Activity để lưu lại và phục hồi theo tùy chọn của bạn. Nên nhớ là khi onSaveInstanceState(Bundle outState) của Activity gọi thì nó lưu dữ liệu riêng của Activity đồng thời quét qua các View và yêu cầu các View đó chạy onSaveInstanceState() của riêng View đó. Tương tự khi onRestoreInstanceState(Bundle savedInstanceState) của Activity được gọi, ngoài phục hồi dữ liệu riêng, nó cũng quét qua từng View và yêu cầu chúng chạy onRestoreInstanceState(Parcelable state)
Ví dụ lưu và phục hồi Activity
Như trên đã nói onRestoreInstanceState/onSaveInstanceState gọi tự động ở thời điểm cụ thể của vòng đời Activity. EditText mặc địch nó chỉ lưu và phục hồi nội dung text, trạng thái đang chọn text, nên nếu bạn thay đổi màu nền TextView thì màu nền này sẽ không phục hồi lại vì hàm mặc định không lưu và phục hồi dữ liệu này. Giờ ta sẽ quá tải onRestoreInstanceState/onSaveInstanceState với mục địch có phục hồi thông số này.
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //Lấy màu của mytext và lưu lại
    ColorDrawable colorDrawable =
            (ColorDrawable)findViewById(R.id.mytext).getBackground();
    int background_mytext = colorDrawable.getColor();
    outState.putInt("background_mytext", background_mytext);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    //Phục hồi màu của mytext
    int background_mytext = savedInstanceState.getInt("background_mytext");
    findViewById(R.id.mytext).setBackgroundColor(background_mytext);


}
Bạn cũng có thể sử mã onCreate thành như sau, để khi bấm vào FAB thì màu EditText thành màu đỏ
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    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();
            findViewById(R.id.mytext).setBackgroundColor(Color.RED);
        }
    });
}
Giờ bạn chạy ứng dụng, trạng thái của EditText đã lưu và phục hồi không chỉ nội dung chữ mà cả màu nền thiết lập cho nó.
Sử dụng Bundle
Trong ví dụ trên, đã sử dụng đến đối tượng lớp Bundle để lưu và phục hồi trạng thái. Mục đích của Bundle để truyền dữ liệu giữa các Activity, nó được sử dụng cơ bản bởi Intent
Tạo một đối tượng Bundle
Bundle bundle = new Bundle();
Dữ liệu được đưa vào Bundle khá giống với Map, tức là dữ liệu nào đó sẽ được lưu theo cặp keygiá trị. Dữ liệu được đọc căn cứ vào key lưu trữ đó
Ví dụ:
bundle.putInt("songuyen", 1000); //key là songuyen

int giatri = bundle.getInt("songuyen"); //Đọc
Các phương thức để lưu dữ liệu như: putInt, putBoolean, putString, putFloat ... tương ứng có các phương thức đọc dữ liệu như getInt, getBoolean, getString, getFloat ...

Không có nhận xét nào :

Đăng nhận xét

Danh mục Gửi liên hệ

Loading...