Nhúng API Tỉ giá hối đoái của Ngân hàng Đông Á vào Website riêng – ASP.NET Core


Trong bài lấy tỉ giá hối đoái của Ngân Hàng Vietcombank, họ dùng XML để lưu trữ dữ liêu. Tui cũng đã hướng dẫn cách đọc XML từ service của Ngân hàng rồi.

Trong bài này, Tui hướng dẫn các bạn cách nhúng API tỉ giá hối đoái của Ngân Hàng Đông Á vào Website ASP .NET Core

API của họ được viết bằng JSON: http://www.dongabank.com.vn/exchange/export

Ta phân tích 1 chút xíu về cấu trúc json này của Ngân hàng Đông Á.

  • Thứ nhất:  Cấu trúc trên Ngân Hàng Đông Á đã cố tình sửa JSON bằng cách thêm ngoặc tròn bao lấy 2 đầu của dữ liệu, ta cần xóa bỏ nó đi để dữ liệu trở về đúng cấu trúc của JSON
  • Thứ 2: Ở ngoài cùng là 1 Json, bên trong là 1 mảng các tỉ giá được lưu vào biến items. Mỗi 1 phần tử trong items (Ta gọi là nó các dòng tỉ giá), nó có các thuộc tính:  type, imageurl, muatienmat, muack, bantienmat, banck
  • Thứ 3: API này Của Đông Á họ đã cấm cách truy suất thông thường, ta phải bổ sung các header là: “User-Agent” = “Mozilla/5.0 ( compatible ) ” và “Accept”] = “*/*”

Mục tiêu sau khi chạy xong Website thì nó phải nhúng được API tỉ giá của Đông Á Bank và hiển thị lên giao diện như sau:

Các bước làm làm chi tiết, các bạn thao tác theo Tui hướng dẫn:

Mở Visual Studio 2017/ chọn File/ New / Project:

Màn hình New Project sẽ hiển thị ra như dưới đây:

Chọn ASP.NET Core Web Application

đặt tên Project là “DongABank”, chọn nơi lưu trữ phù hợp rồi nhấn nút OK:

Màn hình chọn New ASP.NET Core Web Application hiển thị như dưới đây:

Ta chọn Web Application (Model – View – Controller)  và cấu hình như trên rồi nhấn nút OK để tạo Project:

Quan sát Cấu trúc JSON của Ngân Hàng Đông Á, Ta sẽ tạo 1 Lớp tên là Item (lưu ý Lớp này tên gì cũng được vì không được định nghĩa tên), thuộc tính của nó bao gồm:

Ta bấm chuột phải vào Models/Add/ Class:

Sau khi chọn Class-> màn hình sau xuất hiện:

Ta chọn Class rồi đặt tên Lớp là: Item.cs rồi bấm ADD:

Bổ sung Coding cho Item.cs dựa vào cấu trúc JSON của Ngân Hàng Đông Á Cung cấp:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DongAbank.Models
{
public class Item
{
public string type { get; set; }
public string imageurl { get; set; }
public string muatienmat { get; set; }
public string muack { get; set; }
public string bantienmat { get; set; }
public string banck { get; set; }
}
}

Tiếp tục tạo thêm 1 Lớp để lưu trữ danh sách các item: Bấm chuột phải vào models/ chọn Add/ chọn Class:

Cửa sổ tạo Class hiển thị ra:

Đặt tên lớp là TiGiaDongA (lưu ý thích đặt tên gì cũng được, miễn thuộc tính của nó phải chính xác là items)

Coding của TiGiaDongA.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DongAbank.Models
{
public class TiGiaDongA
{
public List<Item> items { get; set; }
}
}

Bước tiếp theo tạo Controller cho Website. Cụ thể là tên TiGiaDongABankController:

Bấm chuột phải vào thư mục Controller/ chọn Controller:

màn hình chọn Controller xuất hiện . Ta chọn MVC Controller – Empty:

Sau đó nhấn nút ADD:

Ta đặt tên là TiGiaDongABankController rồi bấm Add:

Hiểu chỉnh coding của Controller này để trả về Danh sách Item:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using DongAbank.Models;
using System.Net;
using System.IO;

namespace DongAbank.Controllers
{
public class TiGiaDongABankController : Controller
{
public IActionResult Index()
{
string siteContent = string.Empty;

// link JSON của DongA
string url = "http://www.dongabank.com.vn/exchange/export";

//dùng HTTPWebRequest
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//với Đông Á Bank phải thêm 2 dòng lệnh này:
request.Headers["User-Agent"] = "Mozilla/5.0 ( compatible ) ";
request.Headers["Accept"] = "*/*";
request.AutomaticDecompression = DecompressionMethods.GZip;

//lấy đối tượng Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
//gọi hàm GetResponseStream() để trả về đối tượng Stream
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
string data = reader.ReadToEnd();
//vì dữ liệu bị bao bọc là ngoặc tròn, ta đổi nó thành rỗng để đúng cấu trúc Json
data = data.Replace(")", "").Replace("(", "");
//chuyển dữ liệu Json qua C# class:
TiGiaDongA tigia = (TiGiaDongA)JsonConvert.DeserializeObject(data,typeof(TiGiaDongA));
//trả về cho View là 1 danh sách các Item (các dòng Tỉ Giá)
return View(tigia.items);
}
}
}

Bây giờ ta tạo View cho Controller tỉ giá:

Bấm chuột phải vào hàm Index/ chọn Add View…

Màn hình Add MVC View hiển ra như dưới đây, ta chọn Template là List, Model class là Item:

Chọn xong thì bấm ADD, lúc này trang Index.cshtml được tạo ra với coding Razor như dưới đây:

Chi tiết coding cho Index.cshtml:


@model IEnumerable<DongAbank.Models.Item>

@{
ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.type)
</th>
<th>
@Html.DisplayNameFor(model => model.imageurl)
</th>
<th>
@Html.DisplayNameFor(model => model.muatienmat)
</th>
<th>
@Html.DisplayNameFor(model => model.muack)
</th>
<th>
@Html.DisplayNameFor(model => model.bantienmat)
</th>
<th>
@Html.DisplayNameFor(model => model.banck)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.type)
</td>
<td>
@Html.DisplayFor(modelItem => item.imageurl)
</td>
<td>
@Html.DisplayFor(modelItem => item.muatienmat)
</td>
<td>
@Html.DisplayFor(modelItem => item.muack)
</td>
<td>
@Html.DisplayFor(modelItem => item.bantienmat)
</td>
<td>
@Html.DisplayFor(modelItem => item.banck)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>

Bây giờ ta F5 chạy lên sẽ có được kết quả như dưới đây:

Ở đây ta cần chỉnh sửa 2 chỗ:

  • Hiển thị Hình Ảnh thay vì hiển thị link hình ảnh
  • Chỉnh tiêu đề của danh sách Tỉ giá

Ta chỉnh coding thành hiển thị hình ảnh như sau:

ở trên Tui đã thay thế thành thẻ img để hiển thị hình ảnh, và bên trong là lệnh @Url.Content để lấy hình ảnh từ Http về trình duyệt:

<img src=”@Url.Content(item.imageurl)” alt=”Image” />

Để chỉnh sửa tiêu đề thì đơn giản rồi, tự gõ vào thôi:

Bây giờ F5 chạy Website lên, ta có kết quả mới đúng như mong muốn:

Như vậy Tui đã hướng dẫn các bạn xong cách thức nhúng API Tỉ Giá Hối Đoái Của Ngân Hàng Đông Á vào Website riêng của bạn, dùng ASP .NET Core. Dữ liệu là JSON, biết cách đứa JSON thành C# class.

Coding bạn tải ở đây: Link tải coding ở đây

Chúc các bạn thành công!

Rút trích dữ liệu Tỉ giá hối đoái của ngân hàng Vietcombank bằng ASP.NET Core


Dạo này thiết kế Website với ASP. NET Core rầm rộ, với nhiều lợi ích được mô tả ở đây: https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-2.1

Nên Tui cũng ngứa tay hướng dẫn các bạn 1 bài nhỏ nhỏ dùng ASP .NET Core đó là “Rút trích dữ liệu Tỉ giá hối đoái của ngân hàng Vietcombank”.

Cụ thể Ngân Hàng Vietcombank có công bố Tỉ giá hối đoái dưới dạng XML trên Website. Chúng ta có thể rút trích dữ liệu từ đây về để phục vụ cho các vấn đề khác (tra cứu tỉ giá, kết hợp du lịch)

Cụ thể các bạn vào website của Ngân Hàng: https://www.vietcombank.com.vn/

Kéo xuống dưới cùng Website, nhìn vào góc phải thấy mục “Xem thông tin tỷ giá các chi nhánh tại đây”

ta nhấn vào nút này, kết quả ta được dẫn tới trang http://www.vietcombank.com.vn/ExchangeRates/:

Nhấn vào nút XML ở trên, ta tiếp tục được dẫn tới 1 link khác : http://www.vietcombank.com.vn/ExchangeRates/ExrateXML.aspx

Bây giờ nhiệm vụ của ta là xây dựng 1 Website ASP .NET Core để truy suất và hiển thị toàn bộ tỉ giá trong này lên giao diện.

Ta khởi động Visual Studio (Tui dùng VS 2017 nha)-> rồi vào File / chọn New / chọn Project:

Lúc này màn hình tạo Project hiển thị ra như dưới đây:

Ta chọn ASP .NET Core Web Application

Đặt tên Project là “Vietcombank”, nhớ lưu vào đâu đó khác ổ C hay Desktop là OK (Desktop là ổ C đó nha) rồi bấm OK:

Lúc này màn hình tạo Project mới hiển thị ra, ta chọn Web Application (Model- View – Controllers), phía trên chọn ASP .NET Core 2.1, nhớ bỏ tick Configure for HTTPs. sau đó nhấn OK nha, Project sẽ được tạo ra như dưới đây:

Bây giờ ta tiến hành tạo các  lớp C# dạng POCO, cấu trúc của nó giống như Ngân Hàng Vietcombank cung cấp trong XML tỉ giá:

Như vậy dựa vào cấu trúc này thì ta phải tạo 2 Lớp C#. Đó là lớp Exrate và lớp ExrateList.

Exrate có các thuộc tính: CurrencyCode, CurrencyName, Buy, Transfer, Sell. Tất cả chúng đề có kiểu chuỗi là Ok (có thể Buy, Transfer, Sell ta để kiểu double cũng ngon lành (nhưng vì chả tính toán gì cả, ta phang luôn kiểu string)

ExrateList Có các thuộc tính: DateTime, List<Exrate>, Source.

Bây giờ ta lần lượt tạo các lớp:

Đầu tiên là Lớp Exrate, ta bấm chuột phải vào Models/ chọn Add/ Chọn Class:

màn hình Tạo lớp hiển thị ra như dưới đây:

Ta chọn Class, Name đặt là Exrate.cs rồi bấm Add:

Coding cho lớp Exrate này mapping với các thuộc tính, tag được định nghĩa trong XML của Ngân hàng Vietcombank:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Vietcombank.Models
{
public class Exrate
{
[XmlAttribute(AttributeName = "CurrencyCode")]
public string CurrencyCode { get; set; }
[XmlAttribute(AttributeName = "CurrencyName")]
public string CurrencyName { get; set; }
[XmlAttribute(AttributeName = "Buy")]
public string Buy { get; set; }
[XmlAttribute(AttributeName = "Transfer")]
public string Transfer { get; set; }
[XmlAttribute(AttributeName = "Sell")]
public string Sell { get; set; }
}
}

Ở trên thấy Tui using System.Xml.Serialization, đây là thư viên liên quan XML, cho phép chuyển hóa từ XML -> C# class

Còn các [XmlAttribute(AttributeName = “CurrencyCode”)] để nói cho C# hiểu nó cần mapping đúng thuộc tính nào trong tag XML. Tên Property của C# có thể viết lung tung, nhưng trong XmlAttribute phải viết chính xác những gì Ngân Hàng Vietcombank cung cấp.

Tiếp tục lặp lại thao tác thêm lớp mới cho ExrateList:

chỉnh sửa coding cho nó như sau:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Vietcombank.Models
{
[XmlRoot(ElementName = "ExrateList")]
public class ExrateList
{
[XmlElement(ElementName = "DateTime")]
public string DateTime { get; set; }
[XmlElement(ElementName = "Exrate")]
public List<Exrate> Exrates { get; set; }
[XmlElement(ElementName = "Source")]
public string Source { get; set; }
}
}

Bước tiếp theo là tạo 1 Controller tên là TiGiaController để rút trích dữ liệu Tỉ giá của Ngân hàng đồng thời chuyển hóa nó qua C# class để hiển thị lên Website riêng của mình:

Bấm chuột phải vào Controllers/ chọn Add/ chọn Controller…:

màn hình lựa chọn Controller xuất hiện:

Ta chọn MVC Controller Empty rồi bấm Add, màn hình yêu cầu đặt tên cho Controller xuất hiện:

ta đổi Default thành TiGia rồi bấm Add, kết quả:

Lúc này hàm Index hiển thị ra, ta bấm chuột phải vào Index để tạo View bằng cách chọn Add View… (giao diện Website cho nó):

Lúc này màn hinh tạo View hiển thị ra:

Phần Template: Chọn List (hiển thị danh sách, trong View nó là Table đó)

Phần Model calss: Chọn Exrate -> để hiển thị danh sách Exrate trong lớp ExrateList

các thông số khác để vậy nha, giờ bấm ADD:

Kết quả View hiển thị ra như dưới đây:

Coding HTML đầy đủ của Tigia/Index.cshtml


@model IEnumerable<Vietcombank.Models.Exrate>

@{
ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.CurrencyCode)
</th>
<th>
@Html.DisplayNameFor(model => model.CurrencyName)
</th>
<th>
@Html.DisplayNameFor(model => model.Buy)
</th>
<th>
@Html.DisplayNameFor(model => model.Transfer)
</th>
<th>
@Html.DisplayNameFor(model => model.Sell)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CurrencyCode)
</td>
<td>
@Html.DisplayFor(modelItem => item.CurrencyName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Buy)
</td>
<td>
@Html.DisplayFor(modelItem => item.Transfer)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sell)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>

Giờ quay lại Controller: TiGiaController, tiến hành chỉnh sửa coding:


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Xml.Serialization;
using Vietcombank.Models;

namespace Vietcombank.Controllers
{
public class TiGiaController : Controller
{
public IActionResult Index()
{
string siteContent = string.Empty;

// link XML của Vietcombank
string url = "https://www.vietcombank.com.vn/exchangerates/ExrateXML.aspx";

//dùng HTTPWebRequest
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AutomaticDecompression = DecompressionMethods.GZip;
//lấy đối tượng Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
//gọi hàm GetResponseStream() để trả về đối tượng Stream
Stream responseStream = response.GetResponseStream();
//convert từ XML qua C# model:
XmlSerializer serializer = new XmlSerializer(typeof(ExrateList));
ExrateList exrateList =(ExrateList) serializer.Deserialize(responseStream);
//lấy danh sách Extrates truyền qua cho View
return View(exrateList.Exrates);
}
}
}

Nhấn F5 chạy lên, ta có kết quả Website như mong muốn:

Như vậy Tui đã trình bày xong cách dùng ASP .NET Core để truy suất dữ liệu Tỉ giá của ngân hàng Vietcombank, cách thức chuyển hóa từ XML thành C# class, cũng như hướng dẫn cách làm với Model – View – Controller trong ASP .NET Core.

Đây là source code của Project: Tải tại đây

Chúc các bạn thành công!

 

Phát triển ứng dụng cơ sở dữ liệu thời gian thực với Firebase-phần 5


Trong các bài hướng dẫn trước (Xem tại đây) Tui đã trình bày hoàn chỉnh thác tác CRUD trên Realtime Database Firebase, với những ví dụ từ cơ bản đến nâng cao, nó đã giúp các bạn hiểu và tự tạo được những ứng dụng riêng để Android có thể tương tác với Realtime Database Firebase.

Ở bài này, Tui muốn hướng dẫn thêm một vài trường hợp đặc biệt đó là: Làm thể nào để Chụp hình cũng như lấy hình từ SD Card rồi đưa hình này lên Realtime Database Firebase.

Trong hệ sinh thái Firebase của Google thì họ hỗ trợ rất nhiều công cụ, trong đó có Cloud Storage For Firebase giúp bạn có thể upload/download 1 file bất kỳ lên Cloud của Google. Phần Cloud Storage Tui sẽ có những TUT hướng dẫn riêng. Còn trong bài này Tui vẫn dùng dự án từ bài số 4, tiếp tục hiệu chỉnh phần mềm để thêm các chức năng: CHỤP HÌNH + CHỌN HÌNH từ SD Card + Đưa hình lên Realtime Database Firebase + Tải hình từ Realtime Database Firebase.

Bạn nhớ mở lại Project ở phần 4 nha.

Sau đó tiến hành làm các thao tác như Tui hướng dẫn dưới này:

Tạo một màn hình mới, tên là ThemContactActivity: Bấm chuột phải vào package/ chọn new / Chọn Activit / chọn Empty Activity:

Sau đó đặt tên: ThemContactActivity

Đặt tên xong nhấn Finish để tạo màn hình, thiết kế màn hình Thêm Contact như dưới đây:

Ở trên ta có thêm 2 ImageButton: Chụp ảnh + chọn Ảnh từ SD Card

Và thêm Image View để hiển thị hình ảnh mới chụp hoặc hình ảnh lấy từ SD Card

Đây là XML layout của màn hình Thêm Contact:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".ThemContactActivity">
<TextView
android:id="@+id/textView0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Contact Id:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtContactId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Contact Id ở đây"
android:inputType="textPersonName"
android:textSize="15sp" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Ten:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtTen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Tên ở đây"
android:inputType="textPersonName"
android:textSize="15sp" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Email:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Email ở đây"
android:inputType="textEmailAddress"
android:textSize="15sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Phone:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone ở đây"
android:inputType="phone"
android:textSize="15sp" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Chọn Hình:"
android:textSize="15sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnCapture"
android:src="@drawable/camera" />

<ImageButton
android:id="@+id/btnChoose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:src="@drawable/choose" />
</LinearLayout>

<ImageView
android:id="@+id/imgPicture"
android:layout_width="match_parent"
android:layout_height="150dp"
app:srcCompat="@drawable/noimage" />
<Button
android:onClick="xuLyThemMoi"
android:id="@+id/btnDongYThem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Đồng Ý Thêm"
android:textSize="15sp" />
</LinearLayout>

Chỉnh sửa coding cho ThemContactActivity để có thể đẩy hình lên Realtime Database Firebase:


package com.communityuni.advancedrealtimedatabasefirebase;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ThemContactActivity extends AppCompatActivity {
ImageButton btnCapture;
ImageButton btnChoose;
ImageView imgPicture;
Bitmap selectedBitmap;
EditText edtId,edtTen,edtPhone,edtEmail;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_them_contact);
addControls();
addEvents();
}
public void addControls()
{
btnCapture = findViewById(R.id.btnCapture);
btnChoose= findViewById(R.id.btnChoose);
imgPicture=findViewById(R.id.imgPicture);
edtId=findViewById(R.id.edtContactId);
edtTen=findViewById(R.id.edtTen);
edtPhone=findViewById(R.id.edtPhone);
edtEmail=findViewById(R.id.edtEmail);
}
public void addEvents() {
btnCapture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
capturePicture();
}
});
btnChoose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
choosePicture();
}
});
}
//xử lý chọn hình
private void choosePicture() {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto , 200);//one can be replaced with any action code
}
//xử lý chụp hình
private void capturePicture() {
Intent cInt = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cInt,100);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100&& resultCode == RESULT_OK) {
//xử lý lấy ảnh trực tiếp lúc chụp hình:
selectedBitmap = (Bitmap) data.getExtras().get("data");
imgPicture.setImageBitmap(selectedBitmap);
}
else if(requestCode == 200&& resultCode == RESULT_OK) {
try {
//xử lý lấy ảnh chọn từ điện thoại:
Uri imageUri = data.getData();
selectedBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
imgPicture.setImageBitmap(selectedBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void xuLyThemMoi(View view) {
try {
FirebaseDatabase database = FirebaseDatabase.getInstance();
//Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
DatabaseReference myRef = database.getReference("contacts");
String contactId=edtId.getText().toString();
String ten = edtTen.getText().toString();
String phone = edtPhone.getText().toString();
String email = edtEmail.getText().toString();
myRef.child(contactId).child("phone").setValue(phone);
myRef.child(contactId).child("email").setValue(email);
myRef.child(contactId).child("name").setValue(ten);

//đưa bitmap về base64string:
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
selectedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
String imgeEncoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
myRef.child(contactId).child("picture").setValue(imgeEncoded);

finish();
}
catch (Exception ex)
{
Toast.makeText(this,"Error:"+ex.toString(),Toast.LENGTH_LONG).show();
}
}
}

Coding ở trên có 3 điểm mới:

Thứ nhất: Chụp hình

Thứ 2: Chọn hình từ thiết bị

Thứ 3: Đưa hình về Base64String

Lưu ý rằng ta phải cấp quyền truy suất CAMERA, cũng như Storage cho phần mềm nhé, chỉnh sửa lại AndroidManifest:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.communityuni.advancedrealtimedatabasefirebase">

<uses-feature
android:name="android.hardware.Camera"
android:required="true"></uses-feature>

<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ThemContactActivity" />
</application>

</manifest>

Sau đó bổ sung Option Menu ở màn hình MainActivity để thêm menu “Thêm Contact” cho người dùng sử dụng, bước thêm Menu xem lại phần 2:

Bổ sung coding hiển thị menu và xử lý chọn menu cho MainActivity:


@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater=getMenuInflater();
menuInflater.inflate(R.menu.main_menu,menu);
return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.mnuAdd)
{
//mở màn hình thêm ở đây
Intent intent=new Intent(MainActivity.this,ThemContactActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}

Chạy phần mềm lên ta có:

Xem màn hình thêm Contact có các chức năng: Chụp hình, chọn hình, đưa hình lên Realtime Database Firebase:

Lưu ý từ bản Android version 6.0 trở về đây do tính bảo mật nên những phần mềm không trực tiếp chạy từ Google Play, ngoài bước cấp quyền trong Android Manifest ra, ta còn phải cấp quyền trong thiết bị nữa mới được, nếu quên không cấp quyền thêm 1 lần nữa trong thiết bị thì ứng dụng sẽ không thể chạy được.

Ta cấp theo theo bước sau:

Quay trở lại màn hình HOME SCREEN của điện thoại, chọn phần mềm mà ta đang lập trình này-> nhấn thật lâu vào nó rồi chọn  “App Infor”:

Chọn App Infor xong ta có giao diện:

Ở trên ta thấy mục “Permissions” đang báo là chưa có quyền nào được cấp, ta nhấn vào nó:

Ta tick enable nó lên như hình ở trên-> đã thành công, giờ có thể sử dụng được phần mềm rồi nha:

Lưu ý ở trên 2 ImageButton: Chụp hình + Chọn hình tùy ta chọn nha: Muốn chụp hình Live thì bấm vào nút đầu tiên, muốn chọn 1 hình có sẵn trong máy thì chọn nút 2.

Sau khi nhập đầy đủ thông tin và hình ảnh thì bấm nút “Đồng ý thêm”, chương trình thêm thành công và quay trở về màn hình chính, lúc này ta có kết quả:

Trên Realtime Database Firebase ta có hình ảnh dưới dạng Base64String, và giao diện cũng có Contact mới này.

Như vậy là tới đây Tui đã hướng dẫn xong bước thêm 1 Contact có hình ảnh.

Bây giờ ta bổ sung chức năng cập nhập Contact. Vì trước đó ta có các Contact mà chưa có hình ảnh (tức là không có thuộc tính picture). giờ ta thêm chức năng này để cập nhật hình cho bất kỳ contact nào.

Ta tạo màn hình tên “CapNhatContactActivity”, bước làm tương tự như ThemContactActivity. ta chỉnh giao diện màn hình cập nhật như dưới đây:

Đây là XML layout của màn hình Cập nhật:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".CapNhatContactActivity">
<TextView
android:id="@+id/textView0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Contact Id:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtContactId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Contact Id ở đây"
android:inputType="textPersonName"
android:textSize="15sp" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Ten:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtTen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Tên ở đây"
android:inputType="textPersonName"
android:textSize="15sp" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Email:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Email ở đây"
android:inputType="textEmailAddress"
android:textSize="15sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Phone:"
android:textSize="15sp" />

<EditText
android:id="@+id/edtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone ở đây"
android:inputType="phone"
android:textSize="15sp" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Chọn Hình:"
android:textSize="15sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnCapture"
android:src="@drawable/camera" />

<ImageButton
android:id="@+id/btnChoose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:src="@drawable/choose" />
</LinearLayout>

<ImageView
android:id="@+id/imgPicture"
android:layout_width="match_parent"
android:layout_height="150dp"
app:srcCompat="@drawable/noimage" />
<Button
android:onClick="xuLyCapNhat"
android:id="@+id/btnDongCapNhat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Đồng Ý Cập Nhật"
android:textSize="15sp" />
</LinearLayout>

Nhiệm vụ của màn hình Cập nhật là:

  • hiển thị chi tiết Contact được chọn trên giao diện ListView, bao gồm hình ảnh  Base64String được tải về từ Realtime database firebase
  • cho phép cập nhật dữ liệu cho Contact bao gồm cả hình ảnh

Chỉnh sửa coding của MainActivity: Nhấn vào 1 Contact nào trên Listview thì mở màn hình Cập nhật ra:


lvContact.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Contact data=adapter.getItem(position);
Intent intent=new Intent(MainActivity.this,CapNhatContactActivity.class);
intent.putExtra("KEY",data.getContactId());
startActivity(intent);
}
});

Tiếp tục chỉnh sửa coding của màn hình CapNhatContactActivity:


package com.communityuni.advancedrealtimedatabasefirebase;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import com.communityuni.model.Contact;
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 com.google.firebase.database.ValueEventListener;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;

public class CapNhatContactActivity extends AppCompatActivity {

EditText edtId,edtTen,edtPhone,edtEmail;
ImageView imgPicture;
ImageButton btnCapture;
ImageButton btnChoose;
Bitmap selectedBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cap_nhat_contact);
addControls();
getContactDetail();
addEvents();
}

private void getContactDetail() {
Intent intent=getIntent();
final String key=intent.getStringExtra("KEY");
final FirebaseDatabase database = FirebaseDatabase.getInstance();
//Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
DatabaseReference myRef = database.getReference("contacts");

//truy suất và lắng nghe sự thay đổi dữ liệu
//chỉ truy suất node được chọn trên ListView myRef.child(key)
//addListenerForSingleValueEvent để lấy dữ liệu đơn
myRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
try {
Contact contact=dataSnapshot.getValue(Contact.class);
contact.setContactId(dataSnapshot.getKey());
edtId.setText(contact.getContactId());
edtTen.setText(contact.getName());
edtEmail.setText(contact.getEmail());
edtPhone.setText(contact.getPhone());
if(contact.getPicture()!=null) {
byte[] decodedString = Base64.decode(contact.getPicture(), Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
imgPicture.setImageBitmap(decodedByte);
}
}
catch (Exception ex)
{
Log.e("LOI_JSON",ex.toString());
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w("LOI_CHITIET", "loadPost:onCancelled", databaseError.toException());
}
});
}

private void addControls() {
btnCapture = (ImageButton) findViewById(R.id.btnCapture);
btnChoose= (ImageButton) findViewById(R.id.btnChoose);
imgPicture= (ImageView) findViewById(R.id.imgPicture);
edtId=findViewById(R.id.edtContactId);
edtTen=findViewById(R.id.edtTen);
edtPhone=findViewById(R.id.edtPhone);
edtEmail=findViewById(R.id.edtEmail);
}
public void addEvents() {
btnCapture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
capturePicture();
}
});
btnChoose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
choosePicture();
}
});
}
private void choosePicture() {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto , 200);//one can be replaced with any action code
}

private void capturePicture() {
Intent cInt = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cInt,100);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100&& resultCode == RESULT_OK) {
//xử lý lấy ảnh trực tiếp lúc chụp hình:
selectedBitmap = (Bitmap) data.getExtras().get("data");
imgPicture.setImageBitmap(selectedBitmap);
}
else if(requestCode == 200&& resultCode == RESULT_OK) {
try {
//xử lý lấy ảnh chọn từ điện thoại:
Uri imageUri = data.getData();
selectedBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
imgPicture.setImageBitmap(selectedBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void xuLyCapNhat(View view) {
try {
FirebaseDatabase database = FirebaseDatabase.getInstance();
//Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
DatabaseReference myRef = database.getReference("contacts");
String contactId=edtId.getText().toString();
String ten = edtTen.getText().toString();
String phone = edtPhone.getText().toString();
String email = edtEmail.getText().toString();
myRef.child(contactId).child("phone").setValue(phone);
myRef.child(contactId).child("email").setValue(email);
myRef.child(contactId).child("name").setValue(ten);

//đưa bitmap về base64string:
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
selectedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
String imgeEncoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
myRef.child(contactId).child("picture").setValue(imgeEncoded);

finish();
}
catch (Exception ex)
{
Toast.makeText(this,"Error:"+ex.toString(),Toast.LENGTH_LONG).show();
}
}
}

Chạy phần mềm lên, ta chỉnh sửa dữ liệu cho contact7:

Khi cập nhật thành công, lúc này contact7 sẽ được bổ sung thêm thuộc tính picture và có giá trị là base64string cho bức hình gửi từ điện thoại lên.

Như vậy Tui đã hoàn chỉnh hướng dẫn toàn bộ chức năng CRUD nâng cao, bao gồm đưa hình ảnh lên Realtime Database Firebase và tải hình ảnh từ Realtime Database Firebase về điện thoại. Bài này khá phức tạp nhưng rất hay, Các bạn cố gắng hoàn thành nha.

Coding đầy đủ các bạn tải tại đây: Tải source code tại đây

Chúc các bạn thành công!

Phát triển ứng dụng cơ sở dữ liệu thời gian thực với Firebase-phần 4


Như vậy Tui đã hướng dẫn xong toàn bộ CRUD trong Realtime Database Firebase, các bạn nhớ làm theo thứ tự bài 1, bài 2, bài 3 nha.

Ở bài này Tui sẽ trình bày cách sử dụng Firebase ở mức cao hơn 1 xíu đó là: Sử dụng Java model class, Custom layout, tái sử dụng lại Realtime database ở bài trước(tức là giờ có 2 App cùng sử dụng 1 CSDL). Để giúp phần mềm được chuyên nghiệp và dễ dàng hơn cho Dev cũng như Customer.

Kết thúc bài này, ta có giao diện phần mềm như sau:

Các bước làm giống như các bài trước. Tuy nhiên trong bài này ta sẽ Bổ sung Customlayout, model class.

Tui mới update Android studio version mới nhất 3.2.1 (Tính tới ngày 15/10/2018) nên Tui new lại Project từ đầu nha.

Khởi động Android Studio:

Chọn Start a new Android Studio project:

Đặt tên Project là AdvancedRealtimeDatabaseFirebase rồi bấm Next

Chọn API 26 rồi bấm Next:

Tiếp tục chọn Empty Activity trong màn hình trên rồi bấm Next:

Mặc định để MainActivity rồi bấm Finish để tạo Project:

Đợi cho Project tạo xong ta sẽ kết nối tới Firebase bằng cách vào menu Tools/ chọn Firebase:

Sau khi chọn Firebase, lúc này công cụ trợ giúp sẽ xuất hiện:

Google cung cấp rất nhiều Service, nhưng ta cũng quan tâm tới Realtime Database-> nhấn vào mục có chữ này:

Tiếp tục nhấn vào “Save and retrieve data” để kết nối Realtime Database:

Google cũng  cung cấp nhiều bước, ta nhấn vào bước 1 “Connect to Firebase”, lúc này cửa sổ kết nối Firebase xuất hiện, tại bài Advanced này Tui muốn tái sử dụng lại CSDL ở các bài trước, Tui sẽ chọn mục số 2 “Choose an existing Firebase ỏ Google Project”:

Lúc này màn hình sẽ như sau:

rõ ràng ở trên bạn thấy “RealtimeDatabaseFirebase”, là Project ở các bài trước. Giờ ta bấm “Connect to Firebase”:

Ta chờ nó kết nối nha, nếu có lỗi xảy ra nó sẽ xuất hiện thông báo như màn hình dưới đây:

Đừng lo lắng, tiếp tục bấm lại “Connect to Firebase”, lúc này màn hình sau xuất hiện:

Ta nhấn vào “Sync” để đồng bộ, kết quả sẽ thành công như dưới đây:

Tiếp tục nhấn vào “Add the Realtime Database Rules” ở bước 2:

Chương trình sẽ yêu cầu thêm một số thư viện vào build.gradle. Ta nhấn Accept Changes:

Quan sát tập tin build.gradle, có lệnh: implementation ở cuối cùng. Ta xóa :15.0.0 đi:

Sau đó nhấn vào nút Try Again ở góc trên màn hình thông báo lỗi. Sau khi thành công thì ta bắt đầu lập trình được rồi. Lưu ý là khi kết nối thành công thì trên Firebase Console ta cũng thấy xuất hiện app này nha:

Như vậy bây giờ là 2 Ứng dụng Android có thể sử dụng chung 1 cơ sở dữ liệu Realtime Database Firebase rồi.

Ta xem lại cấu trúc JSON trên Firebase:

Bây giờ ta sẽ tạo 1 java class để mapping dữ liệu: Bằng cách nhấn chuột phải vào java/ chọn New / chọn Package:

Lúc này cửa sổ tạo package sẽ show ra như dưới đây:

Ta chọn dòng giữa rồi nhấn OK:

Đặt tên Package mới rồi nhấn OK. Lưu ý model để cuối cùng nha, sao cho nó có cấu trúc như vầy cho nó khóa học:

Tiếp tục nhấn chuột phải vào model / chọn New / chọn Java Class:

Màn hình tạo Lớp mới xuất hiện ra như bên dưới. Ta đặt tên lớp là Contact:

Nhấn OK để đồng ý tạo lớp Contact:

Chỉ sửa coding cho Lớp Contact này như dưới đây:


package com.communityuni.model;

public class Contact {
private String contactId;
private String name;
private String phone;
private String email;

public String getContactId() {
return contactId;
}

public void setContactId(String contactId) {
this.contactId = contactId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}

Vì ta làm Custom Layout để hiển thị danh sách Contact như hình:

Nên ta cần tạo 1 layout như sau:

Bấm chuột phải vào layout/ chọn New / chọn Layout resource file:

Đặt tên file là item rồi bấm OK:

Sau khi bấm OK, ta có giao diện:

Chỉnh sửa layout của item.xml sao cho giao diện đáp ứng yêu cầu:

Dưới đây là file XML layout của item.xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical">

<TextView
android:id="@+id/txtId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="@android:color/holo_red_dark"
android:textSize="25sp" />

<TextView
android:id="@+id/txtName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="?android:attr/colorActivatedHighlight"
android:textSize="25sp" />

<TextView
android:id="@+id/txtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="@android:color/holo_green_dark"
android:textSize="25sp" />

<TextView
android:id="@+id/txtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="@android:color/holo_blue_dark"
android:textSize="25sp" />

<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="?attr/colorControlActivated" />
</LinearLayout>

Tiếp tục lặp lại bước tạo package mới, đó là package adapter:

Bấm chuột phải vào java/ chọn New/ chọn Package:

Chọn mục số 2:

Nhấn OK để đặt tên cho Package:

package adapter cũng đồng cấp với package model:

Tiếp tục tạo một lớp tên là : ContactAdapter để vẽ giao diện theo Custom Layout

Bấm chuột phải vào thư mục adapter/ chọn New/ chọn Java Class:

Đặt tên cho Class là ContactAdapter:

Sau đó nhấn OK để đồng ý tạo class, chỉnh sửa coding cho ContactAdapter như dưới đây:


package com.communityuni.adapter;

import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.communityuni.advancedrealtimedatabasefirebase.R;
import com.communityuni.model.Contact;

public class ContactAdapter extends ArrayAdapter<Contact> {
Activity context;
int resource;
public ContactAdapter(Activity context, int resource) {
super(context, resource);
this.context=context;
this.resource=resource;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View custom=context.getLayoutInflater().inflate(resource,null);
TextView txtId=custom.findViewById(R.id.txtId);
TextView txtName=custom.findViewById(R.id.txtName);
TextView txtPhone=custom.findViewById(R.id.txtPhone);
TextView txtEmail=custom.findViewById(R.id.txtEmail);
Contact contact=getItem(position);
txtId.setText(contact.getContactId());
txtName.setText(contact.getName());
txtPhone.setText(contact.getPhone());
txtEmail.setText(contact.getEmail());
return custom;
}
}

 

OK, cũng sắp xong rồi. Bây giờ bạn quay lại MainActivity để coding:


package com.communityuni.advancedrealtimedatabasefirebase;

import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;

import com.communityuni.adapter.ContactAdapter;
import com.communityuni.model.Contact;
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 com.google.firebase.database.ValueEventListener;

public class MainActivity extends AppCompatActivity {

ListView lvContact;
ContactAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addControls();
getContactsFromFirebase();
}
private void getContactsFromFirebase() {
FirebaseDatabase firebaseDatabase=FirebaseDatabase.getInstance();
DatabaseReference myRef= firebaseDatabase.getReference("contacts");
adapter.clear();
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot dss : dataSnapshot.getChildren())
{
//convert ra đối tượng Contact:
Contact contact=dss.getValue(Contact.class);
String key=dss.getKey();
contact.setContactId(key);
adapter.add(contact);
}
}

@Override
public void onCancelled(@NonNull DatabaseError databaseError) {

}
});
}

private void addControls() {
lvContact=findViewById(R.id.lvContact);
adapter=new ContactAdapter(this,R.layout.item);
lvContact.setAdapter(adapter);
}
}

ở trên ta thấy dòng lệnh 37:

Contact contact=dss.getValue(Contact.class);

Dòng lệnh này để đưa dữ liệu trong HashMap về đúng kiểu Contact (nó tự mapping)

Khi có đối tượng contact rồi thì ta dán nó vào adapter là tự nhiên có giao diện như mong muốn

Chạy phần mềm lên ta sẽ có kết quả như sau:

Như vậy Tui đã hướng dẫn xong cách dùng model class, customlayout để truy suất dữ liệu từ Firebase. Cách làm cũng tương tự như loại cơ bản. tuy nhiên cách này sẽ khoa học hơn, tùy biến nhanh hơn.

Ngoài ra các thao tác Thêm, Sửa, Xóa. Các bạn có thể cho chương trình lắng nghe kết quả trả về, tương tự như:

Ví dụ: Thêm sự kiện xóa Contact khi nhấn lâu vào ListView và kiểm tra kết quả thành công hay thất bại:


protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addControls();
getContactsFromFirebase();
addEvents();
}
private void addEvents() {

lvContact.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Contact contact=adapter.getItem(position);
FirebaseDatabase firebaseDatabase=FirebaseDatabase.getInstance();
DatabaseReference myRef= firebaseDatabase.getReference("contacts");
myRef.child(contact.getContactId()).removeValue()
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Toast.makeText(MainActivity.this,"Thành công",Toast.LENGTH_LONG).show();

adapter.remove(contact);
}})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(MainActivity.this,"Lỗi rồi :"+ e.toString(),Toast.LENGTH_LONG).show();
}
});
return false;
}
});
}

Xử lý cho Thêm Contact tương tự nha!

Đây là sourcode của chương trình: Tải ở đây

Chúc các bạn thành công

Phát triển ứng dụng cơ sở dữ liệu thời gian thực với Firebase-phần 3


Bài 1 các bạn đã biết cách kết nối và truy vấn Realtime Database Firebase, Bài 2 biết cách thêm mới dữ liệu vào Firbase. Trong bài này Tui sẽ hướng dẫn các bạn cách chỉnh sửa và xóa dữ liệu khỏi Realtime Database Firebase.

Tiếp tục mở lại Project ở bài 2, Ta bổ sung thêm 3 chức năng: Lấy chi tiết Cập nhật và xóa.

Ta tạo một màn hình mới tên là CapNhatContactActivity, bằng cách bấm chuột phải vào package/ chọn New / Chọn Activity/ chọn Empty Activity:

Sau khi chọn Empty Activity, ta nhập tên CapNhatContactActivity:

sau khi nhập xong, nhấn FINISH để tạo màn hình Cập nhật, thiết kế giao diện màn hình này như hình dưới đây:

Mục đích của màn hình này là:

– Truy vấn chi tiết 1 Contact trên Firebase dựa vào Contact ID được chọn trên ListView ở màn hình chính

– Cho phép Cập nhật

– Cho phép Xóa

XML Layout của màn hình này như sau:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".CapNhatContactActivity">
    <TextView
        android:id="@+id/textView0"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Nhập Contact Id:"
        android:textSize="25sp" />
    <EditText
        android:id="@+id/edtContactId"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Contact Id ở đây"
        android:inputType="textPersonName"
        android:textSize="25sp" />
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Nhập Ten:"
        android:textSize="25sp" />
    <EditText
        android:id="@+id/edtTen"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Tên ở đây"
        android:inputType="textPersonName"
        android:textSize="25sp" />
    <TextView
        android:id="@+id/textView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Nhập Email:"
        android:textSize="25sp" />
    <EditText
        android:id="@+id/edtEmail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Email ở đây"
        android:inputType="textEmailAddress"
        android:textSize="25sp" />
    <TextView
        android:id="@+id/textView3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Nhập Phone:"
        android:textSize="25sp" />
    <EditText
        android:id="@+id/edtPhone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Phone ở đây"
        android:inputType="phone"
        android:textSize="25sp" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnChinhSua"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cập Nhật"
            android:textSize="25sp" />
        <Button
            android:id="@+id/btnXoa"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Xóa"
            android:textSize="25sp" />
    </LinearLayout>
</LinearLayout>

Tiến hành bổ sung sự kiện cho ListView ở màn hình MainActivity:

lvContact.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        String data=adapter.getItem(position);
        String key=data.split("\n")[0];
        Intent intent=new Intent(MainActivity.this,CapNhatContactActivity.class);
        intent.putExtra("KEY",key);
        startActivity(intent);
    }
});

Vì ở màn hình chính Tui hiển thị mỗi dòng là các thông số Contact Id, Phone, Email, Name được nối nhau bằng ký tự xuống dòng \n. Nên ở đây khi có được dòng đang chọn, Tui dùng hàm split để tách ra, phần tử đầu tiên là Contact ID nên lấy index là [0]

Contact ID này sẽ được gửi qua màn hình CapNhatContactActivity, Tui đặt tên nó là KEY. Bên màn hình Cập nhật sẽ dựa vào KEY này để lấy thông tin chi tiết từ Firebase cũng như dùng cho cập nhật, xóa (Các bạn đừng thắc mắc là tại sao không truyền hết qua luôn để không phải mất công truy suất lại). Ở đây Tui có 2 lý do:

  • Lý do 1: Tui muốn hướng dẫn các bạn cách lấy thông tin chi tiết từ Firebase khi có Key
  • Lý do 2: Trong thực tế dữ liệu có rất nhiều thuộc tính, trong màn hình danh sách không phải tải hết về, mà chỉ tải một số thông tin quan trọng, do đó trong màn hình chí tiết ta mới tải hết về dựa vào Key

OK, giờ quay lại màn hình CapNhatContactActivity, coding để lấy chi tiết Contact:

package com.communityuni.realtimedatabasefirebase;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
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 com.google.firebase.database.ValueEventListener;
import java.util.HashMap;

public class CapNhatContactActivity extends AppCompatActivity {
    EditText edtId,edtTen,edtPhone,edtEmail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cap_nhat_contact);
        addControls();
        getContactDetail();
    }

    private void getContactDetail() {
        Intent intent=getIntent();
        final String key=intent.getStringExtra("KEY");
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
        DatabaseReference myRef = database.getReference("contacts");

        //truy suất và lắng nghe sự thay đổi dữ liệu
        //chỉ truy suất node được chọn trên ListView myRef.child(key)
        //addListenerForSingleValueEvent để lấy dữ liệu đơn
        myRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                try {
                    //nó trả về 1 DataSnapShot, mà giá trị đơn nên gọi getValue trả về 1 HashMap
                    HashMap<String,Object> hashMap= (HashMap<String, Object>) dataSnapshot.getValue();
                    //HashMap này sẽ có kích  thước bằng số Node con bên trong node truy vấn
                    //mỗi phần tử có key là name được định nghĩa trong cấu trúc Json của Firebase
                    edtId.setText(key);
                    edtTen.setText(hashMap.get("name").toString());
                    edtEmail.setText(hashMap.get("email").toString());
                    edtPhone.setText(hashMap.get("phone").toString());
                }
                 catch (Exception ex)
                {
                    Log.e("LOI_JSON",ex.toString());
                }
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w("LOI_CHITIET", "loadPost:onCancelled", databaseError.toException());
            }
        });
    }

    private void addControls() {
        edtId=findViewById(R.id.edtContactId);
        edtTen=findViewById(R.id.edtTen);
        edtPhone=findViewById(R.id.edtPhone);
        edtEmail=findViewById(R.id.edtEmail);
    }
}

Lưu ý trong sự kiện lấy chi tiết, Tui dùng addListenerForSingleValueEvent, nó khác với lấy danh sách Contact trong MainActivity nha (addValueEventListener).

Chạy lên ta có kết quả:

Khi chọn 1 Contact, ví dụ như Putin–> nó sẽ mò lên Realtime Database Firebase để lấy chi tiết  và trả về cho màn hình CapNhatContactActivity.

Tiếp tục bổ sung 2 sự kiện (dùng onClick XML) Cập nhật và Xóa cho màn hình Cập Nhật:

<Button
    android:id="@+id/btnChinhSua"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="xuLySua"
    android:text="Cập Nhật"
    android:textSize="25sp" />
<Button
    android:id="@+id/btnXoa"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="xuLyXoa"
    android:text="Xóa"
    android:textSize="25sp" />

Coding cho 2 sự kiện này trong màn hình CapNhatContactActivity như sau:

Sự kiện sửa:

public void xuLySua(View view) {
    String key=edtId.getText().toString();
    String phone=edtPhone.getText().toString();
    String name=edtTen.getText().toString();
    String email=edtEmail.getText().toString();
    FirebaseDatabase database = FirebaseDatabase.getInstance();
    //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
    DatabaseReference myRef = database.getReference("contacts");
    myRef.child(key).child("phone").setValue(phone);
    myRef.child(key).child("email").setValue(email);
    myRef.child(key).child("name").setValue(name);
    finish();
}

Ở trên biến key là ContactId, ta sửa giá trị các node bên trong của nó lần lượt có: Phone, email, name:

myRef.child(key).child(“phone”) -> truy suất tới node phone

myRef.child(key).child(“phone”).setValue(phone)–> đổi mới giá trị cho node phone.

Sự kiện Xóa:

public void xuLyXoa(View view) {
    String key=edtId.getText().toString();
    FirebaseDatabase database = FirebaseDatabase.getInstance();
    //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
    DatabaseReference myRef = database.getReference("contacts");
    myRef.child(key).removeValue();
    finish();
}

Ta dùng hàm removeValue() để xóa, hoặc setValue(null) nha. Còn muốn cập nhập/ xóa nhiều thì dùng hàm updateChildren, truyền vào là 1 HashMap.

Cuối cùng ta có coding đầy đủ của màn hình CapNhatContactActivity như sau:

package com.communityuni.realtimedatabasefirebase;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
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 com.google.firebase.database.ValueEventListener;
import java.util.HashMap;

public class CapNhatContactActivity extends AppCompatActivity {
    EditText edtId,edtTen,edtPhone,edtEmail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cap_nhat_contact);
        addControls();
        getContactDetail();
    }

    private void getContactDetail() {
        Intent intent=getIntent();
        final String key=intent.getStringExtra("KEY");
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
        DatabaseReference myRef = database.getReference("contacts");

        //truy suất và lắng nghe sự thay đổi dữ liệu
        //chỉ truy suất node được chọn trên ListView myRef.child(key)
        //addListenerForSingleValueEvent để lấy dữ liệu đơn
        myRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                try {
                    //nó trả về 1 DataSnapShot, mà giá trị đơn nên gọi getValue trả về 1 HashMap
                    HashMap<String,Object> hashMap= (HashMap<String, Object>) dataSnapshot.getValue();
                    //HashMap này sẽ có kích  thước bằng số Node con bên trong node truy vấn
                    //mỗi phần tử có key là name được định nghĩa trong cấu trúc Json của Firebase
                    edtId.setText(key);
                    edtTen.setText(hashMap.get("name").toString());
                    edtEmail.setText(hashMap.get("email").toString());
                    edtPhone.setText(hashMap.get("phone").toString());
                }
                 catch (Exception ex)
                {
                    Log.e("LOI_JSON",ex.toString());
                }
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w("LOI_CHITIET", "loadPost:onCancelled", databaseError.toException());
            }
        });
    }

    private void addControls() {
        edtId=findViewById(R.id.edtContactId);
        edtTen=findViewById(R.id.edtTen);
        edtPhone=findViewById(R.id.edtPhone);
        edtEmail=findViewById(R.id.edtEmail);
    }

    public void xuLySua(View view) {
        String key=edtId.getText().toString();
        String phone=edtPhone.getText().toString();
        String name=edtTen.getText().toString();
        String email=edtEmail.getText().toString();
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
        DatabaseReference myRef = database.getReference("contacts");
        myRef.child(key).child("phone").setValue(phone);
        myRef.child(key).child("email").setValue(email);
        myRef.child(key).child("name").setValue(name);
        finish();
    }
    public void xuLyXoa(View view) {
        String key=edtId.getText().toString();
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        //Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
        DatabaseReference myRef = database.getReference("contacts");
        myRef.child(key).removeValue();
        finish();
    }
}

Chạy phần mềm lên, giả sử chọn Contact có tên là Tám:

Ta bấm cập nhật, dữ liệu sẽ được update luôn trên Firebase Server, và bất kỳ client nào đang sài phần mềm này cũng thấy sự thay đổi đó.

Giả sử bạn nhấn nút Xóa, trên Firebase sẽ đánh dấu mà đỏ báo cho ta chuẩn bị xóa-> và xóa thành công:

Như vậy Tui đã hướng dẫn xong cách: lấy chi tiết, cập nhật và xóa, các bạn thực hành lại nha.

Đây là source code của phần này: Tải code ở đây

Bài sau Tui sẽ hướng dẫn phần nâng cao: dùng java model class, cũng như lắng nghe kết quả thực hiện, transaction… các bạn chú ý theo dõi.

Chúc các bạn thành công!

Phát triển ứng dụng cơ sở dữ liệu thời gian thực với Firebase-phần 2


phần 1 Tui đã hướng dẫn các bạn cách thức kết nối Android Studio tới Realtime Database Firebase,  cách thao tác Cơ sở dữ liệu trên console.firebase.google.com cũng như cách thức truy suất danh sách Contact từ Realtime Database Firebase xuống Client.

Ở bài này Tui sẽ hướng dẫn các bạn cách thức thêm mới dữ liệu từ Android lên Realtime Database. Tuy nhiên Tui vẫn sử dụng cách thức cơ bản nhất, khi nào qua hết tất cả các bài cơ bản, Tui sẽ trình bày phần nâng cao đó là sử dụng Model class, Transaction…. Còn những bài này chỉ cần các bạn hiểu cơ chế hoạt động, cách thức gọi lệnh là đủ rồi.

Tiếp tục mở lại Project đã được hướng dẫn trong phần 1.

Bổ sung Option Menu có 1 MenuItem là “Thêm Contact”  như hình dưới đây:

Để them Menu ta làm như sau:

Bấm chuột phải vào thư mục res/ chọn new/ chọn Directory:

Sau khi chọn Director, Android Studio sẽ cung cấp 1 màn hình yêu cầu ta đặt tên, Ta sẽ đặt tên Directory là: menu như hình trong màn hình dưới đây:

Đặt tên xong bấm OK, ta thấy thư mục menu được tạo vào Project:

Tiếp tục bấm chuột phải vào menu/ chọn New/ chọn menu resource file:

Android Studio sẽ cung cấp màn hình cho ta đặt tên menu cũng như các thông số cấu hình khác:

Để đơn giản, ta chỉ quan tâm tới phần File Name, đặt tên là : main_menu rồi bấm OK:

Ta tiến hành kéo Menu Item ra, đổi nhãn thành Thêm Contact, đặt id là mnuAdd.

đây là main_menu.xml:


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/mnuAdd"
android:title="Thêm Contact" />
</menu>

Trong coding của màn hình MainActivity, Ta Override hàm onCreateOptionsMenu ra để tạo Menu cho Activity:

Chỉ cần gõ onCreate thì Android Studio cũng gợi ý ra hàm onCreateOptionsMenu, ta chỉ cần Enter là nó xổ hết ra cho lẹ:

Sau đó ta tiến hành cập nhật Coding như dưới đây:


@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater=getMenuInflater();
menuInflater.inflate(R.menu.main_menu,menu);
return super.onCreateOptionsMenu(menu);
}

Tiếp tục bổ sung hàm xử lý người dùng lựa chọn MenuItem – onOptionsItemSelected, ta cũng chi cần gõ 1 vài ký tự đầu onOption… nó tự xổ ra và ta chọn cho lẹ:

Ta sẽ có coding như sau:


@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.mnuAdd) // kiểm tra người dùng chọn Menu Thêm Contact
{
//mở màn hình thêm ở đây (sẽ gọi sau)
}
return super.onOptionsItemSelected(item);
}

Giờ Ta thêm một màn hình mới để nhập liệu cho Contact: Bấm chuột phải vào Package/ chọn New/ chọn Activity/ chọn Empty Activity :

Màn hình tạo Activity hiển thị ra, ta tiến hành đặt tên cho màn hình:

Ở trên ta đặt: là ThemContactActivity rồi bấm Finish
Tiến hành thiết kế giao diện cho màn hình Thêm mới:

XML Layout của activity_them_contact.xml như sau:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".ThemContactActivity">
<TextView
android:id="@+id/textView0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Contact Id:"
android:textSize="25sp" />

<EditText
android:id="@+id/edtContactId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Contact Id ở đây"
android:inputType="textPersonName"
android:textSize="25sp" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Ten:"
android:textSize="25sp" />

<EditText
android:id="@+id/edtTen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Tên ở đây"
android:inputType="textPersonName"
android:textSize="25sp" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Email:"
android:textSize="25sp" />

<EditText
android:id="@+id/edtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Email ở đây"
android:inputType="textEmailAddress"
android:textSize="25sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nhập Phone:"
android:textSize="25sp" />

<EditText
android:id="@+id/edtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone ở đây"
android:inputType="phone"
android:textSize="25sp" />

<Button
android:onClick="xuLyThemMoi"
android:id="@+id/btnDongYThem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Đồng Ý Thêm"
android:textSize="25sp" />

</LinearLayout>

Để có thể lưu một dữ liệu mới từ Client lên Database Firebase thì không mấy khó khăn, do Google cung cấp cho ta phương thức đơn giản là: setValue(dữ_liệu). Nếu chưa tồn tại Name trên Firebase thì nó tự thêm mới, nếu Name đã tồn tại thì trở thành cập nhật. Một số loại dữ liệu mà Firebase hỗ trợ đó là:

  • String
  • Long
  • Double
    Boolean
  • Map<String, Object>
  • List<Object>

Object có thể là Java Object, ta đẩy luôn 1 đối tượng hoặc danh sách đối tượng lên Server Firebase được luôn nha (Sẽ trình bày chi tiết ở những bài sau).

Cái khó của phần này chắc chỉ là do ta mapping sai giữa cấu trúc JSON trên Realtime Database Firebase với các biến truyền vào trong Android mà thôi.

Chúng ta xem cái hình Tui chụp dưới này:

Hình trên là bộ 3: Giao diện tương tác – Coding – Reatime Database Firebase

Các bạn nhìn kỹ các mũi tên mà Tui  trỏ nha. Cấu trúc Cơ sở dữ liệu (JSON) trong Firebase như thế nào thì bạn phải truyền cho đúng nha.

Ở trên cấu trúc JSON ta có:

1 element contacts . Trong thẻ contacts  có rất nhiều element con contactX, Mỗi element con này lại có 3 Element con là: email, name, phone. Trong coding ta phải so khớp chính xác với các thành phần này nha. So Khớp chính xác thì tự nhiên dữ liệu sẽ được lưu thành công, và phía mobile client bất kỳ sẽ tự động cập nhật khi có dữ liệu thay đổi (Realtime)

Chi tiết Coding cho màn hình ThemContactActivity:


package com.communityuni.realtimedatabasefirebase;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

public class ThemContactActivity extends AppCompatActivity {
EditText edtId,edtTen,edtPhone,edtEmail;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_them_contact);
addControls();
}
private void addControls() {
edtId=findViewById(R.id.edtContactId);
edtTen=findViewById(R.id.edtTen);
edtPhone=findViewById(R.id.edtPhone);
edtEmail=findViewById(R.id.edtEmail);
}

public void xuLyThemMoi(View view) {
try {
FirebaseDatabase database = FirebaseDatabase.getInstance();
//Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
DatabaseReference myRef = database.getReference("contacts");
String contactId=edtId.getText().toString();
String ten = edtTen.getText().toString();
String phone = edtPhone.getText().toString();
String email = edtEmail.getText().toString();
myRef.child(contactId).child("phone").setValue(phone);
myRef.child(contactId).child("email").setValue(email);
myRef.child(contactId).child("name").setValue(ten);
finish();
}
catch (Exception ex)
{
Toast.makeText(this,"Error:"+ex.toString(),Toast.LENGTH_LONG).show();
}
}
}

Cuối cùng chỉnh sửa sự kiện nhấn MenuItem trong màn hình MainActivity:


@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.mnuAdd)
{
//mở màn hình thêm ở đây
Intent intent=new Intent(MainActivity.this,ThemContactActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}

Chạy lên Ta sẽ có kết quả như mong muốn: bất kỳ 1 Thiết bị nào thêm mới 1 Contact –> nó Sẽ được lưu trên RealTime Database Firebase –> Tất cả các thiết bị khác sẽ tự động cập nhật mới khi có dữ liệu thay đổi.

Đây là Coding đầy đủ của phần này: Link tải tại đây

Phần sau Tui sẽ trình bày cách Chỉnh sửa và Xóa dữ liệu khỏi RealTime Database Firebase cũng như lắng nghe kết quả thay công hay thất bại.

Chúc các bạn thành công.

Phát triển ứng dụng cơ sở dữ liệu thời gian thực với Firebase-phần 1


Realtime Database Firebase là một dịch vụ cơ sở dữ liệu thời gian thực hoạt động trên nền tảng đám mây được cung cấp bởi Google nhằm giúp các lập trình phát triển nhanh các ứng dụng có tương tác cơ sở dữ liệu (CRUD) một cách nhanh chóng và ngay tức thời (Realtime).

Thực ra ta cũng nên bày vẽ dịch Realtime Database Firebase ra Cơ sơ dữ liệu thời gian thực firebase

hay cũng không nên dịch Cloud ra chữ nền tảng đám mây. Cứ để Cloud là được rồi.

CRUD: Là 4 thao tác không thể thiếu với mọi ứng dụng có tương tác Cơ Sở Dữ liệu. C (Create- thêm mới), R (Retrieve – truy vấn xem dữ liệu), U (Update- cập nhật dữ liệu), D (Delete- xóa dữ liệu)

Khi có sự thay đổi dữ liệu trên Database Firebase thì ngay lập tức giao diện của bất kỳ thiết bị nào có sử dụng phần mềm này sẽ tự động cập nhật (gọi là Realtime):

Dữ liệu trên Database Firebase có định dạng JSON kiểu Gson.

Trong bài này Tui sẽ hướng dẫn các bạn cách sử dụng Firebase Database trong Android Studio như thế nào, và làm một ví dụ về hiển thị danh sách Danh bạ đẩy từ Firebase Server xuống.

Các bạn làm theo các bước hướng dẫn dưới đây (Tui hướng dẫn cho cả người chưa biết gì về Android nên sẽ làm chi tiết từ bước tạo Project):

Khởi động Android Studio:

Click Chọn “Start a new Android Studio Project”, lúc này cửa sổ yêu cầu tạo tên Project sẽ hiển thị ra, ta Đặt tên Project là RealtimeDatabaseFirebase:

Mục Application Name: Đặt tên cho ứng dụng RealtimeDatabaseFirebase

Mục Company Domain: Đặt tên theo domain của bạn, không có thì đặt đại nhưng phải viết thường hết nha, ví dụ ở trên Tui đặt là: communityuni.com

bản chất nó là package để tổ chức sắp xếp lại hệ thống các lớp trong Project, cũng là cơ sở để Google quản lý ứng dụng trên Google Play, Developer nữa.

Mục Project Location: Nơi lưu trữ dữ án

Lưu ý đừng có tick vào C++ hay Kotlin, vì bài hướng dẫn này Tui trình bày về Java

Sau cung cấp đủ các thông tin rồi thì bấm Next để qua bước tiếp theo:

Ở màn hình này ta chọn API là 26 nha rồi bấm Next:

Chọn Empty Activity rồi bấm Next

Bước này là thiết lập màn hình chính chạy: Activity Name là lớp Java để xử lý nghiệp vụ, Layout Name là giao diện cho màn hình đó, để mặc định vậy bấm FINISH

Như là ta đã tạo xong được Project trong Android Studio. Bây giờ bước tiếp theo là kết nối với Firebase để sử dụng Realtime Database Firebase. Ta làm như sau:

Vào menu Tools/ chọn Firbase, lúc này cửa sổ hướng dẫn cách sử Firebase sẽ được hiển thị ra ở góc phải của cửa sổ Android Studio:

Trong màn hình trên, Firebase cung cấp rất nhiều công cụ, tuy nhiên ta chỉ quan tâm tới Realtime Database thôi nha. Vì vậy ta nhấn vào Realtime Database:

Lúc này ta thấy nút “Save and retrieve data” hiển thị ra như hình trên. Bấm vào nó nha. Trình trợ giúp cung cấp cho ta 8 bước thao tác (bao gốm kết nối và hướng dẫn lập trình, ta dùng 2 -3 bước là đủ rồi):

bước 1: Connect your app to firebase
bước 2: Add the Realtime Database to our app
bước 3: Configure Firebase Database Rules
bước 4: Write to your database
bước 5: Read from your database
bước 6: Optional: Configure ProGuard
bước 7: Prepare for Launch
Bước 8: Next steps

Ở bước 1, ta nhấn vào nút “Connect to Firebase”, lúc này hệ thống sẽ yêu cầu chúng ta đăng nhập bằng tài khoản gmail:

Ta lựa chọn tài khoản email, ở trên có sẵn 2 email của Tui: duythanhcse@gmail.com và thanhtd@uel.edu.vn, nếu tui muốn làm các email khác thì nhấn vào Use another account
Sau khi chọn Email để đăng nhập, Google yêu cầu ta cung cấp một số quyền hạn như sau:

Ta nhấn Allow để tiếp tục, lúc này màn hình Developer của Android sẽ hiển thị ra:

Ở trên có 2 server: Firebase và Google Cloud, ta chọn Firebase ta có màn hình kết quả sau:

Chú ý góc phải trên cùng có nút “GO TO CONSOLE”, nhấn vào đây để vào trang quản trị của Firebase.

Lưu ý, Sau khi ta có màn hình trên, thì lúc này ở Project Android Studio sẽ tự động xuất hiện màn hình xác nhận kết nối Firebase (nhớ để ý để quay lại Android Studio xác nhận):

Phía trên là tên ứng dụng “RealtimeDatabaseFirebase”, Tui đăng nhập với email thanhtd@uel.edu.vn, phần existing chưa có vì ta mới làm. mục Country chọn Vietnam
sau đó nhấn nút “Connect to Firebase” để tiến hành kết nối:

Ta thấy màn hình nhỏ nhỏ ở góc phải bên dưới không, nó đang báo là đang két nối ứng dụng tới Firebase (Connecting app to Firebase). Ta chờ nha, chờ đợi trong hạnh phúc:

Khi kết nối thành công, bước 1 sẽ được bật đèn xanh như hình trên, và ta nhận được thông báo là thành công ở cửa sổ nhỏ nhỏ ở góc phải bên dưới Android Studio.

Một số lưu ý khi kết nối thất bại:

Giả sử ta gặp báo lỗi như hình dưới đây:

Thì có khả năng lỗi do các vấn đề sau:

– Mỗi một email như vậy, Google cho tạo khoảng 10 tới 12 kết nối Project, nên nó có thể thất bại nếu bạn đã vượt qua, lúc này tạo 1 email mới cho lẹ. Vì muốn xóa thì cũng phải chờ 1 tháng sau Google mới thực sự xác nhận xóa.

– Nếu không phải lỗi do vượt quá số lượng kết nối,  thì tiếp tục bấm lại nút “Connect to Firebase” lần nữa, nó xuất hiện ra thông báo này thì chọn Sync:

Hi vọng là các bạn không bị lỗi.

OK ta tiếp tục bước 2 nha.

Khi bước 1 thành công thì ta nhận được thông báo “Connected” màu xanh ở trên thấy không? tiếp theo ta nhấn vào “Add the Realtime Database to Your app” để đưa đưa cơ sở dữ liệu thời gian thực vào ứng dụng của mình:

Ta nhấn “Accept Changes” để hệ thống tự động đưa các thư viện vào build.gradle nha, nếu thành công ta sẽ thấy màu xanh được bật lên như dưới đây:

Bây giờ ta đợi Android studio Refresh nha, sau đó nếu có thông báo lỗi như dưới này:

Thì ta chỉ việc xóa :15.0.0 đằng sau đi nha (Tui có thử 16.0.1 hay 16.0.3 thấy ổn):

ta sửa:

implementation ‘com.google.firebase:firebase-database:16.0.3:15.0.0’

thành:

implementation ‘com.google.firebase:firebase-database:16.0.3’

rồi bấm Try Again lại:

nếu còn lỗi: Thì phải kiểm tra xem, coi chừng Android của bạn chưa cài Google Play Service, các bước kiểm tra và tải như sau:

Vào Menu Tools/ chọn SDK Manager:

Chọn  thẻ SDK Tools/ Tick vào Google Play Services rồi bấm  Apply chờ nó tải và cài đặt vào:

Ta chờ nó cài đặt Google Play Service nha, chờ nó cài xong là OK. Nếu mà vẫn bị lỗi thì chắc do ăn ở, mua máy mới luôn đi cho rồi.

Ta quay lại Website Firebase khi kết nối thành công để kiểm tra cũng như thao tác nha:

Nhìn thấy nút “GO TO CONSOLE” ở góc phải bên trên không? bấm vào nó:

Ta sẽ được dẫn tới màn hình dưới đây:

Ta thấy ứng dụng tên là RealtimeDatabaseFirebase xuất hiện ở trên không? nhấn vào nó (lưu ý là ở Android Studio kết nối thành công thì ở đây mới tự động thấy nó nha):

Hệ thống yêu cầu cấu hình chia sẻ dữ liệu, Ta bấm vào “Choose data sharing settings” chỗ dòng cảnh báo bên trên đó:

Ta tick chọn hết, tick I Accept… rồi nhấn FINISH

ta chờ hệ thống chạy một chút xíu sẽ xong và ra màn hình dưới đây:

Ta nhìn vào danh sách bên trái màn hình Website — > nhấn vào Database:

Sau khi nhấn vào Database, bên nội dung ta Kéo xuống nhìn thấy nhóm Realtime Database thì chọn “Create Database”:

Hệ thống cung cấp 2 mode:

  • Start In locked mode, dùng để chạy thực sử (khi đã release sản phẩm)
  • Start in Test mode, dùng cho Debug lúc mà ta đang phát triển phần mềm

Do ta đang test thì chọn start intest mode, khi nào xong thì chỉnh lại Locked mode sau:

Chọn Start in test mode xong thì bấm nút Enable nha. cấu hình thành công ta sẽ có màn hình với thông báo dưới đây:

Nó thông báo về bảo mật đó, vì ta đang test mà. Muốn chỉnh lại trong thẻ RULE:

OK, bây giờ ta nhập dữ liệu cho Database Realtime này. Có 2 cách nhập:

  • nhập trực tiếp vào màn hình Web
  • nhập bằng cách import 1 file JSON theo đúng cấu trúc

thấy chữ null ở trên không bạn? ta click vào chữ null rồi dán dữ liệu JSON theo đúng cấu trúc là OK:

Hoặc ở góc phải trên cùng nhấn vào …:

Có chức năng Export JSON là xuất dữ liệu ra file JSON, Import JSON là nhập 1 file dữ liệu JSON ở ngoài và hệ thống Database Realtime của Firebase.

Đây là cấu trúc của file dữ liệu ở trên, bạn lấy mẫu này cho lẹ nha,  dùng chức năng IMPORT JSON để lưu:

Tải JSON mẫu của Tui ở đây (realtimedatabasefirebase-cf3ad-export.json): https://www.mediafire.com/file/b5gsxfqal952ne6/realtimedatabasefirebase-cf3ad-export.json/file

Chọn Browse-> trỏ tải file tải về được, nhấn IMPORT -> Có kết quả như của Tui.

Như vậy ta đã làm xong bước kết nối Firebase, tạo cơ sở dữ liệu

Bước cuối cùng là ta truy suất Cơ sở dữ liệu Realtime này lên giao diện, thay đổi và quan sát nó cập nhật giao diện tức thời (Realtime):

Quay lại Android Studio:

Chính sửa Màn hình giao diện (activity_main.xml):


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">

<ListView
android:id="@+id/lvContact"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

Màn hình chỉ có 1 ListView duy nhất, đặt Id là lvContact.

Trong bài này ta dùng hiển thị cơ bản thôi nha, bài sau Tui hướng dẫn nâng cao hơn.

Sau đó coding trong MainActivity.java như sau:


package com.communityuni.realtimedatabasefirebase;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
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 com.google.firebase.database.ValueEventListener;

public class MainActivity extends AppCompatActivity {

ListView lvContact;
ArrayAdapter<String> adapter;
String TAG="FIREBASE";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1);
lvContact=findViewById(R.id.lvContact);
lvContact.setAdapter(adapter);
//lấy đối tượng FirebaseDatabase
FirebaseDatabase database = FirebaseDatabase.getInstance();
//Kết nối tới node có tên là contacts (node này do ta định nghĩa trong CSDL Firebase)
DatabaseReference myRef = database.getReference("contacts");
//truy suất và lắng nghe sự thay đổi dữ liệu
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
adapter.clear();
//vòng lặp để lấy dữ liệu khi có sự thay đổi trên Firebase
for (DataSnapshot data: dataSnapshot.getChildren())
{
//lấy key của dữ liệu
String key=data.getKey();
//lấy giá trị của key (nội dung)
String value=data.getValue().toString();
adapter.add(key+"\n"+value);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
}
}

Chạy lên và ta có dữ liệu hiển thị như mong muốn:

Các bạn lưu ý, giờ trong Website Firebase, bạn thêm sửa xóa dữ liệ thì lập tức bất kỳ thiết bị Mobile nào đang sử dụng phần mềm nó sẽ thấy sự thay đổi này và cập nhật thời gian thực liền.

Như vậy Tui đã hướng dẫn các bạn hoàn chỉnh các bước làm thế nào để tạo một Project Android sử dụng Cơ sở dữ liệu thời gian thực, thao tác Realtime Database, cách kết nối, lập trình…

Source code của bài này: https://www.mediafire.com/file/r1bihzvc91zpa3n/RealtimeDatabaseFirebase.rar/file

Chúc các bạn thành công

iFactory thông báo tuyển dụng lập trình viên


VIỆC LÀM CÔNG TY START-UP

1. Street Smart

Bạn có biết Street Smart là gì không?

STREET SMART

Nếu chưa biết thì chỉ cần lên Google là xong ngay thôi!

Thực tế thì các kỹ sư Street Smart có thu nhập và cơ hội thăng tiến cao hơn nhiều lần mặt bằng chung.

Để có Street Smart cách duy nhất là phải ở “Street”. Không ai ngồi nhà mà có thể hiểu rõ chuyện đường phố.

Start-up là một trong những môi trường tốt nhất để đào tạo Street Smart. Vì ở đó bạn phải xoay sở và tự làm mọi việc.

Nếu bạn nghe điều này mà thấy “sôi máu” hãy nộp đơn để gia nhập đôi ngũ iFactory.

2. Về iFactory

iFactory là một start up công nghệ. Chúng tôi đeo đuổi việc ứng dụng công nghệ vào các giải pháp quản trị nhà máy sản xuất.

Sau 3 năm phát triển, iFactory đã phát triển thành công các giải pháp:

  • Phần mềm quản trị sản xuất toàn diện trên Desktop và Mobile.
  • Phần mềm thống kê chuyên dụng cho sản xuất.
  • Phần mềm kết nối dữ liệu với hầu hết các thiết bị điện tử.
  • Công nghệ truyền dữ liệu Zigbee.
  • Công nghệ đo lường chính xác bằng camera.
  • Công nghệ kiểm tra lỗi bề mặt chính xác bằng camera.

3. Mô tả công việc

  • Năm đầu tiên lập trình C# – WPF.
  • Năm thứ 2 trở đi phát triển nghề nghiệp phù hợp với khả năng.

4. Chế độ phúc lợi

  • Môi trường làm việc mở.
  • Đồng nghiệp thân thiện, gắn bó và hỗ trợ.
  • Lương khởi điểm thoả thuận.
  • Thu nhập phát triển theo doanh thu công ty.
  • Chỗ ở ngay tại khu công nghệ phần mềm ITP.

5.Yêu cầu:

  • Kỹ sư IT mới tốt nghiệp hoặc sẵn sàng đi làm toàn thời gian.
  • Kỹ sư đã có 1~2 năm kinh nghiệm.
  • Không yêu cầu tiếng Anh nhưng cần biết tự nghiên cứu trên mạng.
  • Năng lực phù hợp với công việc IT.
  • Đam mê lập trình và công nghệ.
  • Thái độ làm việc nghiêm túc.

6. Thông tin nộp đơn

  • Email: ta@ifactory.vn (Mr. Toàn)
  • Nhận hồ sơ đến hết ngày 30.09.2018

[Quốc Khánh 02/09] Danh sách 9 bạn được tặng 14 khóa học Online


🔥🔥🔥Tin Nóng Hổi! Vừa Thổi Vừa Đọc!🔥🔥🔥
Sau hơn 1 tuần gay cấn, lọc hơn 300 Email gửi về có cùng ngày sinh với Đại Lễ 02/09. Thầy chốt danh sách 9 bạn được tặng toàn bộ các Khóa học online của Thầy, với tổng thời lượng các khóa học lên tới 264 giờ.
Thầy sẽ gửi danh sách mã kích hoạt qua Email cho 9 bạn may mắn!

Danh sách khóa học được tặng:


Các khóa học vẫn đang được giới thiệu trên http://communityuni.com/. Để được tư vấn miễn phí lộ trình Học Lập Trình các bạn hãy vào https://www.facebook.com/communityuni/ để Inbox nhé

Hiện tại Mua 2 cuốn sách Mobile [176k] vẫn còn chính sách tặng 1 Khóa Android Online:
Link đăng ký mua sách: 🌏https://goo.gl/UBneJr

Thầy Thanh.

[Quốc Khánh 02/09] Đăng ký nhận tặng 14 khoá học


📣📣📣Mừng Quốc Khánh 02/09📣📣📣

9 bạn đầu tiên có ngày sinh nhật 02/09 trên Chứng Minh Nhân hoặc Thẻ Căn Cước sẽ được tặng 14 khóa học với tổng thời lượng lên tới 264 giờ học liên tục:

Không có văn bản thay thế tự động nào.

👉Các Em đăng ký vào email: 💌[duythanhcse@gmail.com]
Yêu cầu định dạng Email:
1) Tiêu đề: [Đại Lễ 02/09] Đăng ký nhận tặng 14 khóa học
2) Nội dung: Cung cấp nội dung phù hợp (cách hành văn) + hình chụp của Chứng Minh Nhân Dân hoặc Thẻ Căn Cước có ngày sinh trùng với Đại Lễ 02/09.

Thầy sẽ chọn lọc 9 bạn may mắn đầu tiên và có định dạng email gửi phù hợp.

🔥🔥🔥Lưu ý: Sự kiện sẽ bắt đầu từ ngày 23/08/2018-> hết ngày 02/09/2018. Sau ngày 03/09/2018 Thầy sẽ gửi tặng khóa học qua Email.🔥🔥🔥

Thông tin chi tiết đề cương các khóa học tại đây:
🌏http://communityuni.com/

Ngoài ra trong sự kiện này những bạn không may mắn có ngày sinh trùng Đại Lễ 02/09. Thầy sẽ cung cấp khóa học theo các hướng lập trình:
😍A) Gói lập trình .NET chuyên nghiệp gồm chuỗi 5 môn học:
C++ -> Cấu trúc dữ liệu và Giải thuật -> C# Cơ bản -> C# nâng cao -> tăng tốc thiết kế phần mềm với LINQ
Tổng học phí: 500k💰
😍B) Gói lập trình Java chuyên nghiệp gồm chuỗi 4 khóa học:
C++ -> Cấu trúc dữ liệu và Giải thuật -> Java Cơ bản -> Java nâng cao
Tổng học phí: 400k💰
😍C) Gói Lập trình Mobile chuyên nghiệp gồm chuỗi 5 khóa học:
C++ -> Cấu trúc dữ liệu và Giải thuật -> Java Cơ bản -> Kotlin -> Android
Tổng học phí: 500k💰
😍D) Gói Lập trình full-stack chuyên nghiệp gồm chuỗi 9 khóa học:
C++ -> Cấu trúc dữ liệu và Giải thuật -> Java Cơ bản -> Kotlin -> Android -> C# cơ bản -> C# nâng cao -> tăng tốc thiết kết phần mềm với LINQ -> Webservice RESTful API

Tổng học phí: 900k💰
😍E) Mua 2 cuốn sách Mobile[176k] được tặng Khóa Android Online:
Link đăng ký: 🌏https://goo.gl/UBneJr

Để được tư vấn kỹ càng hơn, hãy LIKE và FOLLOW :
🌏https://www.facebook.com/communityuni/

🔥🔥🔥🔥🔥🔥🔥🔥🔥
Thông tin chuyển khoản khi mua các khóa học:
-STK: 0101146302
-Ngân hàng Đông á, chi nhánh gò vấp
-Chủ TK: Trần Duy Thanh
Chuyển Banking xong chát thông báo cho Thầy là Ok
🔥🔥🔥🔥🔥🔥🔥🔥🔥

Thầy Thanh
%d bloggers like this: