07 พฤศจิกายน 2557

[Android Code] Let's Fragment - รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 1]


        ในบทความก่อนหน้านี้เจ้าของบล็อกได้พูดถึงการเรียกใช้ Fragment แบบพื้นฐานไปแล้ว เพื่อให้เข้าใจขั้นตอนในการสร้าง Fragment ขึ้นมาซักตัวหนึ่ง แต่ทว่าในการใช้งานจริงมันไม่ได้ง่ายดายแบบนั้น เพราะว่าส่วนใหญ่จะเรียกแสดง Fragment ผ่านโค๊ดคำสั่ง (Programmatically) ไม่ได้สร้างเตรียมไว้ใน Layout XML เพียงอย่างเดียว

        ดังนั้นคราวนี้เจ้าของบล็อกก็จะมาพูดถึง FragmentTransaction ที่จะช่วยให้ผู้ที่หลงเข้ามาอ่านสามารถกำหนดและจัดการ Fragment ที่จะแสดงได้ตามต้องการ

        ในบทความนี้เจ้าของบล็อกจะเริ่มใช้ android.support.v4.app.Fragment แล้วนะครับ รวมไปถึงบทความต่อๆไปด้วย เนื่องจากในการใช้งานจริงจะนิยมใช้ตัวนี้กันมากกว่า

        ก่อนจะเริ่มต้นบทความนี้ อยากจะให้เข้าใจกันก่อนว่า การนำ Fragment มาแสดงบน Layout XML นั้น ไม่จำเป็นต้องใช้ <fragment> เสมอไป เพราะในความเป็นจริง Fragment นั้นสามารถดึงมาแสดงบน ViewGroup ตัวไหนๆก็ได้ ไม่ว่าจะเป็น LinearLayout, RelativeLayout หรือ FrameLayout ดังนั้นสิ่งที่อยากจะให้จำเอาไว้เลยก็คือ

        Fragment สามารถเรียกมาแสดงบน ViewGroup ได้



        แต่ทว่า Fragment นั้นไม่ใช่ View เหมือนอย่าง Button หรือ TextView ดังนั้นจะใช้คำสั่ง addView ก็ทำไม่ได้ จึงต้องใช้ Class ตัวหนึ่งเข้ามาช่วยแทน ซึ่งมีชื่อเรียกว่า FragmentTransaction


FragmentTransation นั้นคืออะไร?

        FragmentTransaction เป็น Class ที่เอาไว้จัดการกับการแสดงผลของ Fragment ไม่ว่าจะเป็นการเพิ่ม Fragment เข้าไปใน ViewGroup (Add) เอา Fragment ตัวใหม่ไปแทนที่ตัวเก่า (Replace) หรือลบ Fragment นั้นๆออกจาก ViewGroup (Remove)

        เพราะว่าในแต่ละ Activity ผู้ที่หลงเข้ามาอ่านสามารถสับเปลี่ยน Fragment ได้ตามต้องการ ซึ่งจะช่วยให้การทำงานของแอพฯนั้นยืดหยุ่นมากขึ้น สามารถทำ Fragment เป็น Stack ซ้อนกันได้ (เหมือนกดเปิด Activity ไปเรื่อยๆแล้วมัน Stack เป็นชั้นๆ)


ลองใช้งาน FragmentTransaction กัน

        ในการเรียกใช้งาน FragmentTransaction จะต้องเรียกจาก Activity ทุกครั้ง ดังนี้

import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();

        จะเห็นว่า FragmentTransaction จะต้องเรียกขึ้นมาจาก FragmentManager เสมอ ในขณะที่ FragmentManager ก็เรียกขึ้นมาจาก Activity อีกทีด้วยคำสั่ง getSupportFragmentManager

         และพิมพ์แบบสั้นๆแบบนี้ก็ได้

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        เพิ่มเติม - เนื่องจากใช้ android.support.v4.app.Fragment ดังนั้นจะต้องใช้คำสั่ง getSupportFragmentManager และ Activity จะใช้เป็น FragmentActivity


        เมื่อได้ FragmentTransaction มาแล้ว การจับ Fragment มายัดไว้ใน ViewGroup จะใช้คำสั่งดังนี้

transaction.add(viewGroupId, fragment);
     
        และเมื่อกำหนดเสร็จแล้วจะต้อง Commit เพื่อให้ FragmentTransaction ทำงานตามที่กำหนดไว้

transaction.commit();

        สรุปคำสั่งได้ประมาณนี้

import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(viewGroupId, fragment);
transaction.commit();


มาลองเรียกใช้งานจริงๆกันเถอะ!!

        ก่อนอื่นก็ให้สร้าง Activity และ Fragment ขึ้นมาอย่างละตัว โดยที่ Activity จะใช้เป็น FragmentActivity ส่วน Fragment จะใช้เป็น android.support.v4.app.Fragment


        • Activity ชื่อ MainActivity.java มี Layout เป็น activity_main.xml
        • Fragment ชื่อ OneFragment.java มี Layout เป็น fragment_one.xml


MainActivity.java
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
}

        สำหรับ activity_main ก็จะใช้ RelativeLayout ที่มีสีพื้นหลังเป็นสีเทาบางๆ แล้ววาง LinearLayout โดยให้มี Margin 30 dp และพื้นหลังเป็นสีขาว และมีชื่อ ID ว่า fragment_container

activity_main.xml
<RelativeLayout 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:background="#f2f2f2"
    tools:context="${relativePackage}.${activityClass}" >

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="30dp"
        android:background="#ffffff"
        android:orientation="vertical" />

</RelativeLayout>



OneFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class OneFragment extends Fragment {
    
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_one, container, false);
        
        return rootView;
    }
}

        ส่วน Layout ของ OneFragment จะใช้สีพื้นหลังเป็นสีเทาเข้มขึ้นเล็กน้อยและก็จะมี TextView แสดงคำว่า Fragment One และมีปุ่ม Close ที่ตั้งชื่อ ID ไว้ว่า btn_close

fragment_one.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e6e6e6" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Fragment One" />

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="Close" />

</RelativeLayout>


        เมื่อเตรียม Activity และ Fragment ไว้พร้อมแล้ว ก็มาดูที่ MainActivity กันก่อน เมื่อกด Run ดูเล่นๆก็จะยังไม่มีอะไร เพราะยังไม่ได้กำหนดโค๊ดเพิ่มเข้าไป ดังนั้นเจ้าของบล็อกจะเอาโค๊ดที่เกริ่นไว้ตอนแรกมาลองใช้งานกันดู

MainActivity.java
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.View;

public class MainActivity extends FragmentActivity {
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        OneFragment fragment = new OneFragment();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.add(R.id.fragment_container, fragment);
        transaction.commit();
    }
}

        จะเห็นว่าตอนใช้คำสั่ง add ที่จะต้องกำหนด ViewGroup ที่จะให้แสดง Fragment จะใช้วิธีระบุแค่ ID Name ของ ViewGroup ตัวนั้นๆ ซึ่งเจ้าของบล็อกได้กำหนดไว้แล้วว่า @+id/fragment_container ในหน้า Layout ส่วน Fragment ก็จะต้องสร้าง Instance ของ OneFragment ขึ้นมาแล้วนำไปกำหนดไว้ในคำสั่ง add

        เมื่อเสร็จแล้วให้ลองรันทดสอบดูก็จะเห็นว่า Fragment ที่ได้สร้างไว้ ถูกนำมาแสดงไว้ใน LinearLayout แล้ว (ถ้ายังไม่ได้ก็ให้ทบทวนใหม่ตั้งแต่แรกอีกครั้ง)


        จะเห็นว่าที่เจ้าของบล็อกกำหนดสีไว้ก็เพราะว่าผู้ที่หลงเข้ามาอ่านจะได้สังเกตขอบเขตของ View ได้ง่ายขึ้น เพื่อที่จะได้รู้ว่าพื้นที่ตรงไหนเป็นของ Fragment และตรงไหนเป็นของ Activity


        ทีนี้จะเห็นว่าเจ้าของบล็อกเตรียมปุ่ม Close ไว้ใน Fragment ไว้แล้ว แต่ทว่ายังไม่ได้ทำอะไรกับมัน ดังนั้นให้เปิดไฟล์ OneFragment ขึ้นมาเพื่อเพิ่มคำสั่ง

        อย่างที่บอกในตอนแรกว่า FragmentTransaction จะจัดการการแสดงผล Fragment ทั้งหมด ดังนั้นจึงสามารถสั่งให้เอา Fragment ที่แสดงอยู่ออกไปได้เช่นกัน โดยใช้คำสั่ง

import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(fragment);
transaction.commit();
        สำหรับ fragment ก็คือ Fragment ที่ต้องการลบทิ้ง

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

        ก่อนอื่นขอพูดถึง FragmentManager ก่อน ถ้ายังจำกันได้เจ้าของบล็อกบอกไว้ว่าถ้า Fragment ที่ใช้เป็น android.app.Fragment จะต้องใช้คำสั่ง getFragmentManager และถ้าใช้เป็น android.support.v4.app.Fragment จะต้องใช้คำสั่ง getSupportFragmentManager


        เพราะคำสั่งทั้งสองนี้จะ Return Fragment Class คนละตัวกัน ดังนั้นบทความนี้ที่ใช้ android.support.v4.app.Fragment เวลาที่เรียก FragmentManager จะต้องใช้คำสั่ง getSupportFragmentManager แทน

        แต่ทว่านี่เป็นแค่คำสั่งเฉพาะบน Activity เท่านั้น เวลาที่เรียก FragmentManager ใน Fragment ก็จะใช้คำสั่ง getFragmentManager เลย ไม่ว่าจะเป็น android.app.Fragment หรือ android.support.v4.app.Fragment ก็ตาม


        ทั้งนี้ก็เพราะว่า Fragment ทั้งคู่จะอิง FragmentManager ของตัวเองอยู่แล้ว ถ้าเรียกจาก android.app.Fragment ก็จะได้เป็น android.app.FragmentManager และถ้าเรียกจาก android.support.v4.app.Fragment ก็จะได้เป็น android.support.v4.app.FragmentManager โดยทันที จึงไม่จำเป็นต้องมีคำสั่งสองแบบ เพราะ FragmentManager ไม่สามารถเรียกใช้งานสลับกันได้อยู่แล้ว


        ดังนั้นคำสั่ง Remove Fragment เมื่อเรียกใช้ใน Fragment ก็จะเปลี่ยนแปลงเล็กน้อย

FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(fragment);
transaction.commit();

        อ้าว แล้ว fragment ล่ะ? จะไปดึง Instance จากที่ไหน?

        เนื่องจากเราเรียกใช้คำสั่งนี้ใน Fragment ดังนั้นจึงสามารถใช้ this หรือ OneFragment.this เพื่อกำหนดว่าให้ลบตัวเองออกได้เลย

FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(OneFragment.this);
transaction.commit();
        หรือจะพิมพ์แบบสั้นๆ
getFragmentManager().beginTransaction().remove(OneFragment.this).commit();


        ถ้างั้นคำสั่งใน OneFragment ก็จะได้ออกมาเป็นดังนี้

OneFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class OneFragment extends Fragment {
    
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_one, container, false);
        
        getFragmentManager().beginTransaction().remove(OneFragment.this).commit();
        
        return rootView;
    }
}

        เฮ้ย!! เดี๋ยวๆๆๆ มันไม่ใช่แล้ว!!

        เพราะว่าเจ้าของบล็อกไปสั่ง Remove ในทันทีที่ onCreateView ทำงาน ผลก็คือ เมื่อ Fragment ตัวนี้ถูกสร้างขึ้นมาปุ๊ป onCreateView ก็จะทำงานแล้วสั่งลบ Fragment ตัวนี้ปั๊ป ดังนั้นจึงเป็นที่มาว่าทำไมเจ้าของบล็อกต้องสร้าง Button ขึ้นมา

OneFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class OneFragment extends Fragment {
    
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_one, container, false);
                
        Button btn_close = (Button)rootView.findViewById(R.id.btn_close);
        btn_close.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                getFragmentManager().beginTransaction().remove(OneFragment.this).commit();
            }
        });
        
        return rootView;
    }
}

        เมื่อเปิดแอพฯ OneFragment ก็จะแสดงขึ้นมา และจะถูกลบออกไปเมื่อผู้ใช้กดปุ่ม Close ที่อยู่ใน Fragment


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


        กลับมาเปิดที่ไฟล์ activity_main.xml อีกครั้ง เจ้าของบล็อกจะให้เพิ่มปุ่มไว้ที่ข้างล่างสุด โดยวาง LinearLayout แบบ Horizontal ให้ ID เป็น @+id/layout_menu แล้ววาง Button ไว้ในนั้น โดยให้ ID เป็น btn_one

activity_main.xml
<RelativeLayout 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:background="#f2f2f2"
    tools:context="${relativePackage}.${activityClass}" >

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/layoutMenu"
        android:layout_margin="30dp"
        android:background="#ffffff"
        android:orientation="vertical" />

    <LinearLayout
        android:id="@+id/layout_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:gravity="center_horizontal" >

        <Button
            android:id="@+id/btn_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="One" />
    </LinearLayout>

</RelativeLayout>


        น่าจะเดากันต่อได้แล้วเนอะว่าสร้าง Button ไว้ทำไม

MainActivity.java
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends FragmentActivity {
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn_one = (Button)findViewById(R.id.btn_one);
        btn_one.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                OneFragment oneFragment = new OneFragment();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.add(R.id.fragment_container, oneFragment);
                transaction.commit();
            }
        });
    }
}
        เจ้าของบล็อกจะย้ายคำสั่ง Add Fragment มาไว้ใน Button นั่นเอง เพื่อที่ว่าจะได้กด Button แล้วแสดง Fragment ในแอพฯ

        คราวนี้ก็ลองรันทดสอบดูก็จะพบว่าเมื่อกดปุ่ม One ก็จะทำให้ OneFragment แสดงบนหน้าจอ



        ทีนี้ให้ทดสอบตามนี้ดู

        • กดปุ่ม One ให้ OneFragment แสดงขึ้นมา แล้วกดปุ่ม Close เพื่อปิด

        • กดปุ่ม One สองครั้ง แล้วลองกดปุ่ม Close ดู


        เมื่อลองกดเปิด OneFragment ขึ้นมาแล้วกด ปิด ก็จะกลับเข้าสู่หน้าขาวๆที่ไม่มีอะไรเหมือนเดิม แต่เมื่อกดเปิด OneFragment สองครั้ง และเมื่อกดปิดจะพบว่า OneFragment นั้นยังไม่หายไป ต้องกดอีกครั้งถึงจะหายไป (ลองกดเปิดรัวๆดูก็ได้)

        ที่เป็นแบบนี้ก็เพราะว่าเจ้าของบล็อกใช้คำสั่ง add ใน FragmentTransaction นั่นเอง จึงทำให้ Fragment ซ้อนกันเป็น Stack


        ทีนี้จะแก้ปัญหาอย่างไรล่ะ?

        ก่อนจะเฉลยให้ได้อ่านกัน อยากจะให้สร้าง Fragment เพิ่มขึ้นมาอีก 2 ตัว โดยกำหนดให้ชื่อว่า TwoFragment และ ThreeFragment (ตั้งชื่อแบบโง่ๆ ฮ่าๆ) ส่วน Layout ก็ 2 ตัวเช่นกัน ชื่อว่า fragment_two และ fragment_three

        โดยให้ TwoFragment และ ThreeFragment มีคำสั่งกด Button เพื่อปิด เหมือนกับ OneFragment


TwoFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;

public class TwoFragment extends Fragment {
    
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_two, container, false);

        Button btn_close = (Button)rootView.findViewById(R.id.btn_close);
        btn_close.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                getFragmentManager().beginTransaction().remove(TwoFragment.this).commit();
            }
        });
        
        return rootView;
    }
}

fragment_two.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e6e6e6" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Fragment Two" />

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="Close" />

</RelativeLayout>


ThreeFragment.java
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ThreeFragment extends Fragment {
    
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_three, container, false);

        Button btn_close = (Button)rootView.findViewById(R.id.btn_close);
        btn_close.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                getFragmentManager().beginTransaction().remove(ThreeFragment.this).commit();
            }
        });
        
        return rootView;
    }
}

fragment_three.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e6e6e6" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Fragment Three" />

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="Close" />

</RelativeLayout>


        จึงสรุปได้ว่าตอนนี้มี Activity 1 ตัว, Fragment 3 ตัว และ Layout 4 ตัว

        • MainActivity : activity_main
        • OneFragment : fragment_one
        • TwoFragment : fragment_two
        • ThreeFragment : fragment_three


        กลับมาที่ activity_main อีกครั้ง ให้เพิ่ม Button เข้าไปสำหรับ TwoFragment และ ThreeFragment

activity_main.xml
<RelativeLayout 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:background="#f2f2f2"
    tools:context="${relativePackage}.${activityClass}" >

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/layout_menu"
        android:layout_margin="30dp"
        android:background="#ffffff"
        android:orientation="vertical" />

    <LinearLayout
        android:id="@+id/layout_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:gravity="center_horizontal" >

        <Button
            android:id="@+id/btn_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="One" />

        <Button
            android:id="@+id/btn_two"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Two" />

        <Button
            android:id="@+id/btn_three"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Three" />
    </LinearLayout>

</RelativeLayout>


        จากนั้นก็เพิ่มคำสั่งสำหรับ Button ทั้งสองตัวเข้าไปใน MainActivity ดังนี้

MainActivity.java
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends FragmentActivity {
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn_one = (Button)findViewById(R.id.btn_one);
        btn_one.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                OneFragment oneFragment = new OneFragment();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.add(R.id.fragment_container, oneFragment);
                transaction.commit();
            }
        });
        
        Button btn_two = (Button)findViewById(R.id.btn_two);
        btn_two.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                TwoFragment twoFragment = new TwoFragment();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.add(R.id.fragment_container, twoFragment);
                transaction.commit();
            }
        });

        Button btn_three = (Button)findViewById(R.id.btn_three);
        btn_three.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                ThreeFragment threeFragment = new ThreeFragment();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.add(R.id.fragment_container, threeFragment);
                transaction.commit();
            }
        });
    }
}

        จะเห็นว่าในแต่ละ Event ของการกด Button เจ้าของบล็อกจะให้ Add Fragment ของแต่ละตัวเข้าไปใน fragment_container

        จากนั้นก็ให้ลองรันทดสอบดูอีกครั้ง ก็จะเห็นว่าเมื่อกดปุ่มใด ก็จะเป็นการแสดง Fragment นั้นๆ โดยที่ Fragment ของเก่าที่เคยเปิดไว้ก็จะถูกซ้อนข้างหลังไปเรื่อยๆ โดยจะขึ้นอยู่กับลำดับการกด




        กดยังไงก็ไม่รู้สึกว่ามันเปลี่ยนแปลงเลย = =

        แต่พอกดปุ่ม Close ก็จะเริ่มเห็นว่าที่เคย Add ไว้มันซ้อนอยู่ข้างหลังนั่นเอง




        จึงอยากให้จำการทำงานแบบนี้ไว้นะครับ เพราะว่านี่คือรูปแบบการทำงานของคำสั่ง Add


        และถ้าลองกดปุ่ม Back ก็จะพบว่า Activity ถูกปิดลงในทันทีโดยไม่สนว่าจะเปิด Fragment ไว้อยู่ หรือเปิดซ้อนไว้มากขนาดไหน


        ซึ่งอาจจะดูไม่ค่อย Make Sense ซักเท่าไร เพราะบางทีแอพฯอาจจะมีการเปิด Fragment ซ้อนกันอยู่แต่ดันกด Back ทีเดียวแล้วปิดทั้งหมด มันก็รู้สึกแปลกๆใช่มั้ยล่ะ? ดังนั้นจึงขอแนะนำให้รู้จักกับ BackStack กันก่อน
     

BackStack คืออะไร?

         นึกภาพง่ายๆว่าผู้ที่หลงเข้ามาอ่านสร้าง Activity ขึ้นมา 3 ตัวด้วยกัน ชื่อว่า A, B และ C โดยให้กดหน้า A ไปหน้า B และกดหน้า B ไปหน้า C


        ในการเปลี่ยนไปยังแต่ละหน้าก็จะใช้คำสั่ง startActivity ที่คุ้นเคยกันดี และเมื่อเปิดไปถึงหน้า C ก็จะเกิด BackStack ขึ้นมาดังนี้


        ซึ่ง BackStack ก็คือการเก็บบางอย่างไว้เป็น Stack เพื่อให้มีการเคลียร์ทิ้งไปทีละ Stack เมื่อกดปุ่ม Back ดังนั้นในตัวอย่างนี้เมื่อกด Back ที่หน้า C ก็จะกลับไปที่หน้า B และเมื่อกด Back อีกครั้งก็จะกลับไปที่หน้า A และเมื่อกดอีกทีก็จะเป็นการปิดแอพฯนั่นเอง

        แต่ทว่า BackStack ณ ตอนนี้ไม่มี Fragment อยู่ด้วย ทำให้เวลากด Back ก็จะเคลียร์ Activity ทิ้งในทันทีโดยไม่สน Fragment

        เพิ่ม Fragment เข้าไปใน BackStack ได้เหมือนกันนะเออ

        ใน FragmentTransaction จะมีคำสั่ง addToBackStack อยู่ด้วย เพื่อที่จะเพิ่ม Fragment ที่สร้างขึ้น ณ ตอนนั้นๆเข้าไปใน BackStack จึงทำให้สามารถกด Back เพื่อปิด Fragment นั้นๆได้ด้วย

OneFragment oneFragment = new OneFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, oneFragment);
transaction.addToBackStack(null);
transaction.commit();

        คำสั่งนี้จะทำให้ FragmentTransaction เก็บค่าที่กำหนดไว้ใน BackStack ด้วย และจะเห็นว่าเจ้าของบล็อกใส่ Parameter ว่า Null ลงไป ซึ่งจริงๆแล้วสามารถใช้ String Name ได้ตามใจชอบ เปรียบเสมือนเป็น Tag Name นั่นเอง แต่ส่วนมากไม่ค่อยใช้ประโยชน์จากตรงนี้ซักเท่าไร จึงนิยมใส่เป็น Null กัน

        เพียงเท่านี้ เมื่อ OneFragment เพิ่มเข้ามาใน Activity ก็จะถูกเพิ่มเข้ามาใน BackStack ด้วย จึงทำให้ BackStack ณ ตอนนี้มี MainActivity อยู่ชั้นล่างสุด และ OneFragment อยู่ชั้นบน


        ให้ลองรันทดสอบดูแล้วกดเพิ่ม OneFragment เข้ามาหนึ่งครั้ง จากนั้นก็ให้ลองกดปุ่ม Back ดู ก็จะพบว่าแอพฯไม่ได้ปิดในทันที แต่จะเป็นการปิด OneFragment ก่อน และเมื่อกดอีกครั้งจึงจะเป็นการออกจากแอพ




        ดังนั้นถ้าอยากให้ Fragment อยู่ใน BackStack ก็จะต้องใช้คำสั่ง addToBackStack ด้วยทุกครั้ง

        ทีนี้กลับมาปัญหาเก่าที่ว่ากดปุ่มเพิ่ม Fragment แต่มันดันไปซ้อนใต้ Fragment ของเก่า ไม่ยอมทับข้างบน ซึ่งการใช้ Replace นี่แหละจะมาตอบโจทย์ปัญหานี้ได้ แต่จะขอแยกเป็นบทความตอนที่ 2 นะคร้าบบบบบ เพราะบทความนี้เริ่มจะยาวมากเกินไปแล้วววว ตามไปอ่านกันได้ที่ Let's Fragment - รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 2]



บทความที่เกี่ยวข้อง

        • Fragment Principle - มารู้จักกับ Fragment กันเถอะ~
        • Let's Fragment - เริ่มต้นง่ายๆกับ Fragment แบบพื้นฐาน
        • Let's Fragment - รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 2]
        • Let's Fragment - มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 1]
        • Let's Fragment - มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 2]




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium