ListView Project (10) The End
ListView project ေလးတစ္ခု အပ်င္းေျပ ေရးၾကည္႔ရေအာင္ဗ်ာ ... အပိုင္း ( 10 )
***********************
အရင္အပိုင္းက Level ကို စစ္တဲ႔အပိုင္း အခက္အခဲ မရွိဘူးလို႔ ယူဆပါတယ္။
ကဲ ဒီ project ရဲ႕ ေနာက္ဆံုးအပိုင္းအျဖစ္ က်ေနာ္တို႔ရဲ႕ List စာရင္းကို save သိမ္းႏိုင္ ျပန္ေခၚယူႏိုင္တဲ႔ အပိုင္းကို code သြားၾကရေအာင္။
မ code ခင္ က်ေနာ္တို႔ရဲ႕ List စာရင္းကို အခု က်ေနာ္တို႔ရဲ႕ code flow အရ ဘယ္ေနရာေတြမွာ save လုပ္ရင္ျပီး ဘယ္ေနရာေတြ ျပန္ေခၚသံုးမလဲ ဆိုတဲ႔ point ေနရာေတြကို ရွာၾကရေအာင္။
အခု app မွာ အဖြင့္ စာမ်က္ႏွာ page မွာ က်ေနာ္တို႔ List စာရင္းကို ျမင္ေတြ႕ေအာင္ ေရးထားတာဆိုေတာ႔ app စ စ ဖြင့္ျခင္းမွာ save လုပ္ထားမယ္႔ က်ေနာ္တို႔ List စာရင္းကို ယူျပႏိုင္ရမယ္ေပါ႔။ ဒါဆို app စ စ ဖြင့္ျခင္း အသက္ဝင္တာက MainActivity class ရဲ႕ onCreate() method ဆိုေတာ႔ ဒီ method အတြင္းမွာ save ထားမယ္႔ List စာရင္းကို ျပန္ေခၚလိုက္ရင္ အဆင္ေျပျပီ။ ဒါ ျပန္ေခၚတဲ႔ အပိုင္း။
ဒါဆို save လုပ္ သိမ္းမယ္႔ point ေတြကေရာ? အခု project မွာ List စာရင္းကို အေျပာင္းအလဲ ျဖစ္ေစတဲ႔ ေနရာေတြမွာ က်ေနာ္တို႔ List ကို save မယ္လို႔ စိတ္ကူးမိပါတယ္။ အဲ႔ဒီလို List စာရင္းအေျပာင္းအလဲ ျဖစ္ေစတဲ႔ေနရာ ႏွစ္ေနရာ ရွိပါတယ္။
ပထမ point က New Member အသစ္ထည္႔တဲ႔ ေနရာပါ။ Member အသစ္တစ္ေယာက္ထည္႔တိုင္း List က နဂို List မဟုတ္ေတာ႔ပဲ data အသစ္တိုးလာတဲ႔ List စာရင္းအသစ္ ျဖစ္သြားပါျပီ။ ဒီေနရာမွာ List ကို ဖမ္း save သိမ္းပါမယ္။ Member အသစ္ထည္႔တာ Dialog Box ရဲ႕ Add button onClick() က လုပ္ေပးတာမို႔ save ေပးမယ္႔ code ကိုလည္း အဲ႔ေနရာက ေရးရပါမယ္။ တစ္ေနရာ ရပါျပီ။
ဒုတိယေနရာကေတာ႔ List ေပၚက x ဆိုတဲ႔ Delete လုပ္ျပီး List စာရင္းထဲက member ကို ဖ်က္တဲ႔အခ်ိန္မွာ မူလ List မဟုတ္ေတာ႔ပဲ data အေျပာင္းအလဲ ျဖစ္သြားတဲ႔အတြက္ အဲ႔ဒီေနရာက တစ္ခါ save လုပ္ေပးလိုက္ပါတယ္။ ဒီ "x" Delete Button ဟာ FighterAdapter class အတြင္းက onClick() ခ်ိတ္ထားတာ ျဖစ္တဲ႔အတြက္ အဲ႔ဒီ onClick() method အတြင္းကေန code ျပီး save ေပးရမွာ ျဖစ္ပါတယ္။
ေနရာေတြလည္း သိျပီမို႔ save မယ္႔အပိုင္း စ code လိုက္ၾကရေအာင္။ ဒီေနရာမွာ က်ေနာ္တို႔ သိမ္းခ်င္တဲ႔ data ေတာ္ေတာ္မ်ားမ်ားကို သံုးေနက် Sharedpreferences သံုးျပီး save တတ္ၾကေပမယ္႔ က်ေနာ္ကေတာ႔ အခုလို project မ်ိဳးမွာ အသင့္ေတာ္ဆံုးျဖစ္တဲ႔ file read/write သံုးျပီး object serialization အေၾကာင္းကို Beginner ေတြကို မိတ္ဆက္ေပးလိုက္ပါတယ္။
ဟာ file read/write ဆိုေတာ႔ တို႔ေတာ႔ byte ေတြ bit ေတြ သိပ္နားမလည္ဘူးဆိုျပီး စိတ္ပ်က္မသြားပါနဲ႔ဦး။ ဘာ byte/bit မွ သံုးစရာမလိုပဲ input/output လမ္းေၾကာင္းမွန္မွန္ေရြးျပီး သူတို႔ရဲ႕ read/write လုပ္တဲ႔ method တစ္ခုႏွစ္ခုေလာက္ ယူသံုးေပးလိုက္ရံုနဲ႔ OK ပါတယ္။ အေရးၾကီးတာက code flow/code logic ကို မ်က္ေျခမျပတ္ဖို႔နဲ႔ try/catch block ေလးေတြပါေနလို႔ မ်က္လံုးရွင္ရွင္ပဲ ထားေပးဖို႔ လိုတာပါ။ ကဲ Ready ဆို စပါမယ္။
data ကို file read/write နဲ႔ save/ထုတ္ သံုးမွာဆိုေတာ႔ က်ေနာ္က data ကို file ဆီ write(ေရး) save ေပးမယ္႔ method နဲ႔ file ဆီက read လုပ္ ျပန္ယူမယ္႔ method ေလး ႏွစ္ခုကို MainActivity class ထဲ ဖန္တီးေရးသံုးသြာပါမယ္။
ဒါေၾကာင့္ အရင္ဆံုး write လုပ္ save ေပးမယ္႔ method ကို writeMyListToFile
ဆိုျပီး နာမည္ ေပးလိုက္ၾကရေအာင္
public void writeMyListToFile() {
}
အခု method ဟာ က်ေနာ္တို႔ AIDEFighter object ေတြပါတဲ႔ ArrayList<> တစ္ခုကို ယူျပီး file တစ္ခုဆီကို write လုပ္ေပးရမွာျဖစ္လို႔ အဝင္ input အျဖစ္ ArrayList တစ္ခု ေတာင္းေပးလိုက္ၾကရေအာင္ပါ။
public void writeMyListToFile(ArrayList<AIDEFighter> myList) {
}
method အတြင္း code ပါမယ္။
File တစ္ခုဆီကို လွမ္းေရးဖို႔ FileOutputStream တစ္ခုလိုပါမယ္။ ဒါကို android ကေပးထားတဲ႔ method တစ္ခုျဖစ္တဲ႔ openFileOutput() method ထဲကို ကိုယ္လွမ္းေရးခ်င္တဲ႔ file နာမည္နဲ႔ ေရးမယ္႔ Mode ထည္႔ျပီး အခုလို ယူလို႔ရပါတယ္။
FileOutputStream fos = openFileOutput("mylist.data",0);
ဒီေနရာမွာ ဖိုင္ read/write လုပ္တာကို android က ခြင့္ျပဳေပးထားတဲ႔ app တစ္ခုရဲ႕ ကိုင္ပိုင္ directory/folder ေအာက္မွာပဲ လုပ္မွာမို႔ SD card read/write permission လိုမွာ မဟုတ္ပါဘူး။ ေနာက္က 0 ကေတာ႔ private ကို ရည္ညႊန္းတာ ျဖစ္ပါတယ္။
ရလာမယ္႔ FileOutputStream ကို Object ေတြကို တိုက္ရိုက္ေရးေပးႏိုင္တဲ႔ ObjectOutputStream အျဖစ္ wrap လုပ္ ေျပာင္းပစ္ပါမယ္။
ObjectOutputStream oos = new ObjectOutputStream(fos);
OOS ျဖစ္သြားျပီဆိုရင္ သူ႔မွာရွိတဲ႔ writeObject() method ထဲကို က်ေနာ္တို႔ရဲ႕ List စာရင္းကို ထည္႔ေပးလိုက္ျပီး write ေရးခိုင္းလိုက္ရံုပါပဲ။
oos.writeObject(myList);
ျပီးရင္ ဒီ Stream ကို ပိတ္ပစ္ပါမယ္။
oos.close();
ဒါဆို က်ေနာ္တို႔ write လုပ္ေပးမယ္႕ method ျပီးသြားျပီ ျဖစ္ပါတယ္။
public void writeMyListToFile(ArrayList<AIDEFighter> myList) {
FileOutputStream fos = openFileOutput("mylist.data",0);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myList);
oos.close();
}
အဲ ဒါေပမယ္႔ ဒီလို Input/output stream ေတြကို code တဲ႔ ေရးတဲ႔အခါမွာ ျဖစ္ေပၚလာတဲ႔ Exception (ျပႆနာ/error) ေလးေတြ ရွိေနပါမယ္။ ဆိုပါစို႔ရယ္ က်ေနာ္တို႔က အခု method ထဲမွာ က်ေနာ္တို႔ List စာရင္း data ကို သိမ္းဖို႔ mylist.data ဆိုတဲ႔ ဖိုင္ေလးတစ္ခုကို FileoutputStream နဲ႔ လွမ္းေထာက္ ဖြင့္လိုက္တယ္။ ဒါေပမယ္႔ အဲ႔ဖိုင္ေလးက ရွိမေနရင္ပဲျဖစ္ျဖစ္ FileNotFound ဖိုင္မရွိပါဆိုတဲ႕ error /Exception ျဖစ္လာမွာပါ။ ဒီလို ျဖစ္ေပၚလာတဲ႔ Exception ေတြ ရွိေနတယ္ဆိုရင္ Java က ဒီအတိုင္း မေရးခိုင္းပါဘူး။ try/catch block နဲ႔ ေရးမွာလား ? ဒီ Exception ကို အခုေရးေနတဲ႔ method ကေန throws Exception (Exception အျဖစ္ခံျပီး app ကို crash အျဖစ္ခံမွာလား ေမးပါတယ္။ က်ေနာ္တို႔က အခု ဒီ method ကေန Exception ျဖစ္ခဲ႔ရင္ အျဖစ္ခံပါမယ္ (throws Exceptions) ။ try/catch ေတြနဲ႔ ဒီ method ထဲမွာ ဖမ္းေနရင္ Input/output Steam method ေတြ (ဥပမာ openFileOutput(), writeObject() ) ေခၚသံုးတိုင္း method တစ္ခုကို try/catch block တစ္ခုနဲ႔ လိုက္ေရးေပးေနရလို႔ try/catch block ေတြနဲ႔ ရႈပ္ယွက္ခက္ေနပါမယ္။
ဒီျဖစ္လာမယ္႔ Exception ကို app crash မျဖစ္ေအာင္ ဒီ method ကို ေခၚယူသံုးတဲ႕ေနရာကေန အဲ႔ Exception ကို try/catch block တစ္ခုတည္းနဲ႔ အဆင့္ဆင့္ စု catch လုပ္ျပန္ဖမ္းပါမယ္။ Exception အျဖစ္ခံမွာဆိုေတာ႔ အခု က်ေနာ္တို႔ ေရးလိုက္တဲ႔ method မွာ ျဖစ္ေပၚလာႏိုင္တဲ႔ Exception ေတြအတြက္ throws clause ထည္႔ေပးရမွာပါ။ throws clause ထည္႔ခ်င္ရင္ သူျပထားတဲ႔ error ေပၚေထာက္ျပီး အေပၚဘားတန္းက အမွန္ျခစ္ကေန adds to throws clause သံုးျပီး တစ္ခုခ်င္း ထည္႔သြားရံုပါ။
public void writeMyListToFile(ArrayList<AIDEFighter> myList) throws FileNotFoundException, IOException {
FileOutputStream fos = openFileOutput("mylist.data",0);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myList);
oos.close();
}
ဒီ အခု method က save တဲ႔ အပိုင္းဆိုေတာ႔ အေပၚက ေျပာခဲ႔တဲ႔ save ရမယ္႔ point ႏွစ္ေနရာမွာ save ခ်င္တဲ႔ List ကို ထည္႔ေပးျပီး၊ Exception ဖမ္းဖို႔ေရာ try/catch block နဲ႔ method call လုပ္ေပးရမွာပါ။ ဆိုေတာ႔ FighterAdapter class က delete button onClick() method အတြင္းကေနဆို အရင္ကလို MainActivity class က method ကို လွမ္းယူသံုးတာျဖစ္တဲ႔အတြက္ Casting လုပ္ method call လုပ္ေပးရပါမယ္။
try
{
((MainActivity)mContext).writeMyListToFile(mFighterList);
}
catch (IOException e)
{}
Dialog Box ရဲ႕ Add Button onClick() အတြင္းကေနဆို
try
{
((MainActivity)getActivity()).writeMyListToFile(mFighterList);
}
catch (IOException e)
{}
ဆိုျပီး method call လုပ္ေပးလိုက္ရံုပါပဲ။ ဒီလို အျပင္ Class ကေန ယူသံုးႏိုင္ေအာင္လို႔ MainActivity class က အခု က်ေနာ္တို႔ေရးခဲ႔တဲ႔ writeMyListToFile(...) method ကို public modifier နဲ႔ public scope နဲ႔ ေရးေပးခဲ႔ျခင္းျဖစ္ပါတယ္။
ဒါ save ေပးမယ္႔ အပိုင္း ျပီးသြားပါျပီ။ ေနာက္တစ္ခုက ျပန္ယူမယ္႔အပိုင္းအတြက္ method တစ္ခု ေရးရေအာင္ပါ။ ေပးလိုက္ပါ နာမည္ကို readMyListFromFile() ။ MainActivity class ရဲ႕ ျပန္ယူရမယ္႔ point ျဖစ္တဲ႕ onCteate(...) ကေနသံုးရမွာ ဒီ class အတြင္းမွာပဲ သံုးမွာမို႔ private scope နဲ႔ ေရးတယ္ေပါ႔။
private void readMyListFromFile() {
}
ဒီ method ကေန save သိမ္းထားတဲ႔ List data ကို ျပန္ယူမွာဆိုေတာ႔ ဒီ data ကို ျပန္ထုတ္ေပးဖို႔ return type နဲ႔ ေရးရေအာင္ပါ။ void မဟုတ္ေတာ႔ဘူးေပါ႔။ ထုတ္ေပးမယ္႔ data က ဘာအမ်ိဳးအစား (type) ပါလဲ? သိမ္းတုန္းက data type ျဖစ္တဲ႔ ArrayList<> ျဖစ္ပါမယ္။ အဲ႔ ArrayList က ဘာ object ေတြပါေနမွာလဲ။ AIDEFighter object ေလးေတြ ပါေနမယ္ဆိုတာ က်ေနာ္တို႔ အေသအခ်ာ သိေပမယ္႔ ေရးတတ္သြားေအာင္ ဘာ object ပါမယ္မွန္းမသိဘူးလို႕ သေဘာထားျပီး object type ေနရာမွာ မသိ/မေျပာတတ္လို႔ပဲ အခုလို ေရးလိုက္ပါ။ ArrayList<?> ။ ဒါ အခု method ရဲ႕ return type ပါ။ ဒါေၾကာင့္ return type ထည္႔ျပီး ေရးလိုက္ပါ။
private ArrayList<?> readMyListFromFile() {
}
ျပီးတာနဲ႔ method အတြင္းမွာ
ျပန္ထုတ္မယ္႔ data အတြက္ variable တစ္ခု သတ္မွတ္ ေၾကျငာလိုက္ပါ။
ArrayList<?> myList = null;
ဆက္ျပီး ေစာေစာက save တုန္းကနဲ႔ ေျပာင္းျပန္ျဖစ္တဲ႔
save လုပ္ထားတဲ႔ file ဆီကို InputStream နဲ႔ ေထာက္ဖြင့္ပါမယ္။
FileInputStream fis = openFileInput("mylist.data");
ျပီးရင္ အဲ႔ Stream ကို Object ေတြကို read လုပ္ေပးဖို႔ သီးသန္႔ေပးထားတဲ႔ ObjectInputStream နဲ႔ wrap လုပ္ ပါမယ္။
ObjectInputStream ois = new ObjectInputStream(fis);
ျပီးရင္ ဒီ Stream ရဲ႕ readObject() method နဲ႔ ဖတ္ျပီး ရလာတဲ႔ data ကို အေပၚက ArrayList<> myList ရဲ႕ data အျဖစ္ သတ္မွတ္ေပးပါမယ္။
myList = (ArrayList<?>) ois.readObject();
ျပီးရင္ ဒီ Stream ကို ပိတ္ပစ္တယ္ေပါ႔။
oos.close();
ျပီးရင္ data ရွိေနျပီျဖစ္တဲ႔ myList ကို return ကေန ျပန္ထုတ္ေပးလိုက္ပါမယ္။
return myList;
ဒါဆို ဒီ method အပိုင္း code ျပည္႔စံုပါျပီ။ အေပၚတုန္းကလို throws clause ေတြ ထည္႔ျပီး ေရးေပးလိုက္ေတာ႔ -
private ArrayList<?> readMyListFromFile() throws FileNotFoundException, IOException, ClassNotFoundException {
ArrayList<?> myList = null;
FileInputStream fis = openFileInput("mylist.data");
ObjectInputStream ois = new ObjectInputStream(fis);
myList = (ArrayList<?>) ois.readObject();
ois.close();
return myList;
}
ကဲ ဒီ method ကို က်ေနာ္တို႔ code logic အရ data ျပန္ယူမယ္႔ point ျဖစ္တဲ႔ onCreate() ထဲကေန ေခၚသံုးရမယ္ဆိုေတာ႔ အရင္ mList နဲ႔ ပက္သက္တဲ႕ initialization လုပ္ထားတာကို ျပန္သံုးသပ္ျပင္ေရးေပးရပါမယ္။
အရင္ဆံုး ဒီ List အတြက္ save လုပ္ထားတဲ႔ file ရွိေနမလားဆိုျပီး အခု ေရးခဲ႔တဲ႔ method နဲ႔ mList data ကို (Exception ဖမ္းဖို႔ try/catch နဲ႔ ) ဖိုင္ဖြင့္ ယူၾကည္႔ပါမယ္။
try
{
mList = (ArrayList<AIDEFighter>) readMyListFromFile();
}
catch (IOException e)
{}
catch (ClassNotFoundException e)
{}
ဒီ ဖိုင္ ရွိမေနရင္လည္း Exception ဖမ္းထားတဲ႔အတြက္ app crash ျဖစ္မသြားပါ။ အဲ ဒါေပမယ္႔ က်ေနာ္တို႔ mList ကေတာ႔ ဘာ data မွ ပါမလာတဲ႔အတြက္ null ျဖစ္ေနပါမယ္။ အျခား method ေတြမွာ ဒီ mList ကို ယူသံုးရမွာ ျဖစ္တဲ႔အတြက္ ဒီ mList က null ျဖစ္ေနလို႔မရပါ။
ဒါေၾကာင့္ mList ကို null ျပန္စစ္ျပီး "new" operator နဲ႔ အသက္သြင္း initialize လုပ္ေပးထားရပါမယ္။ (app ကို ပထမဆံုး စသံုးမယ္႔ အခ်ိန္ save ထားတဲ႔ file မရွိတဲ႔အတြက္ ေသခ်ာေပါက္ mList က null ျဖစ္ေနပါမယ္။
if(mList == null) {
mList = new ArrayList<AIDEFighter>();
}
ျပီးမွ ဒီ mList ကို (list item ေတြ ပါသည္ျဖစ္ေစ မပါသည္ျဖစ္ေစ အသက္သြင္းျပီးသားမို႔ ) ListView ေပၚတင္ျပဖို႔ က်ေနာ္တို႔ ေရးခဲ႔တဲ႔ method ကို ေခၚျပီး ListView မွာ တင္ျပေပးလိုက္ရင္ ရပါျပီ။
showMyList();
ဒီ code အားလံုးက အေပၚကေန ေအာက္ကို အစဥ္လိုက္ run အလုပ္လုပ္ သြားတာမို႔ ေရးရမယ္႔ code flow အေပၚေအာက္ တစ္ခုခု လြဲသြားတာနဲ႔ app crash ျဖစ္ႏိုင္ပါတယ္။
ကဲ ဒါဆို data ျပန္ယူတဲ႔ အပိုင္းလည္း ျပီးသြားျပီ ျဖစ္ပါတယ္။
အဲ .... ဒါ ေပ မယ္႕...
အခု ေရးခဲ႔တဲ႔ method ႏွစ္ခုစလံုး တကယ္အလုပ္လုပ္ဖို႔ဆိုရင္ က်ေနာ္တို႔ကိုယ္တိုင္ ဒီဇိုင္းဆြဲ ေရးခဲ႔တဲ႔ AIDEFighter (object) class က ဒီလို ObjectInput/output Stream နဲ႔ read/write လုပ္ရင္ လုပ္ခြင့္ျပဳတယ္ဆိုတဲ႔ Serializable ဆိုတဲ႔ marker interface နဲ႔ ခ်ိတ္ဆက္ ေပးရပါမယ္။ အျခားမဟုတ္ပါဘူး "implements" ဆိုတဲ႔ keyword ကို အသံုးျပဳျပီး class ထိပ္မွာ အခုလို ေရးေပးလိုက္ရံုပါ။
public class AIDEFighter implements Serializable {
…
…
…
}
ကဲ Simple ListView project လည္း အဆံုးသတ္သြားပါျပီ။ ဒီအပိုင္းကို ပိုင္ႏိုင္ေအာင္ code ျပီး app ကို build လုပ္ျပီး List စာရင္း data save မ save စမ္းသပ္ၾကည္႔လိုက္ပါဦး။
ေအာင္ျမင္ျပီဆို Layout ဒီဇိုင္းေတြကိုလည္း ရိြဳင္းျပီး ကလိၾကဖို႔ -
ဒီ project ကေန ရတဲ႔ code flow ကေန မိမိမွာ ဖြင့္ထားတဲ႔ သင္တန္းေတြရွိရင္ AIDEFighter လို ကိုယ္ပိုင္ object class ေတြ ဖန္တီးျပီ ေနာက္ထပ္လိုအပ္မယ္႔ instance field ေတြ (လခ/ေနာက္ဆံုးသြင္းရက္) သင္တန္းေၾကး total / လက္က်န္ ... စသည္) method ေတြ ဖန္တီးျပီး List အပိုင္းေတြပါတဲ႔ app ေတြ အမ်ားၾကီး ေရးႏိုင္ပါေစလို႔ ဆႏၵျပဳရင္း ....
-----------------------------------------
ကိုမ်ိဳး
AIDE Android Lessons And Project Group
မွ ကူးယူတင္ထားပါသည္
#mkk_ListView
Comments
Post a Comment
Thank,s for ...