23 มกราคม 2557

[Android Code] สร้าง Activity หลายตัว แต่แยก Package กันได้หรือไม่?


        อันนี้เป็นคำถามที่ผู้ที่หลงเข้ามาอ่านได้ถามเจ้าของบล็อก เจ้าของบล็อกก็เลยเอามาทำเป็นบทความเล่นๆ ไขปัญหาฝึกสมองไปวันๆบ้าง

        โจทย์ปัญหาก็คือสร้าง Activity มากกว่าหนึ่ง Activity โดยให้ Intent ไปไปอีก Activity หนึ่งได้ โดยมีเงื่อนไขว่า Activity ทั้งสองต้องอยู่ภายใต้ Package คนละชุดกัน

        เผื่อผู้ที่หลงเข้ามาอ่านคนใดยังไม่รู้นะครับ Package ก็หมายถึง Package Name ที่ตั้งตอนสร้างโปรเจคนั่นแหละ


        เริ่มจากสอนวิธีการสร้าง Activity ที่อยู่คนละ Package ไปด้วยเลยละกัน ไหนๆก็พูดถึงเรื่องนี้

        เจ้าของบล็อกขอสร้างโปรเจคขึ้นมาก่อน โดยกำหนดคร่าวๆไว้ดังนี้

                • Application Name : Multiple Package
                • Project Name : MultiplePackage
                • Package Name : app.akexorcist.multiplepackage
             
                • Activity Name : Main
                • Layout Name : main.xml



        สร้าง Package ก่อนเลย โดยคลิกขวาที่โฟลเดอร์ src เลือกที่ New > Package



        เจ้าของบล็อกขอตั้งชื่อ Package ตัวใหม่ที่จะสร้างขึ้นมาว่า app.akexorcist.anotherpackage



        ต่อไปสร้าง Activity ใหม่ขึ้นมาใน Package ที่พึ่งสร้าง โดยคลิกขวาที่ Package ที่พึ่งสร้างแล้วเลือก New > Class



        เจ้าของบล็อกขอตั้งชื่อ Activity นี้ว่า AnotherActivity



        เอาล่ะ ได้ Activity ที่อยู่คนละ Package กันแล้ว



        สำหรับคำสั่งที่อยู่ใน Main.java เจ้าของบล็อกขอแก้ไขให้เหลือแค่นี้ก่อน
package app.akexorcist.multiplepackage; import android.os.Bundle; import android.app.Activity; public class Main extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }



        ทีนี้มาดูที่ AnotherActivity.java บ้าง เจ้าของบล็อก Extends เป็น Activity แล้วประกาศ onCreate ดังภาพเลย คล้ายๆกับ Main.java แต่ต่างกันชื่อ Package ที่ประกาศและชื่อ Activity
package app.akexorcist.anotherpackage; import android.os.Bundle; import android.app.Activity; public class AnotherActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }


        จะเห็นว่า R มีการเออเรอร์ขึ้นมา ทั้งนี้ก็เพราะว่าโปรเจคถูกกำหนดไว้เป็น app.akexorcist.anotherpackage แต่ว่าไฟล์นี้เป็นคนละ Package กัน จึงไม่สามารถเรียกใช้งาน Resource จากการเรียก R โดยตรงได้

        ให้เอาเม้าส์วางไว้บนตัว R เฉยๆ (ไม่ต้องไปกด) รอหน้าต่างขึ้นมาแล้วเลือก Import 'R' (app.akexorcist.multiplepackage) เพื่อที่จะประกาศว่า R ที่จะใช้นี้เป็นของ Package ชื่อ app.akexorcist.multiplepackage นั่นเอง



        เท่านี้ก็ประกาศ R ในไฟล์คนละ Package ได้แล้ว ในภาพยังเห็นเครื่องหมายเออเรอร์ที่หน้าไฟล์ อันนี้เพราะว่าเจ้าของบล็อกไม่ได้กดเซฟ มันเลยยังค้างของเก่าอยู่



        ต่อไปให้สร้างไฟล์ Layout ขึ้นมาสำหรับ AnotherActivity.java เพราะตอนนี้ไปใช้ของ main.xml ซึ่งคงจะไม่เหมาะซักเท่าไรถ้ามาใช้ Layout ตัวเดียวกัน

        คลิกขวาที่โฟลเดอร์ layout แล้วเลือก New > Android XML File



        เจ้าของบล็อกขอตั้งชื่อไฟล์เป็น another_layout.xml โดยเลือก Root Element เป็น Relative Layout



        เมื่อสร้างแล้วก็จัด Layout ให้กับ another_layout.xml กันเสียหน่อย โดยเจ้าของบล็อกวาง Text View ไว้ตรงกลางเลย โดยแสดงข้อความว่า Another Activity เพื่อให้รู้ว่าแสดงหน้านี้อยู่

another_layout.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" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Another Activity" /> </RelativeLayout>


        ตามด้วย main.xml มีการแก้ไขตามความเหมาะสมเสียหน่อย โดยให้ Text View อยู่กลางหน้าจอและแสดงว่า Main 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" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Main Activity" /> </RelativeLayout>


        อ๊ะๆ อย่าลืมแก้ใน AnotherActivity.java ด้วย ว่าให้ใช้ Layout เป็น another_layout.xml

AnotherActivity.java
package app.akexorcist.anotherpackage; import android.app.Activity; import android.os.Bundle; import app.akexorcist.multiplepackage.R; public class AnotherActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.another_layout); } }


        กลับมาที่ Main.java ต้องมีการเพิ่มโค๊ดเสียหน่อย เพราะต้องมีการ Intent จากหน้า Main ไปยังหน้า AnotherActivity โดยจะใช้ onTouchEvent ซึ่งเป็น Listener ที่จะทำงานเมื่อ "ผู้ใช้สัมผัสบน Layout นั้นๆ" ดังนั้นก็หมายความว่าไม่ว่าจะแตะตรงไหนของหน้า Main ก็จะทำงาน ที่ใช้แบบนี้เพราะขี้เกียจสร้าง Button เพื่อกดแล้ว Intent น่ะแหละ ฮาๆ ก็จะใช้ตัวนี้ล่ะ Intent ไปแทน

        โดยจะมีการเช็คว่าการ Touch เป็นแบบใด ถ้าเป็นแบบ ACTION_UP หรือก็คือยกนิ้วขึ้น จึงค่อยทำการ Intent ไป AnotherActivity



        แต่อย่าลืมว่า AnotherActivity ไม่ได้อยู่ใน Package เดียวกันกับ Main จึงทำให้มันมองไม่เห็นคำสั่ง AnotherActivity.class จึงทำให้เกิดเออเรอร์ขึ้นตามภาพ ก็ให้เอาเม้าส์วางบนนั้น แล้วเลือก Import 'AnotherActivity' (app.akexorcist.anotherpackage) เพื่อประกาศว่า AnotherActivity นั้นหมายถึง Activity ที่อยู่ใน app.akexorcist.anotherpackage นั่นเอง



        จะเห็นว่าเออเรอร์ได้หายไปแล้ว แต่เจ้าของบล็อกลืมกดเซฟอีกละ ขึ้นเออเรอที่ชื่อไฟล์เหมือนเดิม...



Main.java
package app.akexorcist.multiplepackage; import android.os.Bundle; import android.view.MotionEvent; import android.app.Activity; import android.content.Intent; import app.akexorcist.anotherpackage.AnotherActivity; public class Main extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP) { Intent i = new Intent(Main.this, AnotherActivity.class); startActivity(i); } return true; } }


        สุดท้ายนี้ สิ่งที่ห้ามลืมเด็ดขาดก็คือ ห้ามลืมประกาศ Activity ใน AndroidManifest.xml เด็ดขาดดดดดดดด

        ขอเปิดเป็นหน้า XML ไปเลยนะ รู้สึกถนัดแบบ XML มากกว่า



        ก็ให้เพิ่ม Activity เข้าไปดังนี้
<activity android:name="app.akexorcist.anotherpackage.AnotherActivity" />

        ได้ออกมาเป็น

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.akexorcist.multiplepackage" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="app.akexorcist.multiplepackage.Main" 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="app.akexorcist.anotherpackage.AnotherActivity" /> </application> </manifest>

        ให้สังเกตให้ดีนะครับตอนประกาศ Activity ใน Android Manifest จะเห็นว่าการประกาศชื่อ Activity จะมีการระบุเจาะจงชื่อ Package ไปด้วย ผู้ที่หลงเข้ามาอ่านอาจจะคุ้นเคยกับการประกาศแค่ชื่อว่า
<activity android:name=".AnotherActivity" />
        จะทำเช่นนี้ได้ก็ต่อเมื่อ Activity อยู่ใน Package ตามที่กำหนดไว้ในโปรเจคนี้เท่านั้น แต่ในกรณีนี้ Activity ดังกล่าวอยู่คนละ Package กัน ถ้าประกาศแบบนี้จะทำให้แอปพลิเคชันมองหา Activity ไม่เจอและเกิดเออเรอร์ขึ้น

        ดังนั้นจึงต้องระบุเจาะจงไปเลยว่าเป็นของ Package อะไร สำหรับ Main นั้นไม่จำเป็นต้องระบุ Package ก็ได้ เพราะอยู่ใน Package เดียวกันกับที่กำหนดไว้ในโปรเจค แต่เพื่อไม่ให้สับสน ระบุเจาะจงไว้ก็ดีเหมือนกัน

        ทีนี้ก็ลองติดตั้งและทดสอบดู ที่หน้า Main ให้ลองกดตรงไหนก็ได้ของแอปพลิเคชัน ก็จะทำการ Intent ไปยัง AnotherActivity ได้


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

        โดยการแยก Package กัน หัวใจสำคัญหลักๆก็คือการระบุ Package ให้ถูกต้อง โดยมีคร่าวๆดังนี้

        • Activity ใน Package หลัก คือ Activity ที่อยู่ใน Package เดียวกันกับที่ประกาศไว้ใน Android Manifest

        • Activity ใน Package อื่นๆ คือ Activity ที่อยู่ใน Package คนละที่กับที่ประกาศไว้ใน Android Manifest

        • Activity ใน Package หลักจะเรียก Activity ใน Package อื่นๆจะต้องประกาศ Import เพื่อระบุ Package นั้นๆด้วย
import app.akexorcist.anotherpackage.AnotherActivity;

        • Activity ใน Package อื่นๆจะเรียกใช้ Resource (R.xxx.xxx) ต้อง Import คลาส R แบบระบุ Package หลัก
import app.akexorcist.multiplepackage.R;

        สำหรับผู้ที่หลงเข้ามาอ่านคนใดต้องการไฟล์ตัวอย่างสามารถดาวน์โหลดได้ที่ Multiple Package [Google Drive]


        ลองทำอะไรแปลกๆดูก็สนุกดีนะเออ แถมได้เข้าใจการทำงานของแอปพลิเคชันได้มากขึ้นไปอีกขั้นนึง ^ ^




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

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