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:

[code language=”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=".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>

[/code]

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

[code language=”java”]

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();
}
}
}

[/code]

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:

[code language=”xml”]

<?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>

[/code]

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:

[code language=”java”]

@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);
}

[/code]

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:

[code language=”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=".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>

[/code]

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:

[code language=”java”]

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);
}
});

[/code]

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

[code language=”java”]

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();
}
}
}

[/code]

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!

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

  1. onDataChange sẽ load lại toàn bộ data dù ta chỉ đổi 1 dòng. Thầy tham khảo dùng FirebaseUI Database đi ạ. Chúc thầy sức khỏe ạ.

Leave a Reply