
หลังจากที่เราพัฒนา 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 นี่แหละ!




