Android DeveloperFirebase

เขียนแอพ Android รับค่า Realtime Database จาก CMS ของ Firebase

หลังจากที่เราพัฒนา Web App ด้วย Javascript ร่วมกับ Firebase Database มาสักพักแล้ว รอบนี้เราก็ต้องนำข้อมูลที่เราทำไปโผล่บนแอพฯ Android ของเรา

บทเรียนที่ควรศึกษาก่อนหน้านี้

หากเราศึกษามาหมดแล้วเราจะได้ผลลัพธ์ของเว็บแอพฯ ที่จะเป็น CMS ของเราบน Firebase Hosting ดังนั้นเราลองกรอกข้อมูลเข้าไปใหม่ จัดสรรค์โครงสร้างข้อมูลให้เป็นแอพฯ อะไรสักอย่างที่มีประโยชน์สักนิด

ขายรองเท้านี่แหละ

ดังนั้นโครงสร้างของ ฐานข้อมูล Firebase Database เราจะเป็นดังนี้:

บนหน้าเว็บไซต์เป็นแบบนี้
โครงสร้างของ Firebase Database
โครงสร้างของ Firebase Database
ส่วนนี่โครงสร้างแบบ JSON
ส่วนนี่โครงสร้างแบบ JSON โดยอ้าง node ที่ต้องการดูข้อมูล

จะเห็นว่าเราจะมีข้อมูล 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() ดังนี้:

สร้าง เมธอด ใหม่
กด Ctrl+Enter เพื่อ Override Methods
@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);

ทดสอบแอพพลิเคชันของเรา

หน้าจอแอพพลิเคชันของเรา

 

ข้อมูลเพิ่มแบบ Real Time

จะเห็นได้ว่าตอนนี้ เราได้ระบบหลังบ้าน Firebase ในการจัดการช้อมูลบนเว็บไซต์ CMS และนำข้อมูลที่จัดเก็บนั้นมาสร้างระบบ รายการแสดงผลผ่านแอพพลิเคชัน Android ซึ่งเราสามารถสร้างแอพพลิเคชันเข้าระบบ Authentication ของ Android นั้นก็บทความนี้ครับ https://www.daydev.com/developer/android-developer/android-authentication-firebase.html

ตัวอย่างนี้สามารถนำมาใช้เป็นตัวอย่างเพื่อการศึกษา และเป็นการบอกว่า Firebase นั้นเหมาะกับการทำ โครงงานของนักศึกษา หรือ SMEs มากๆ (ยกเว้นนักศึกษาปริญญาตรีวิทยาลัยครีเอทีฟดีไซน์ แอนด์ เอ็นเตอร์เทนเมนต์เทคโนโลยี ที่ผมสังกัดอยู่ ถ้าจะเอามาส่งต้องดีกว่าตัวอย่างนี้)

ดังนั้นใครที่สนใจ Firebase น่าจะได้ Idea ดีๆ ทำอะไรใหม่ๆ ได้จากบทความนี้ครับ

บทความหน้า การยิง Push Notification ผ่าน Firebase นี่แหละ!

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน