หลังจากที่เราพัฒนา Web App ด้วย Javascript ร่วมกับ Firebase Database มาสักพักแล้ว รอบนี้เราก็ต้องนำข้อมูลที่เราทำไปโผล่บนแอพฯ Android ของเรา
บทเรียนที่ควรศึกษาก่อนหน้านี้
- การพัฒนาแอพพลิเคชัน Android
- สร้าง Web App สำหรับเข้าระบบ Authentication ด้วย Firebase
- สร้างเว็บ CMS ด้วย Firebase Realtime Database และ Storage ตอนที่ 1
- สร้างเว็บ CMS ด้วย Firebase Realtime Database และ Storage ตอนที่ 2
- สร้างเว็บ CMS ด้วย Firebase Realtime Database และ Storage ตอนที่ 3
หากเราศึกษามาหมดแล้วเราจะได้ผลลัพธ์ของเว็บแอพฯ ที่จะเป็น CMS ของเราบน Firebase Hosting ดังนั้นเราลองกรอกข้อมูลเข้าไปใหม่ จัดสรรค์โครงสร้างข้อมูลให้เป็นแอพฯ อะไรสักอย่างที่มีประโยชน์สักนิด
ดังนั้นโครงสร้างของ ฐานข้อมูล Firebase Database เราจะเป็นดังนี้:
จะเห็นว่าเราจะมีข้อมูล Key ของ แต่ละ Node และ thumbnail ที่อ้างไปยัง Storage ของ Firebase ที่เราสร้างแล้ว เราก็เพียงแค่พัฒนาแอพพลิเคชันของเราให้ทำการ Retieve ข้อมูลลงไปแสดงผลก็พอ
สร้าง Project ใหม่ของเราผ่าน Android Studio
เลือกเป็น Empty Activity หลังจากนั้นให้เราติดตั้ง Firebase ลงไปในแอพพลิเคชันของเรา ตามขั้นตอนบนเว็บ https://console.firebase.google.com
ขอข้ามการติดตั้ง goole-services.json ออกไปจากบทความนี้ก่อนเพราะสามารถไปอ่านบทความหลังๆ ได้ที่: https://www.daydev.com/developer/android-developer/firebase-android-studio.html
ไปที่ AndroidManiFest.xml เปิด Permission ของ Internet ให้เรียบร้อย:
<uses-permission android:name="android.permission.INTERNET"/>
เปิดไฟล์ gradle ตัว Project แก้ไขดังนี้
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.google.gms:google-services:3.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }
เปิดไฟล์ gradle ตัว Module
เพิ่มสิ่งที่เราจะพัฒนาในบทเรียนเข้าไปให้หมด แล้ว Sync gradle เสีย
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.+' compile 'com.google.firebase:firebase-database:9.4.0' compile 'com.android.support:cardview-v7:26.0.+' compile 'com.android.support:recyclerview-v7:26.0.+' compile 'com.squareup.picasso:picasso:2.5.2' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' } apply plugin: 'com.google.gms.google-services'
ในบทเรียนนี้เราจะใช้ Firebase Database ในการดึงข้อมูลมาแสดงผ่าน Adapter โดยใช้ RecyclerView และ CardView มารับค่าข้อมูล และใช้ Picasso จัดการการเก็บแคชของข้อมูล
สร้าง Class ใหม่ขึ้นมาชื่อว่า DataAdapter.java และ DataModel.java
package com.daydev.androidfirebase; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import java.util.List; public class DataAdapter extends RecyclerView.Adapter<DataAdapter.DataViewHolder> { private List<DataModel> datalist; public DataAdapter(List<DataModel> result) { this.datalist = result; } @Override public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new DataViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row,parent,false)); } @Override public void onBindViewHolder(final DataAdapter.DataViewHolder holder, int position) { DataModel dataModel = datalist.get(position); holder.textTitle.setText(dataModel.title); holder.textContent.setText(dataModel.content); Log.d("MyApp",dataModel.thumbnail); //Todo Manage ImageView } @Override public int getItemCount() { return datalist.size(); } public class DataViewHolder extends RecyclerView.ViewHolder { TextView textTitle,textContent; ImageView imageView; public DataViewHolder(View itemView) { super(itemView); textTitle = itemView.findViewById(R.id.txt_title); textContent = itemView.findViewById(R.id.txt_content); imageView = itemView.findViewById(R.id.thumbnail); } } }
ในขั้นตอนนี้จะมีการถามหา DataModel เราจะต้องไปสร้าง Model สำหรับเรียกข้อมูลมาเก็บแปลงลง Adapter และมีการเรียกหา Layout ที่ชื่อว่า list_row
ให้ทำการสร้าง Layout ใหม่ขึ้นมาชื่อว่า list_row.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cardview="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="90dp" cardview:cardCornerRadius="2dp" cardview:cardElevation="3dp" cardview:cardUseCompatPadding="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="90dp" android:orientation="horizontal"> <ImageView android:id="@+id/thumbnail" android:layout_width="120dp" android:layout_height="70dp" android:layout_marginLeft="5dp" android:layout_marginTop="5dp" android:paddingRight="10dp" android:scaleType="centerCrop" cardview:srcCompat="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="206dp" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/txt_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="Banyapon Poolsawas" android:textSize="18sp" /> <TextView android:id="@+id/txt_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/txt_title" android:layout_weight="1" android:text="College of Creative Design and Entertainment Technology" android:textSize="12sp" /> </LinearLayout> <ImageView android:id="@+id/imageView2" android:layout_width="48dp" android:layout_height="48dp" android:layout_weight="1" cardview:srcCompat="@drawable/common_plus_signin_btn_icon_dark_normal" /> </LinearLayout> </android.support.v7.widget.CardView>
สำหรับ DataModel.java นั้นให้ทำการประกาศตัวแปรต่อไปนี้ใน Class
String title,content,thumbnail,key;
หลังจากนั้นให้ทำการ Create ตัว Constructor โดยการกด Ctrl+Enter หรือ Cmd + Enter เราจะได้ Class ของ DataModel.java ดังนี้
package com.daydev.androidfirebase; import java.util.HashMap; import java.util.Map; public class DataModel { String title,content,thumbnail,key; public DataModel(){ } public DataModel(String title, String content, String thumbnail, String key) { this.title = title; this.content = content; this.thumbnail = thumbnail; this.key = key; } public Map<String, Object> toMap(){ HashMap<String, Object> result = new HashMap<>(); result.put("title",title); result.put("content",content); result.put("thumbnail",thumbnail); result.put("key",key); return result; } }
ทำการออกแบบหน้า activity_main.xml ดังนี้:
<?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" tools:context="com.daydev.androidfirebase.MainActivity" tools:layout_editor_absoluteY="81dp" tools:layout_editor_absoluteX="0dp"> <android.support.v7.widget.RecyclerView android:id="@+id/data_list" android:layout_width="366dp" android:layout_height="496dp" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="8dp" android:layout_marginLeft="8dp" app:layout_constraintLeft_toLeftOf="parent" android:layout_marginRight="8dp" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="8dp"> </android.support.v7.widget.RecyclerView> </android.support.constraint.ConstraintLayout>
ไปที่ MainActivity.java ให้เราทำการประกาศตัวแปร Global ดังนี้:
private static final String TAG = "MyApp"; private RecyclerView recyclerView; private List<DataModel> result; private DataAdapter dataAdapter; private FirebaseDatabase firebaseDatabase; private DatabaseReference databaseReference;
ทำการ Implement ส่วนของ onCreate() ดังนี้:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); firebaseDatabase = FirebaseDatabase.getInstance(); databaseReference = firebaseDatabase.getReference("app/data"); result = new ArrayList<>(); recyclerView = (RecyclerView)findViewById(R.id.data_list); recyclerView.setHasFixedSize(true); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(linearLayoutManager); dataAdapter = new DataAdapter(result); recyclerView.setAdapter(dataAdapter); bindingData(); }
จะเห็นว่ามีการเรียกไปยัง refernce Database ส่วนของ Node ที่ชื่อว่า app/data ให้ตรงกับส่วนที่เราเก็บข้อมูลไว้ใน Web CMS เมื่อเสร็จขั้นตอนการ setAdapter ของ recyclersView แล้วเราจะต้องสร้าง Method ใหม่ชื่อว่า bindingData();
private void bindingData(){ databaseReference.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { result.add(dataSnapshot.getValue(DataModel.class)); dataAdapter.notifyDataSetChanged(); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }); }
ใส่เมธอดสำหรับอ้างอิง Index สักหน่อยเผื่อจะนำไปใช้ในการทำ Intent ในอนาคต
private int getIemIndex(DataModel dataModel){ int index = -1; for(int i =0; i < result.size(); i++){ if(result.get(i).key.equals(dataModel.key)){ index = i; break; } } return index; }
ดังนั้นไฟล์ MainActivity.java จะเป็นดังนี้:
package com.daydev.androidfirebase; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import com.google.firebase.database.ChildEventListener; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private static final String TAG = "MyApp"; private RecyclerView recyclerView; private List<DataModel> result; private DataAdapter dataAdapter; private FirebaseDatabase firebaseDatabase; private DatabaseReference databaseReference; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); firebaseDatabase = FirebaseDatabase.getInstance(); databaseReference = firebaseDatabase.getReference("app/data"); result = new ArrayList<>(); recyclerView = (RecyclerView)findViewById(R.id.data_list); recyclerView.setHasFixedSize(true); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(linearLayoutManager); dataAdapter = new DataAdapter(result); recyclerView.setAdapter(dataAdapter); bindingData(); } private void bindingData(){ databaseReference.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { result.add(dataSnapshot.getValue(DataModel.class)); dataAdapter.notifyDataSetChanged(); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }); } private int getIemIndex(DataModel dataModel){ int index = -1; for(int i =0; i < result.size(); i++){ if(result.get(i).key.equals(dataModel.key)){ index = i; break; } } return index; } }
กลับไปที่ DataAdapter ให้เพิ่ม code ส่วนของ Picasso เข้าไปที่ //Todo Manage ImageView
//Todo Manage ImageView Picasso.with(holder.itemView.getContext()).load(dataModel.thumbnail) .error(R.mipmap.ic_launcher) .placeholder(R.mipmap.ic_launcher) .into(holder.imageView);
ทดสอบแอพพลิเคชันของเรา
จะเห็นได้ว่าตอนนี้ เราได้ระบบหลังบ้าน Firebase ในการจัดการช้อมูลบนเว็บไซต์ CMS และนำข้อมูลที่จัดเก็บนั้นมาสร้างระบบ รายการแสดงผลผ่านแอพพลิเคชัน Android ซึ่งเราสามารถสร้างแอพพลิเคชันเข้าระบบ Authentication ของ Android นั้นก็บทความนี้ครับ https://www.daydev.com/developer/android-developer/android-authentication-firebase.html
ตัวอย่างนี้สามารถนำมาใช้เป็นตัวอย่างเพื่อการศึกษา และเป็นการบอกว่า Firebase นั้นเหมาะกับการทำ โครงงานของนักศึกษา หรือ SMEs มากๆ (ยกเว้นนักศึกษาปริญญาตรีวิทยาลัยครีเอทีฟดีไซน์ แอนด์ เอ็นเตอร์เทนเมนต์เทคโนโลยี ที่ผมสังกัดอยู่ ถ้าจะเอามาส่งต้องดีกว่าตัวอย่างนี้)
ดังนั้นใครที่สนใจ Firebase น่าจะได้ Idea ดีๆ ทำอะไรใหม่ๆ ได้จากบทความนี้ครับ
บทความหน้า การยิง Push Notification ผ่าน Firebase นี่แหละ!