การใช้ Intent เพื่อเปิดหน้า Activity และส่งข้อมูลระหว่าง Activity

การใช้ Intent เพื่อเปิดหน้า Activity และส่งข้อมูลระหว่าง Activity Cover Image

บทความนี้ขอนำเสนอบทความพื้นฐานสำหรับมือใหม่เลยครับ เกี่ยบกับเรื่อง Intent และการเปิด Activity หนึ่งไปยังอีก Activity หนึ่ง รวมถึงการส่งข้อมูลระหว่าง Activity ด้วยครับ

จริงๆ ตั้งใจว่าจะไม่เขียนพวกพื้นฐานเท่าไหร่ เพราะหาอ่านได้ตามเว็บอื่นๆ หรือตามหนังสือก็มีอยู่เยอะแยะมากมาย แต่มาคิดอีกที เราอาจจะคิดว่ามันเยอะแยะ แต่บางคนละไม่คิดแบบเรา เค้าอาจจะหาไม่เจอ หรือยังไม่เข้าใจ ก็ต้องพยายามอ่านหลายๆเว็บ ผมก็เลยตัดสินใจ จะทำบทความครอบคลุมพื้นฐานด้วยละกัน อย่างน้อยก็เผื่อมีบางคนมาอ่าน แล้วได้ประโยชน์ ไม่บ้างก็น้อยแหละ :D

มาเข้าเรื่อง Intent และ Activity กันเลยดีกว่า

TLDR

  1. สร้าง Activity ขึ้นใหม่ สมมติชื่อ AboutActivity
  2. เพิ่ม activity name ใน AndroidManifest.xml ให้รู้จัก AboutActivity
  3. ใช้ startActivity(intent); เพื่อเปิดหน้าใหม่
  4. ใช้ finish() เพื่อกลับมา Activity เดิม

การเปิด Activity ไปอีก Activity หนึ่ง

สำหรับบางคน ที่เริ่มเขียนใหม่ อาจจะใช้แค่ Activity เดียวในแอพ แต่ว่าอยากจะให้กดปุ่ม แล้วเปิดอีก Activity หนึ่ง หรือเปลี่ยน Activity จะทำยังไง?

เริ่มต้นสร้างโปรเจ็คใหม่เลยครับ สมมติว่าทุกคน สามารถสร้างโปรเจ็คใหม่เป็นนะครับ ใครไม่เป็นอ่านบทความนี้ สร้างโปรเจ็คด้วย Android Studio

ที่นี้เราจะใช้ ไฟล์ 2 ไฟล์คือ

  • MainActivity.java
  • AboutActivity.java

และเลเอาท์ 2 ไฟล์ คือ

  • activity_main.xml
  • activity_about.xml

เลเอาท์สร้างไว้ที่ /res/layout/ ส่วน Activity สร้างไว้ที่เดียวกับคลาส MainActivity เลย

Layout

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/button_about"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="About"
        android:paddingTop="24dp"
        android:paddingBottom="24dp"
        android:paddingLeft="48dp"
        android:paddingRight="48dp"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"/>

    <ImageView
        android:layout_width="128dp"
        android:layout_height="128dp"
        android:id="@+id/imageView"
        android:src="@drawable/ic_launcher"
        android:layout_above="@+id/button_back"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

activity_about.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:text="AboutActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="36sp"
        android:layout_marginTop="32dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"/>

    <Button
        android:id="@+id/button_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Back"
        android:paddingTop="24dp"
        android:paddingBottom="24dp"
        android:paddingLeft="48dp"
        android:paddingRight="48dp"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

ทีนี้เป้าหมายเรา คือ กดปุ่ม Button จาก MainActivity แล้วให้ทำการเปิด AboutActivity ขึ้นมา จากนั้นหน้า AboutActivity เมื่อกด Back ก็จะกลับมาหน้า MainActivity อีกครั้ง

ง่ายมากครับ แค่เพิ่มโค๊ด สองบรรทัดนี้ลงไป ใน onClickListener คือเมื่อทำการกดปุ่มก็จะเปิด AboutActivity ขึ้นมา นั่นเอง

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

โค๊ดมันมีแค่นี้แหละ คือ เราจะสร้างออปเจ็ค intent ขึ้นมา โดยรับ parameter 2 ตัว ตัวแรกคือ Context ภายใน Activityแรก และ parameter ตัวที่สองคือ Activity ที่เราต้องการเปิด

โค๊ดเต็มๆของ MainActivity คือ

package com.devahoy.sample.intent;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {

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

        Button buttonAbout = (Button) findViewById(R.id.button_about);

        buttonAbout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, AboutActivity.class);
                startActivity(intent);
            }
        });
    }
}

ส่วนฝั่ง AboutActivity เมื่อกดปุ่ม back ก็จะกลับมา Activity จะใช้ finish() ครับ หมายถึงการปิด Activity ครับ แต่ถ้าเราไปใช้แบบ ด้านบน คือเปลี่ยนเป็นแบบนี้

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

มันก็จะเปิดหน้า MainActivity ได้ครับ แต่ว่ามันจะมีหน้าซ้อนกัน 2 หน้าครับ (โดยปกติใน Android Activity มันจะถูกเปิดซ้อนกันไปเรื่อยๆครับ) ไม่เชื่อคุณลองรันโปรแกรม แล้วสั่งเปิดหน้าสอง แล้วกด back หลายๆครั้งดูครับ Activity มันจะซ้อนกันตามจำนวนที่คุณกดเลย

คลาส AboutActivity.java

package com.devahoy.sample.intent;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;

public class AboutActivity extends ActionBarActivity {

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

        Button buttonBack = (Button) findViewById(R.id.button_back);

        buttonBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

แต่ถ้าหากอยากจะใช้แบบ ส่ง Intent ก็ทำได้ครับ โดย MainActivity ต้อง เรียก finish() ด้วยครับ เช่น

MainActivity.java

Intent intent = new Intent(this, AboutActivity.class);
startActivity(intent);
finish();

AboutActivity.java

Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();

แบบนี้ก็จะไม่เกิด Activity ซ้ำกันแล้ว แต่ว่าในกรณีที่เรามี 2 Activity แค่กดปิด แล้วกลับไปหน้าหลัก ดีกว่า ที่จะต้องมาสร้างหน้าหลักใหม่ อีก จริงมั้ยครับ :D

สุดท้ายที่ไฟล์ AndroidManifest.xml เราต้องไปเพิ่มในส่วนของ Android Name ให้มันรู้จัก Activity ที่เราเพิ่มมา

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

ปัญหาการใช้ this

จะเห็นว่า บางทีพวกนี้จะเป็นปัญหาสำหรับมือใหม่เลย คือใส่ this แต่ไม่รู้ว่ามันอยู่ใน block, scope ไหน ก็ยังไปใส่ this เช่น

btnSubmit.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, AboutActivity.class);
        startActivity(intent);
    }
});

ด้านบน เรียกเปิด Activity ใหม่ จากภายใน Anonymous Inner Class ของ OnClickListener ซึ่ง this มันก็จะไปมองเป็น OnClickListener ไม่ได้มองเป็น MainActivity วิธีแก้ก็ต้องเปลี่ยนเป็นแบบนี้


btnSubmit.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, AboutActivity.class);
        //หรือแบบนี้
        //Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
        startActivity(intent);
    }
});

หรือทางแก้อีกแบบ ก็ประกาศตัวแปรหนึ่งเป็น global ไปเลย เช่น

public class YourClass extends Activity {
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mContext = getApplicationContext();
    }
}

ทีนี้ทุกอย่างที่ต้องการ ApplicationContext ก็แค่ส่ง mContext ไปเท่านั้น

อีกปัญหานึงที่เจอบ่อยๆ คือ จะเปิด Activity ใหม่ จากหน้า Fragment แต่ดันไปใช้ this หรือ MyFragment.this มันก็จะไปได้อย่างไร? เนื่องจาก context มันอยู่ใน Activity ฉะนั้นก็ต้องเรียกแบบนี้

Intent intent = new Intent(getActivity(), AboutActivity.class);
startActivity(intent);

การส่งข้อมูลข้าม Activity

วิธีการส่งข้อมูลข้าม Activity กรณีเรามีข้อมูลอยู่ MainActivity แต่อยากให้มันไปโชว์ที่หน้า DetailActivity เราจะทำอย่างไร?

วิธีการ ก็คือใช้ Intent เหมือนเดิมครับ โดยทาง Intent จะมีเมธอด putExtra(key, value) มาให้เราสำหรับส่งค่าไปครับ parameter แรกจะเป็นชื่อเรียก ส่วน parameter สอง จะเป็นชนิดของตัวแปร จะเป็น int, String, boolean, float ก็ได้ครับ ดังเช่นตัวอย่าง

ในคลาส MainActivity เรา ทำการเปิด AboutActivity โดยส่งค่า ไปด้วย

Intent intent = new Intent(this, AboutActivity.class);
intent.putExtra("name", "devahoy");
intent.putExtra("isSmart", true);
intent.putExtra("star", 5);
startActivity(intent);

จากนั้นที่ AboutActivity ก็รับค่าที่ส่งมา แบบนี้ เปิด onCreate()

Bundle bundle = getIntent().getExtras();
if (bundle != null) {
    String name = bundle.getString("name");
    int star = bundle.getInt("star");
    boolean isSmart = bundle.getBoolean("smart");

    String result = String.format("Name is %s, star : %s, smart : %s",
            name, star, isSmart);
    Toast.makeText(this, "Name : " + result, Toast.LENGTH_SHORT).show();
}

เราจะทำการเช็คก่อน ว่ามีการส่งค่ามาจริงไหม ถ้ามีจริง ก็ใช้ Bundle.getXXX เช่นจะรับค่า String ก็ใช้ getString(key) ข้างในก็เป็น key ตัวเดียวกับที่่ไว้ก่อนส่งมาจาก MainActivity

สุดท้าย ก็โชว์ Toast แสดงผลลัพธ์ที่ส่งมา มีแค่นี้แหละครับ ลองนำไปประยุกต์ใช้กันดูครับ

Chai

Chai Phonbopit : Developer แห่งหนึ่ง • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Node.js, JavaScript และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด