Tài liệu Kotlin Tiếng Việt

Theo nhiều lời đề nghị của mọi người, Tui soạn thảo lại các bài học lập trình Kotlin trên Blog https://duythanhcse.wordpress.com/kotlin/kotlin-co-ban-den-nang-cao/ thành Ebook với 271 trang. Hi vọng với tài liệu Kotlin Tiếng Việt này sẽ giúp các bạn dễ dàng hiểu được Kotlin.

Kotlin và Java là song kiếm hợp bích, để học tốt Kotlin thì theo Tui các bạn nên học tốt Java trước. Hai ngôn ngữ này sẽ tương hỗ cho nhau trong quá trình viết mã lệnh. Google đã công Kotlin trở thành ngôn ngữ chính thống cho việc triển khai các dự án Android, do đó tương lai nó có tiềm năng rất lớn. Các bạn cố gắng học tốt Kotlin, Tui đã chủ ý biên soạn theo thứ tự từ thấp lên cao do đó các bạn nên học theo từng bài. Nắm vững Kotlin tốt sẽ tạo cơ hội trong tương lai cho các bạn, vì tương lai sẽ có làn sóng mạnh mẽ về tuyển dụng lập trình viên Android với Kotlin.

Trong quá trình biên soạn sẽ không tránh khỏi những sai sót, quý độc giả vui lòng hoan hỉ lượng thứ và gửi thư góp ý về cho: Trần Duy Thanh (duythanhcse@gmail.com) để các phiên bản sau được chu đáo hơn.

Nếu quý độc giả có share và dùng làm tài liệu cho trung tâm, trường học… thì vui lòng ghi rõ nguồn gốc tài liệu này:

Link tải tài liệu Lập Trình Kotlin Tiếng Việt(6.16 MB, 271 trang)

Xin chân thành cảm ơn


📣📣Thông báo chuẩn bị ra khóa học mới “Lập Trình Kotlin toàn tập”📣📣
🏆Tiếp nối thành công với hơn 11.000 tài khoản tham gia học các khóa học:
1)Lập trình Android trong 6 tuần
2)Lập trình Cơ sở dữ liệu với Java
3)Lập trình C# cơ bản
4)Lập trình C# nâng cao
5)Lập trình LinQ
6)Lập trình Restful Webservice
👨‍🏫Thầy thông báo chuẩn bị ra khóa học mới “Lập Trình Kotlin toàn tập”, với gần 100 bài học, đầy đủ và chi tiết các kiến thức về Kotlin cùng với các bài tập rèn luyện phong phú… làm nền tảng xây dựng các ứng dụng cho Android, Web Service, Native…
📜Đề cương và một số bài học mẫu: 

[youtube https://www.youtube.com/watch?v=pbEdhrZ0E1o?list=PLmEUE4MG8_b4FTcScxHuXNRTBOxaiqcVX&w=560&h=315]

Giá gốc khóa học 599kĐể được ưu đãi với giá 399k ==>Các bạn sẽ nộp học phí trước ngày 15/08/2017, số lượng có hạn ưu tiên cho 100 bạn đầu tiên, thông tin chuyển khoản:

Chủ tài khoản: Trần Duy Thanh
Số tài khoản: 0101146302
Ngân hàng ĐÔNG Á – chi nhánh GÒ VẤP – TP. Hồ Chí Minh
Email Tư Vấn: duythanhcse@gmail.com / nguyenphuc9119@gmail.com
Sau khi chuyển khoản, các bạn vào http://communityuni.com/Home/XacNhanThanhToan chọn môn Kotlin rồi trong lời nhắn ghi “Đăng ký dành chỗ học Kotlin” để xác nhận

Cám ơn các bạn!
Chúc các bạn gặt hái được nhiều kết quả khi tham gia các khóa học


 

Trần Duy Thanh.

Bài 40-Kết xuất Executable cho Kotlin [Kết thúc khóa học Kotlin]

[polldaddy poll=9764234]

Chào các bạn!

Chúng ta Say Goodbye ngôn ngữ lập trình Kotlin ở đây nhé, Tui phải Busy cho nhiều tasks khác. Toàn bộ các bài giảng về Kotlin Tui đã tổng hợp trong link https://duythanhcse.wordpress.com/kotlin/kotlin-co-ban-den-nang-cao/ các bạn vào đây học nhé. Ráng học cho tốt vì sắp tới Android Studio 3.0 ra đời nó sẽ đính kém lập trình Kotlin For Android, Tui gọi tắt là KfA. Nếu bạn học tới bài 40 này tức là đã đi trước nhiều người một số bước rồi.

Theo Tui tham khảo từ nhiều nguồn, thì trong tương lai sẽ bùng nổ các dự án liên quan tới Machine Learning (Máy học). Có nhiều ngôn ngữ để lập trình cho Machine Learning chẳng hạn như Python, R, Matlab… Nhiều Đại Học lớn trên thế giới hiện nay đã dùng ngôn ngữ lập trình Python vào giảng dạy kỹ thuật lập trình thay thế cho C++ và Java… Trước đó Tui có viết được một số bài về Python https://duythanhcse.wordpress.com/python/ nhưng do Busy quá phải tạm Pause, Nếu các bạn còn trẻ, khỏe, nhanh nhẹn(xấu trai cũng được) thì hãy tiếp tục nghiên cứu Python, R để về sau có nhiều cơ hội tham gia các dự án Machine Learning.

OK OK OK

Giờ Tui kết thúc khóa học Kotlin với bài hướng dẫn cách Kết xuất Kotlin ra Jar file để người sử dụng có thể bấm  1 phát là chạy luôn mà không cần mở công cụ lập trình.

IntelliZ IDEA cung cấp sẵn cho chúng ta Tool để làm điều này, vô cùng đơn giản đến mức chúng ta không thể nghĩ ra được. Cách làm chi tiết như sau:

1)Bước 1: Chọn 1 Project nào đó, trong hướng dẫn này Tui chọn bài Quản Lý Sản Phẩm tại link https://duythanhcse.wordpress.com/2017/06/04/bai-38-thiet-ke-giao-dien-trong-kotlin-phan-4/

Tui hướng dẫn thêm 1 cách tạo class có chứa hàm main để chạy:

-Cách Cũ chúng ta đang làm (tập tin AppTestSanPhamUI.kt):

[code language=”groovy”]

package communityuni.com.test

import communityuni.com.ui.SanPhamUI

/**
* Created by cafe on 04/06/2017.
*/
fun main(args: Array) {
var gui:SanPhamUI= SanPhamUI(“Trần Duy Thanh- Chương trình quản lý Sản phẩm”)
gui.showWindow()
}

[/code]

-Cách mới Ta có thể tạo thành một lớp theo cấu trúc dùng companion object và notation @JvmStatic:

[code language=”groovy”]

package communityuni.com.test

import communityuni.com.ui.SanPhamUI

/**
* Created by cafe on 06/06/2017.
*/
class SanPhamApp {
companion object
{
@JvmStatic
fun main(args: Array) {
var gui: SanPhamUI = SanPhamUI(“Trần Duy Thanh- Chương trình quản lý Sản phẩm”)
gui.showWindow()
}
}
}

[/code]

Vì sao lại phải biết thêm cách mới này để chạy hàm main? Bạn cứ làm nhiều đi rồi sẽ biết (thiên cơ bất khả lộ)

2)Bước 2: Vào File/ chọn Project Structure:

3)Bước 3: Chọn artifacts/ bấm vào dấu + màu xanh / chọn JAR/ chọn From Modules with dependencies…

Lúc này màn hình yêu cầu chọn Main Class xuất hiện:

Ta bấm vào nút chọn Main Class:

ở trên bạn chọn cái nào cũng được (2 cách tạo hàm main mà Tui trình bày ở trên) rồi nhấn nút OK

Bấm OK tiếp:

Mục 1 là tên jar được tạo ra, mục 2 là nơi lưu trữ Jar này. Ta chọn OK để quay lại màn hình chính

Tiến hành chạy lại phần mềm ta thấy nó tạo ra thư mục artifacts trong out folder:

Vào bên trong artifacts có thư mục lưu file jar đó là HocGUIPhan4_jar:

Vào bên trong thư mục này ta thấy file jar:

Bạn double click để chạy nó lên:

Bạn thấy nó mất tiêu mấy cái hình rồi đúng không? vì Project này Tui hướng dẫn trước đó có hình ảnh, các hình ảnh được lưu trong thư mục hinh, bây giờ bạn chỉ cần sao chép thư mục đó vào ngay chỗ file jar này là OK:

Bây giờ chạy lại file jar ta có kết quả:

Hình ảnh đã được hiển thị lên===> Ngon như cơm mẹ nấu đúng không các bạn

Giờ thử vào Menu Hệ thống/ chọn mở File bất kỳ xem nó hiển thị được không nhé:

Như vậy là đã tải file thành công.

Bạn đã hoàn thành các bước để tạo file Jar trong Intellij IDEA.

Các bạn cố gắng học tốt các ngôn ngữ lập trình nhé, hãy tự đào tạo mình để có nhiều kiến thức về công nghệ. Tạo ra nhiều cơ hội trong tương lai để tìm tới những Công ty có cơ hội làm việc tốt hơn, lương bổng ổn định hơn.

Để học lập trình tốt các bạn phải chịu khó cày, học ngày học đêm và bắt buộc phải Practice thật nhiều.

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

Trần Duy Thanh (http://communityuni.com/)

Bài 39-Thiết kế giao diện trong Kotlin – phần 5

[polldaddy poll=9764234]

Chúc mừng các bạn đã vượt qua 4 cửa ải GUI trong Kotlin, đây là cửa ải cuối cùng Tui muốn các bạn vượt qua. Phần này Tui sẽ nói về JTree, một trong những Control phổ biến được dùng rất nhiều trong các dự án để hiển thị dữ liệu dạng phân cấp cây thư mục:

Bài học này các bạn sẽ biết cách tạo 1 JTree, đưa được DefaultMutableTreeNode vào JTree. Đặc biệt sử dụng lập trình Hướng Đối Tượng để đưa dữ liệu lên Cây cối này, cũng như biết cách xử lý sự kiện khi người dùng nhấn vào từng Node trên Cây.

Bạn tạo một Project có cấu trúc như sau:

Lớp Nhân Viên (NhanVien) có cấu trúc (Mã, tên):

[code language=”groovy”]

package communityuni.com.model

/**
* Created by cafe on 04/06/2017.
*/
class NhanVien {
var Ma:Int=0
var Ten:String=””
constructor()
constructor(Ma: Int, Ten: String) {
this.Ma = Ma
this.Ten = Ten
}

override fun toString(): String {
return Ten
}
}

[/code]

Lớp Phòng Ban (PhongBan) có cấu trúc (mã, tên, danh sách nhân viên):

[code language=”groovy”]

package communityuni.com.model

/**
* Created by cafe on 04/06/2017.
*/
class PhongBan {
var Ma:Int=0
var Ten:String=””
var NhanViens:MutableList<NhanVien> = mutableListOf()
constructor()
constructor(Ma: Int, Ten: String) {
this.Ma = Ma
this.Ten = Ten
}

override fun toString(): String {
return Ten
}
}

[/code]

Tiếp theo thiết kế màn hình NhanSuPanel.form như hình dưới:

Chỉnh pnMain có layout manager là BorderLayout, kéo JScrollPane vào phần Center => sau đó kéo JTree vào bên trong JScrollPane.

Nhớ chọn JTree rồi checked Custom Create để nó ra hàm createUIComponents()

Sau đó Chỉnh sửa source code của NhanSuPanel, giả lập một số dữ liệu:

[code language=”java”]

package communityuni.com.ui;

import communityuni.com.model.NhanVien;
import communityuni.com.model.PhongBan;

import javax.swing.*;

import javax.swing.tree.DefaultMutableTreeNode;
import java.util.ArrayList;
import java.util.List;

/**
* Created by cafe on 04/06/2017.
*/
public class NhanSuPanel {
private JPanel pnMain;
private JTree treePhongBan;
DefaultMutableTreeNode root;
List<PhongBan>database=null;

private void loadSampleDatabaseToGUI()
{
root=new DefaultMutableTreeNode(“http://communityuni.com”);
treePhongBan=new JTree(root);
for (int i=0;i<database.size();i++)
{
PhongBan pb=database.get(i);
DefaultMutableTreeNode pbNode=new DefaultMutableTreeNode(pb);
root.add(pbNode);
for (int j=0;j<pb.getNhanViens().size();j++)
{
NhanVien nv=pb.getNhanViens().get(j);
DefaultMutableTreeNode nvNode=new DefaultMutableTreeNode(nv);

pbNode.add(nvNode);
}
}
}
private void createSampleDatabase()
{
database=new ArrayList<>();
PhongBan pns=new PhongBan(1,”Phòng tổ chức hành chánh”);
PhongBan phc=new PhongBan(2,”Phòng Kế hoạch tài chính”);
PhongBan pkhcn=new PhongBan(3,”Phòng Khách hàng cá nhân”);
PhongBan pkhdn=new PhongBan(4,”Phòng Khách hàng doanh nghiệp”);
database.add(pns);database.add(phc);database.add(pkhcn);database.add(pkhdn);
pns.getNhanViens().add(new NhanVien(1,”Trần Thị Giải”));
pns.getNhanViens().add(new NhanVien(2,”Nguyễn Thị Thoát”));
pns.getNhanViens().add(new NhanVien(3,”Hồ Văn Hạnh”));
pns.getNhanViens().add(new NhanVien(4,”Đinh Thị Phúc”));
phc.getNhanViens().add(new NhanVien(5,”Trần Văn Yên”));
phc.getNhanViens().add(new NhanVien(6,”Hoàng thị Giấc”));
phc.getNhanViens().add(new NhanVien(7,”Nguyễn Ngọc Ngàn”));
phc.getNhanViens().add(new NhanVien(8,”Ma Văn Thu”));
}

public JPanel getPnMain()
{
return pnMain;
}

private void createUIComponents() {
createSampleDatabase();
loadSampleDatabaseToGUI();
}
}

[/code]

Chạy chương trình lên ta sẽ có giao diện như Tui cung cấp ở trên.

Bây giờ để gán sự kiện cho JTree ta bấm chuột phải vào JTree rồi chọn Create Listener:

Sau đó chọn TreeSelectionListener:

Chọn valueChanged trong màn hình Select Methods to Implement:

Nhấn OK ta thấy sự kiện xuất hiện:

[code language=”java”]

public NhanSuPanel() {
treePhongBan.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {

}
});
}

[/code]

Bổ sung coding để kiểm tra Node nào được nhấn:

[code language=”java”]

public NhanSuPanel() {
treePhongBan.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node= (DefaultMutableTreeNode) treePhongBan.getLastSelectedPathComponent();
switch (node.getLevel())
{
case 0:
JOptionPane.showMessageDialog(null,”Bạn nhấn Root=”+node.getUserObject());
break;
case 1:
PhongBan pb= (PhongBan) node.getUserObject();
JOptionPane.showMessageDialog(null,pb.getTen());
break;
case 2:
NhanVien nv= (NhanVien) node.getUserObject();
JOptionPane.showMessageDialog(null,nv.getTen());
break;
}
}
});
}

[/code]

Như vậy ta dùng treePhongBan.getLastSelectedPathComponent(); để biết được Node nào được chọn, dùng node.getLevel() để lấy chính xác cấp (thực ra là lấy đối tượng), ứng với mỗi level ta sẽ kiểm tra để lấy Đối tượng.

Như vậy Tui đã hướng dẫn xong cách dùng JTree trong Kotlin. các bạn nhớ kế hợp nó với JTable để hiển thị chi tiết dữ liệu.

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/goj6bdooxe2h3bq/HocGUIPhan5.rar

Hẹn gặp các bạn ở cuối cùng, bài 40 của khóa học Kotlin này.

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

Trần Duy Thanh (http://communityuni.com/)

Bài 38-Thiết kế giao diện trong Kotlin – phần 4

[polldaddy poll=9764234]

Bài 37-Thiết kế giao diện trong Kotlin – phần 3 bạn đã xây dựng được một phần mềm quản lý Sản phẩm nhưng chưa liên quan tới OOP, tương tác File , JMenu và JFileChooser. Trong bài này Tui sẽ tiếp tục phát triển bài 37, nên bạn nào chưa học bài 37 thì tự vào học để lấy Code tiếp tục phát triển nhé, giao diện sẽ bổ sung thêm JMenu để lưu file và mở File:

Bài này Tui bổ sung thêm các Menu Lưu File và Mở file với 4 loại mà chúng ta đã được học trước đó (Text File, Serialize File, XML file,JSon File) cũng như các Icon cho các Button.

Chương trình sẽ dùng JFileChooser để cho phép người dùng chỉ định nơi lưu trữ, đặc biệt phải dùng lập trình hướng đối tượng để xử lý. Bài học này khá khó nên các bạn phải chú ý làm theo hướng dẫn và chịu khó suy luận.

Bây giờ Tui hướng dẫn chi tiết cách thực hiện:

Trước tiên bạn lôi cái Project làm ở bài 37 ra, bổ sung thêm danh sách các hình ảnh sau(bạn tạo một thư mục hinh, các hình bạn tự tải rồi lưu vào đây, lấy đại cho nó lành):

Bổ sung ICon cho các Button trong SanPhamPanel bằng cách tạo một hàm createICon() có coding như sau(Tui lược bỏ các coding cũ, chút nữa sẽ có coding đầy đủ):

[code language=”java”]

public SanPhamPanel() {
//Tui hide coding …. cho dễ nhìn
createICon();
}

private void createICon() {
btnLuu.setIcon(new ImageIcon(“hinh/add.png”));
btnTiep.setIcon(new ImageIcon(“hinh/clear.png”));
btnXoa.setIcon(new ImageIcon(“hinh/delete.png”));
btnThoat.setIcon(new ImageIcon(“hinh/exit.png”));
}

[/code]

Tiếp tục vào SanPhamUI để bổ sung các Menu và Icon cho nó (Tui lược bỏ các coding cũ, chút nữa sẽ có coding đầy đủ):

[code language=”java”]

public class SanPhamUI extends JFrame {
JMenuBar mnuBar;
JMenu mnuSystem;
JMenu mnuSystemSave;
JMenuItem mnuSystemSaveTextFile;
JMenuItem mnuSystemSaveSerializeFile;
JMenuItem mnuSystemSaveXMLFile;
JMenuItem mnuSystemSaveJSonFile;
JMenu mnuSystemOpen;
JMenuItem mnuSystemOpenTextFile;
JMenuItem mnuSystemOpenSerializeFile;
JMenuItem mnuSystemOpenXMLFile;
JMenuItem mnuSystemOpenJSonFile;
JMenuItem mnuSystemExit;
public SanPhamUI(String title)
{
super(title);
setContentPane(new SanPhamPanel().getPnMain());
createMenu();
createICon();
}
public void createMenu()
{
mnuBar=new JMenuBar();
setJMenuBar(mnuBar);
mnuSystem=new JMenu(“Hệ thống”);
mnuBar.add(mnuSystem);

mnuSystemSave=new JMenu(“Lưu file”);
mnuSystem.add(mnuSystemSave);
mnuSystemSaveTextFile=new JMenuItem(“Text file”);
mnuSystemSave.add(mnuSystemSaveTextFile);
mnuSystemSave.addSeparator();
mnuSystemSaveSerializeFile=new JMenuItem(“Serialize file”);
mnuSystemSave.add(mnuSystemSaveSerializeFile);
mnuSystemSave.addSeparator();
mnuSystemSaveXMLFile=new JMenuItem(“XML file”);
mnuSystemSave.add(mnuSystemSaveXMLFile);
mnuSystemSave.addSeparator();
mnuSystemSaveJSonFile=new JMenuItem(“JSON file”);
mnuSystemSave.add(mnuSystemSaveJSonFile);

mnuSystem.addSeparator();

mnuSystemOpen=new JMenu(“Đọc File”);
mnuSystem.add(mnuSystemOpen);
mnuSystemOpenTextFile=new JMenuItem(“Text file”);
mnuSystemOpen.add(mnuSystemOpenTextFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenSerializeFile=new JMenuItem(“Serialize file”);
mnuSystemOpen.add(mnuSystemOpenSerializeFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenXMLFile=new JMenuItem(“XML file”);
mnuSystemOpen.add(mnuSystemOpenXMLFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenJSonFile=new JMenuItem(“JSON file”);
mnuSystemOpen.add(mnuSystemOpenJSonFile);

mnuSystem.addSeparator();

mnuSystemExit=new JMenuItem(“Thoát”);
mnuSystem.add(mnuSystemExit);
}
private void createICon() {
mnuSystem.setIcon(new ImageIcon(“hinh/system.png”));
mnuSystemSave.setIcon(new ImageIcon(“hinh/save.png”));
mnuSystemSaveTextFile.setIcon(new ImageIcon(“hinh/textfile.png”));
mnuSystemSaveSerializeFile.setIcon(new ImageIcon(“hinh/serializefile.png”));
mnuSystemSaveXMLFile.setIcon(new ImageIcon(“hinh/xmlfile.png”));
mnuSystemSaveJSonFile.setIcon(new ImageIcon(“hinh/jsonfile.png”));

mnuSystemOpen.setIcon(new ImageIcon(“hinh/open.png”));
mnuSystemOpenTextFile.setIcon(new ImageIcon(“hinh/textfile.png”));
mnuSystemOpenSerializeFile.setIcon(new ImageIcon(“hinh/serializefile.png”));
mnuSystemOpenXMLFile.setIcon(new ImageIcon(“hinh/xmlfile.png”));
mnuSystemOpenJSonFile.setIcon(new ImageIcon(“hinh/jsonfile.png”));

mnuSystemExit.setIcon(new ImageIcon(“hinh/exit.png”));
}
}

[/code]

Khi bạn bổ sung xong các coding ở trên thì sẽ có được giao diện như Tui yêu cầu ở trên.

Bây giờ Ta xử lý Coding hướng đối tượng Kotlin cho Sản Phẩm (tạo class SanPham), và 4 loại tập tin (Tui sẽ viết mô hình lớp kế thừa dẫn xuất từ Interface).

Trước tiên bạn tạo một package tên là communityuni.com.model, có chứa Lớp Sản phẩm với cấu trúc như sau:

[code language=”groovy”]

package communityuni.com.model

import java.io.Serializable

/**
* Created by cafe on 04/06/2017.
*/
class SanPham:Serializable {
private var ma:String=””;
private var ten:String=””;
private var gia:Double=0.0;
constructor()
constructor(ma: String, ten: String, gia: Double) {
this.ma = ma
this.ten = ten
this.gia = gia
}
public var Ma:String
get() = ma
set(value) {ma=value}
public var Ten:String
get() = ten
set(value) {ten=value}
public var Gia:Double
get() = gia
set(value) {gia=value}
}
override fun toString(): String {
return “$ma\t$ten\t$gia”
}
[/code]

Để xử lý lưu và đọc tập tin ta tạo package communityuni.com.io, tạo một interface FileFactory bằng Kotlin như hình dưới đây:

[code language=”groovy”]

package communityuni.com.io

import communityuni.com.model.SanPham

/**
* Created by cafe on 04/06/2017.
*/
interface FileFactory {
/**
* @author Trần Duy Thanh
* @param data: Dữ liệu là Danh sách sản phẩm muốn lưu
* @param path: Đường dẫn lưu trữ
* @return true nếu lưu thành công, false nếu lưu thất bại
*/
fun LuuFile(database:MutableList,path:String):Boolean
/**
* @author Trần Duy Thanh
* @param path:đường dẫn muốn đọc dữ liệu
* @return Danh sách sản phẩm MutableList
*/
fun DocFile(path:String):MutableList
}

[/code]

Sau Đó bạn tạo 4 Lớp để xử lý 4 loại tập tin, các lớp này bạn đã được học ở các bài: Text File, Serialize File, XML file,JSon File.

Lớp TextFileFactory dẫn xuất từ Interface FileFactory ở trên để lưu và đọc TextFile:

Chú ý khi bạn cho dẫn xuất từ Interface thì như lý thuyết đã học, bạn phải Override toàn bộ phương thức trừu tượng trong Interface nhé (chỉ cần nhấn tổ hợp phím Alt+ Enter nó sẽ tự động làm giùm ta):

[code language=”groovy”]

package communityuni.com.io

import communityuni.com.model.SanPham
import java.io.*

/**
* Created by cafe on 04/06/2017.
*/
class TextFileFactory:FileFactory {
override fun LuuFile(database: MutableList, path: String): Boolean {
try {
val fos = FileOutputStream(path)
val osw = OutputStreamWriter(fos, “UTF-8”)
val bw = BufferedWriter(osw)
for (sp in database) {
bw.write(sp.toString());
bw.newLine();
}
bw.close();
osw.close();
fos.close();
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}

override fun DocFile(path: String): MutableList {
var data:MutableList = mutableListOf()
try {
val fis = FileInputStream(path)
val isr = InputStreamReader(fis, “UTF-8”)
val br = BufferedReader(isr)

var line = br.readLine()
while (line != null) {
var arr = line.split(“\t”)
if (arr.size == 3) {
var sp: SanPham = SanPham()
sp.Ma = arr[0]
sp.Ten = arr[1]
sp.Gia = arr[2].toDouble()
data.add(sp)
}
line = br.readLine()
}
br.close()
isr.close()
fis.close()
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return data
}
}

[/code]

Tương tự cho Serialize File:

[code language=”groovy”]

package communityuni.com.io
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import communityuni.com.model.SanPham

/**
* Created by cafe on 04/06/2017.
*/
class SerializeFileFactory : FileFactory{
override fun LuuFile(database: MutableList, path: String): Boolean {
try {
var fos=FileOutputStream(path);
var oos=ObjectOutputStream(fos);
oos.writeObject(database);
oos.close();
fos.close();
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}
override fun DocFile(path: String): MutableList {
var database:MutableList = mutableListOf()
try
{
var fis=FileInputStream(path);
var ois=ObjectInputStream(fis);
var obj=ois.readObject();
database= obj as MutableList;
ois.close();
fis.close();
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return database
}
}

[/code]

Tương tự với lớp Kotlin XML File:

[code language=”groovy”]

package communityuni.com.io

import communityuni.com.model.SanPham
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.TransformerFactory
import org.w3c.dom.Element
/**
* Created by cafe on 04/06/2017.
*/
class XMLFileFactory:FileFactory {
override fun LuuFile(database: MutableList, path: String): Boolean {
try
{
val docFactory = DocumentBuilderFactory.newInstance()
val docBuilder = docFactory.newDocumentBuilder()
// root elements
val doc = docBuilder.newDocument()
val rootElement = doc.createElement(“SanPhams”)
doc.appendChild(rootElement)
for(sp in database)
{
val sanPhamElement = doc.createElement(“SanPham”)
val maElement=doc.createElement(“Ma”)
maElement.textContent=sp.Ma
sanPhamElement.appendChild(maElement)
val tenElement=doc.createElement(“Ten”)
tenElement.textContent=sp.Ten
sanPhamElement.appendChild(tenElement)
val giaElement=doc.createElement(“Gia”)
giaElement.textContent=sp.Gia.toString()
sanPhamElement.appendChild(giaElement)
rootElement.appendChild(sanPhamElement);
}
// write the content into xml file
val transformerFactory = TransformerFactory.newInstance()
val transformer = transformerFactory.newTransformer()
val source = DOMSource(doc)

val result = StreamResult(File(path).absolutePath)

// Output to console for testing
// StreamResult result = new StreamResult(System.out);
transformer.transform(source, result)
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}

override fun DocFile(path: String): MutableList {
var database:MutableList = mutableListOf()
try {
//Get the DOM Builder Factory
val factory = DocumentBuilderFactory.newInstance()

//Get the DOM Builder
val builder = factory.newDocumentBuilder()

//Load and Parse the XML document
//document contains the complete XML as a Tree.
val xmlfile = File(path)

val document = builder.parse(xmlfile)

//Iterating through the nodes and extracting the data.
val nodeList = document.documentElement.childNodes

for (i in 0..nodeList.length – 1) {

//We have encountered an tag.
val node = nodeList.item(i)
if (node is Element) {
val sp = SanPham()
val childNodes = node.getChildNodes()
for (j in 0..childNodes.getLength() – 1) {
val cNode = childNodes.item(j)

//Identifying the child tag of employee encountered.
if (cNode is Element) {
val content = cNode.getLastChild().getTextContent().trim()
when (cNode.getNodeName()) {
“Ma” -> sp.Ma= content
“Ten” -> sp.Ten= content
“Gia” -> sp.Gia= content.toDouble()
}
}
}
database.add(sp)
}
}
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return database
}
}

[/code]

Cuối cùng tới lớp JSon File :

[code language=”groovy”]

package communityuni.com.io

import communityuni.com.model.SanPham
import com.google.gson.Gson
import java.io.FileWriter
import java.io.FileReader
import com.google.gson.reflect.TypeToken
/**
* Created by cafe on 04/06/2017.
*/
class JSonFileFactory:FileFactory {
override fun LuuFile(database: MutableList, path: String): Boolean {
try {
val gs= Gson()
val file=FileWriter(path)
gs.toJson(database,file)
file.close()
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}

override fun DocFile(path: String): MutableList {
var database:MutableList = mutableListOf()
try
{
val gson = Gson()
var file=FileReader(path)
database = gson.fromJson<MutableList>(file,
object : TypeToken<MutableList>()
{
}.type
)
file.close()
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return database
}
}

[/code]

Như vậy là bạn đã tạo xong các lớp mô hình, bây giờ ta tiến hành xử lý nghiệp vụ.

vào SanPhamPanel, bổ sung thêm đối tượng để lưu danh sách Sản phẩm, viết lại hàm thêm, hiển thị lên JTable cũng như hiển thị chi tiết:

Trong Lớp SanPhamPanel bổ sung biến:

[code language=”java”]

Listdatabase=new ArrayList();

[/code]

Sửa lại sự kiện thêm sản phẩm:

[code language=”java”]

btnLuu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Vectorvector=new Vector();
vector.add(txtMa.getText());
vector.add(txtTen.getText());
vector.add(txtDonGia.getText());
tableModelSanPham.addRow(vector);
//bổ sung thêm ở đây:
SanPham sp=new SanPham();
sp.setMa(txtMa.getText());
sp.setTen(txtTen.getText());
sp.setGia(Double.parseDouble(txtDonGia.getText()));
database.add(sp);
}
});

[/code]

Sửa lại đoạn lệnh cho sự kiện xóa:

[code language=”java”]

btnXoa.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(tblSanPham.getSelectedRow()>=0)
{
tableModelSanPham.removeRow(tblSanPham.getSelectedRow());
database.remove(tblSanPham.getSelectedRow());
}
}
});

[/code]

Bổ sung thêm hàm để hiển thị toàn bộ danh sách Sản phẩm từ biến database lên JTable:

[code language=”java”]

public void showDataOnJTable()
{
tableModelSanPham.setRowCount(0);
for(int i=0;i<database.size();i++)
{
SanPham sp=database.get(i);
Vectorvector=new Vector();
vector.add(sp.getMa());
vector.add(sp.getTen());
vector.add(sp.getGia()+””);
tableModelSanPham.addRow(vector);
}
}

[/code]

Vậy cuối cùng ta có cấu trúc coding của lớp SanPhamPanel này như sau:

[code language=”java”]

package communityuni.com.ui;

import communityuni.com.model.SanPham;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
* Created by cafe on 04/06/2017.
*/
public class SanPhamPanel {
private JPanel pnMain;
private JTable tblSanPham;
private DefaultTableModel tableModelSanPham;
private JButton btnLuu;
private JButton btnTiep;
private JButton btnXoa;
private JButton btnThoat;
private JTextField txtMa;
private JTextField txtTen;
private JTextField txtDonGia;

Listdatabase=new ArrayList();

public SanPhamPanel() {
btnTiep.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txtMa.setText(“”);
txtTen.setText(“”);
txtDonGia.setText(“”);
txtMa.requestFocus();
}
});
btnThoat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnLuu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Vectorvector=new Vector();
vector.add(txtMa.getText());
vector.add(txtTen.getText());
vector.add(txtDonGia.getText());
tableModelSanPham.addRow(vector);
//bổ sung thêm ở đây:
SanPham sp=new SanPham();
sp.setMa(txtMa.getText());
sp.setTen(txtTen.getText());
sp.setGia(Double.parseDouble(txtDonGia.getText()));
database.add(sp);
}
});
tblSanPham.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
int row= tblSanPham.getSelectedRow();
String ma=tblSanPham.getValueAt(row,0).toString();
String ten=tblSanPham.getValueAt(row,1).toString();
double gia=Double.parseDouble(tblSanPham.getValueAt(row,2).toString());
txtMa.setText(ma);
txtTen.setText(ten);
txtDonGia.setText(gia+””);
}
});
tblSanPham.addMouseListener(new MouseAdapter() {
});
btnXoa.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(tblSanPham.getSelectedRow()>=0)
{
tableModelSanPham.removeRow(tblSanPham.getSelectedRow());
database.remove(tblSanPham.getSelectedRow());
}
}
});
createICon();
}
public void showDataOnJTable()
{
tableModelSanPham.setRowCount(0);
for(int i=0;i<database.size();i++)
{
SanPham sp=database.get(i);
Vectorvector=new Vector();
vector.add(sp.getMa());
vector.add(sp.getTen());
vector.add(sp.getGia()+””);
tableModelSanPham.addRow(vector);
}
}
private void createICon() {
btnLuu.setIcon(new ImageIcon(“hinh/add.png”));
btnTiep.setIcon(new ImageIcon(“hinh/clear.png”));
btnXoa.setIcon(new ImageIcon(“hinh/delete.png”));
btnThoat.setIcon(new ImageIcon(“hinh/exit.png”));
}

private void createUIComponents() {
tableModelSanPham=new DefaultTableModel();
tableModelSanPham.addColumn(“Mã Sản Phẩm”);
tableModelSanPham.addColumn(“Tên Sản Phẩm”);
tableModelSanPham.addColumn(“Đơn Giá Sản Phẩm”);
tblSanPham=new JTable(tableModelSanPham);
}
public JPanel getPnMain()
{
return pnMain;
}
}

[/code]

Bây giờ ta vào SanPhamUI để xử lý cho các Menu Lưu và đọc File, bổ sung thêm 2 biến cho FileFactory và JFileChooser, SanPhamPanel:

[code language=”java”]

FileFactory fileFactory;
JFileChooser fileChooser=new JFileChooser();
SanPhamPanel sanPhamPanel;

[/code]

Trong Constructor của SanPhamUI sửa lại chỗ setContentPane và gọi hàm createEvent(hàm này do ta tạo thêm để gán sự kiện cho các Menu):

[code language=”java”]

public SanPhamUI(String title)
{
super(title);
sanPhamPanel=new SanPhamPanel();
setContentPane(sanPhamPanel.getPnMain());
createMenu();
createICon();
creatEvent();
}

private void creatEvent() {
mnuSystemSaveTextFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây

}
});
}

[/code]

Coding chi tiết cho sự kiện lưu TextFile:

[code language=”java”]

private void creatEvent() {
mnuSystemSaveTextFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây
fileFactory=new TextFileFactory();
if(fileChooser.showSaveDialog(null)==JFileChooser.APPROVE_OPTION)
{
boolean kq=fileFactory.LuuFile(sanPhamPanel.database,fileChooser.getSelectedFile().getAbsolutePath());
if(kq==true)
{
JOptionPane.showMessageDialog(null,”Lưu Text File thành công”);
}
else
{
JOptionPane.showMessageDialog(null,”Lưu Text File thất bại”);
}
}
}
});
}

[/code]

Tới đây ta chạy phần mềm lên để Test chức năng lưu Text File trước xem thành công hay không nhé:

Ta Vào Menu Lưu file/ chọn Save Text File:

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

Đặt tên file là databasesanpham.txt (tên gì kệ bạn), chọn nơi lưu trữ bất kỳ(kệ bạn). Rồi nhấn nút Save để xác thực lưu Text File, nếu ra cửa sổ thông báo sau là Lưu text file thành công:

Ta kiểm lại xem có đúng không nhé, vào nơi mà ta chọn lưu:

Rõ ràng là lưu Text File thành công, bây giờ ta tắt phần mềm và tiến hành viết lệnh đọc Text File:

[code language=”java”]

private void creatEvent() {
mnuSystemOpenTextFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileFactory=new TextFileFactory();
if(fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
sanPhamPanel.database=fileFactory.DocFile(fileChooser.getSelectedFile().getAbsolutePath());
sanPhamPanel.showDataOnJTable();
}
}
});

}

[/code]

Chạy chương trình lên ta sẽ có giao diện trống rỗng như sau:

Vào menu Hệ thống / chọn Đọc File/ chọn Text File, cửa sổ chọn File xuất hiện:

Chọn đúng tập tin lúc nãy bạn lưu (databasesanpham.txt) rồi bấm Open, kết quả:

Như vậy chúng ta đã Đọc text file thành công, kết thúc công việc lưu file và đọc text file. Còn Serialize, XML, JSON hoàn toàn tương tự, Các bạn tự xử nhé.

và chú ý là không quan tâm lưu dữ liệu với loại định dạng nào, khi đã Nạp lên Bộ nhớ thì ta có thể xuất ra bất kỳ định dạng tập tin nào (4 loại).

Coding đầy đủ của SanPhamUI ở đây:

[code language=”java”]

package communityuni.com.ui;

import communityuni.com.io.*;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
* Created by cafe on 04/06/2017.
*/
public class SanPhamUI extends JFrame {
JMenuBar mnuBar;
JMenu mnuSystem;
JMenu mnuSystemSave;
JMenuItem mnuSystemSaveTextFile;
JMenuItem mnuSystemSaveSerializeFile;
JMenuItem mnuSystemSaveXMLFile;
JMenuItem mnuSystemSaveJSonFile;
JMenu mnuSystemOpen;
JMenuItem mnuSystemOpenTextFile;
JMenuItem mnuSystemOpenSerializeFile;
JMenuItem mnuSystemOpenXMLFile;
JMenuItem mnuSystemOpenJSonFile;
JMenuItem mnuSystemExit;

FileFactory fileFactory;
JFileChooser fileChooser=new JFileChooser();
SanPhamPanel sanPhamPanel;
public SanPhamUI(String title)
{
super(title);
sanPhamPanel=new SanPhamPanel();
setContentPane(sanPhamPanel.getPnMain());
createMenu();
createICon();
creatEvent();
}

private void creatEvent() {
mnuSystemOpenTextFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileFactory=new TextFileFactory();
if(fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
sanPhamPanel.database=fileFactory.DocFile(fileChooser.getSelectedFile().getAbsolutePath());
sanPhamPanel.showDataOnJTable();
}
}
});
mnuSystemSaveTextFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây
fileFactory=new TextFileFactory();
if(fileChooser.showSaveDialog(null)==JFileChooser.APPROVE_OPTION)
{
boolean kq=fileFactory.LuuFile(sanPhamPanel.database,fileChooser.getSelectedFile().getAbsolutePath());
if(kq==true)
{
JOptionPane.showMessageDialog(null,”Lưu Text File thành công”);
}
else
{
JOptionPane.showMessageDialog(null,”Lưu Text File thất bại”);
}
}
}
});

mnuSystemOpenSerializeFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileFactory=new SerializeFileFactory();
if(fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
sanPhamPanel.database=fileFactory.DocFile(fileChooser.getSelectedFile().getAbsolutePath());
sanPhamPanel.showDataOnJTable();
}
}
});
mnuSystemSaveSerializeFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây
fileFactory=new SerializeFileFactory();
if(fileChooser.showSaveDialog(null)==JFileChooser.APPROVE_OPTION)
{
boolean kq=fileFactory.LuuFile(sanPhamPanel.database,fileChooser.getSelectedFile().getAbsolutePath());
if(kq==true)
{
JOptionPane.showMessageDialog(null,”Lưu Serialize File thành công”);
}
else
{
JOptionPane.showMessageDialog(null,”Lưu Serialize File thất bại”);
}
}
}
});
mnuSystemOpenXMLFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileFactory=new XMLFileFactory();
if(fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
sanPhamPanel.database=fileFactory.DocFile(fileChooser.getSelectedFile().getAbsolutePath());
sanPhamPanel.showDataOnJTable();
}
}
});
mnuSystemSaveXMLFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây
fileFactory=new XMLFileFactory();
if(fileChooser.showSaveDialog(null)==JFileChooser.APPROVE_OPTION)
{
boolean kq=fileFactory.LuuFile(sanPhamPanel.database,fileChooser.getSelectedFile().getAbsolutePath());
if(kq==true)
{
JOptionPane.showMessageDialog(null,”Lưu XML File thành công”);
}
else
{
JOptionPane.showMessageDialog(null,”Lưu XML File thất bại”);
}
}
}
});
mnuSystemOpenJSonFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileFactory=new JSonFileFactory();
if(fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
sanPhamPanel.database=fileFactory.DocFile(fileChooser.getSelectedFile().getAbsolutePath());
sanPhamPanel.showDataOnJTable();
}
}
});
mnuSystemSaveJSonFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//xử lý lưu TextFile ở đây
fileFactory=new JSonFileFactory();
if(fileChooser.showSaveDialog(null)==JFileChooser.APPROVE_OPTION)
{
boolean kq=fileFactory.LuuFile(sanPhamPanel.database,fileChooser.getSelectedFile().getAbsolutePath());
if(kq==true)
{
JOptionPane.showMessageDialog(null,”Lưu Json File thành công”);
}
else
{
JOptionPane.showMessageDialog(null,”Lưu JSon File thất bại”);
}
}
}
});
}

public void createMenu()
{
mnuBar=new JMenuBar();
setJMenuBar(mnuBar);
mnuSystem=new JMenu(“Hệ thống”);
mnuBar.add(mnuSystem);

mnuSystemSave=new JMenu(“Lưu file”);
mnuSystem.add(mnuSystemSave);
mnuSystemSaveTextFile=new JMenuItem(“Text file”);
mnuSystemSave.add(mnuSystemSaveTextFile);
mnuSystemSave.addSeparator();
mnuSystemSaveSerializeFile=new JMenuItem(“Serialize file”);
mnuSystemSave.add(mnuSystemSaveSerializeFile);
mnuSystemSave.addSeparator();
mnuSystemSaveXMLFile=new JMenuItem(“XML file”);
mnuSystemSave.add(mnuSystemSaveXMLFile);
mnuSystemSave.addSeparator();
mnuSystemSaveJSonFile=new JMenuItem(“JSON file”);
mnuSystemSave.add(mnuSystemSaveJSonFile);

mnuSystem.addSeparator();

mnuSystemOpen=new JMenu(“Đọc File”);
mnuSystem.add(mnuSystemOpen);
mnuSystemOpenTextFile=new JMenuItem(“Text file”);
mnuSystemOpen.add(mnuSystemOpenTextFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenSerializeFile=new JMenuItem(“Serialize file”);
mnuSystemOpen.add(mnuSystemOpenSerializeFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenXMLFile=new JMenuItem(“XML file”);
mnuSystemOpen.add(mnuSystemOpenXMLFile);
mnuSystemOpen.addSeparator();
mnuSystemOpenJSonFile=new JMenuItem(“JSON file”);
mnuSystemOpen.add(mnuSystemOpenJSonFile);

mnuSystem.addSeparator();

mnuSystemExit=new JMenuItem(“Thoát”);
mnuSystem.add(mnuSystemExit);
}
private void createICon() {
mnuSystem.setIcon(new ImageIcon(“hinh/system.png”));
mnuSystemSave.setIcon(new ImageIcon(“hinh/save.png”));
mnuSystemSaveTextFile.setIcon(new ImageIcon(“hinh/textfile.png”));
mnuSystemSaveSerializeFile.setIcon(new ImageIcon(“hinh/serializefile.png”));
mnuSystemSaveXMLFile.setIcon(new ImageIcon(“hinh/xmlfile.png”));
mnuSystemSaveJSonFile.setIcon(new ImageIcon(“hinh/jsonfile.png”));

mnuSystemOpen.setIcon(new ImageIcon(“hinh/open.png”));
mnuSystemOpenTextFile.setIcon(new ImageIcon(“hinh/textfile.png”));
mnuSystemOpenSerializeFile.setIcon(new ImageIcon(“hinh/serializefile.png”));
mnuSystemOpenXMLFile.setIcon(new ImageIcon(“hinh/xmlfile.png”));
mnuSystemOpenJSonFile.setIcon(new ImageIcon(“hinh/jsonfile.png”));

mnuSystemExit.setIcon(new ImageIcon(“hinh/exit.png”));
}
public void showWindow()
{
setSize(500,500);
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
}

[/code]

So sánh định dạng dữ liệu kết quả 4 loại:

Như vậy Tui đã hướng dẫn xong toàn bộ phần mềm quản lý sản phẩm theo HƯỚNG ĐỐI TƯỢNG, thao tác với các control cơ bản và nâng của trong Kotlin. Kết xuất dữ liệu ra 4 loại tập tin, dễ dàng triệu gọi coding giữa Kotlin vs Java rất hay. Bài Sau Tui sẽ làm minh họa về JTree trong Kotlin, các bạn chú ý theo dõi nhé.

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/1wbbj8tb05vtvqe/HocGUIPhan4.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

 

Bài 37-Thiết kế giao diện trong Kotlin – phần 3

[polldaddy poll=9764234]

bài 35, bài 36 Các bạn đã biết thiết kế giao diện cũng như xử lý sự kiện trong Kotlin. Trong bài này Tui tiếp tục trình bày một số control nâng cao đó là JTable để các bạn có thể hiển thị dữ liệu dạng danh sách cũng nhằm củng cố thêm các kiến thức đã học.

Chúng ta sẽ làm bài Quản Lý sản phẩm có giao diện như dưới đây:

Bài trên sẽ có JTable để lưu trữ danh sách sản phẩm. Khi nhấn vào Sản phẩm nào thì thông tin chi tiết của nó sẽ được hiển thị vào mục chi tiết bên dưới.

Chức năng các nút:

Lưu: Đưa Sản phẩm vào JTable

Tiếp: Xóa trắng dữ liệu trong mục chi tiết và đưa con trỏ văn bản về ô nhập mã Sản phẩm

Xóa: Xóa sản phẩm nào đang chọn trên JTable

Thoát: Bấm vào để thoát phần mềm

Bài này Tui tập trung vào việc hướng dẫn cách thiết kế giao diện, gán sự kiện để các bạn thật rành về giao diện. Tui chưa tập trung vào xử lý hướng đối tượng. Bài sau Tui sẽ làm tổng hợp các kiến thức trong khóa học Kotlin từ cơ bản cho tới Lưu/đọc file trên giao diện có Menu và các Control đã học…

Và Tui nhắc lại là Kotlin đang dùng thư viện JVM, nên các bạn muốn thực sự hiểu sâu về Java để hỗ trợ cho Kotlin thì các bạn có thể đăng ký học khóa Lập trình Java nâng cao tại đây hoặc tại đây . Kotlin có thể gọi Java và Java có thể gọi được Kotlin , Android cũng dùng cả 2 ngôn ngữ này để lập trình nên các bạn cần biết cả 2.

Ta tạo 1 Project tên là HocGUIPhan3 có, đưa thư việt Jgoodies, tạo package như hình dưới đây:

Các giao diện sẽ nằm trong package: communityuni.com.ui . Chú ý là giao diện SanPhamPanel khi bạn tạo ra thì nó tự động chia làm 2 Lớp (Lớp SanPhamPanel.form là nơi chúng ta thiết kế kéo thả giao diện, lớp SanPhamPanel là nơi cho ta xử lý các nghiệp vụ cũng như khởi tạo thêm các custom component)

Tập tin chứa hàm main để chạy phần mềm nằm trong package: communityuni.com.test

trong package communityuni.com.ui bạn tạo một Form có tên SanPhamPanel để thiết kế giao diện như dưới đây (khoan hãy để ý tới lớp SanPhamUI, Tui sẽ trình bày sau):

Bạn nhìn vào màn hình Component Tree để biết cách bố trí các Control trên giao diện, cũng như đặt tên chính xác cho chúng.

Chú ý là Jpanel pnMain bạn chọn layout manager là FormLayout Jgoodies, các Jpanel còn lại chọn Layout manager là FlowLayout hết nhé các bạn.

Bây giờ bạn chọn Jtable (tblSanPham) rồi checked vào Custom Create:

Lúc này chương trình sẽ tự động phát sinh ra hàm createUIComponents() trong lớp xử lý nghiệp vụ:

Ta sẽ khởi tạo và hiệu chỉnh giao diện bằng coding trong hàm này, Control nào muốn được thay đổi bằng coding thì cứ checked vào Custom Create.

Có rất nhiều cách tạo bảng, ở đây ta dùng DefaultTableModel , để tạo bảng ta có thể chỉnh như sau:

[code language=”java”]

private JTable tblSanPham;
private DefaultTableModel tableModelSanPham;
private void createUIComponents() {
tableModelSanPham=new DefaultTableModel();
tableModelSanPham.addColumn(“Mã Sản Phẩm”);
tableModelSanPham.addColumn(“Tên Sản Phẩm”);
tableModelSanPham.addColumn(“Đơn giá Sản Phẩm”);
tblSanPham=new JTable(tableModelSanPham);
}

[/code]

Ở trên là tạo 1 JTable  có 3 cột (mã, tên và đơn giá).

Để gán sự kiện cho Jtable ta làm tương tự như gán sự kiện cho JButton mà Tui đã hướng dẫn ở bài 36 (do đó Tui không có nói lại cách gán sự kiện của các JButton, các bạn tự xử): Ta bấm chuột phải vào Jtable rồi chọn Create Listener:

Sau đó chọn Mouse listener:

Sau đó chọn Mouse pressed hoặc mouse click:

Bấm Ok để tạo, lúc này ta có sự kiện Mouse click:

[code language=”java”]

tblSanPham.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);

}
});

[/code]

Ta bổ sung coding cho sự kiện này để lấy hiển thị thông tin chi tiết lên các JTextField:

[code language=”java”]

tblSanPham.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
int row= tblSanPham.getSelectedRow();
String ma=tblSanPham.getValueAt(row,0).toString();
String ten=tblSanPham.getValueAt(row,1).toString();
double gia=Double.parseDouble(tblSanPham.getValueAt(row,2).toString());
txtMa.setText(ma);
txtTen.setText(ten);
txtDonGia.setText(gia+””);
}
});

[/code]

Cuối cùng ta bổ sung cho toàn bộ các sự kiện của các JButton trên giao diện trong lớp xử lý nghiệp vụ như sau:

[code language=”java”]

package communityuni.com.ui;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;

/**
* Created by cafe on 04/06/2017.
*/
public class SanPhamPanel {
private JPanel pnMain;
private JTable tblSanPham;
private DefaultTableModel tableModelSanPham;
private JButton btnLuu;
private JButton btnTiep;
private JButton btnXoa;
private JButton btnThoat;
private JTextField txtMa;
private JTextField txtTen;
private JTextField txtDonGia;

public SanPhamPanel() {
btnTiep.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txtMa.setText(“”);
txtTen.setText(“”);
txtDonGia.setText(“”);
txtMa.requestFocus();
}
});
btnThoat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnLuu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Vectorvector=new Vector();
vector.add(txtMa.getText());
vector.add(txtTen.getText());
vector.add(txtDonGia.getText());
tableModelSanPham.addRow(vector);
}
});
tblSanPham.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
int row= tblSanPham.getSelectedRow();
String ma=tblSanPham.getValueAt(row,0).toString();
String ten=tblSanPham.getValueAt(row,1).toString();
double gia=Double.parseDouble(tblSanPham.getValueAt(row,2).toString());
txtMa.setText(ma);
txtTen.setText(ten);
txtDonGia.setText(gia+””);
}
});
tblSanPham.addMouseListener(new MouseAdapter() {
});
btnXoa.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(tblSanPham.getSelectedRow()>=0)
tableModelSanPham.removeRow(tblSanPham.getSelectedRow());
}
});
}

private void createUIComponents() {
tableModelSanPham=new DefaultTableModel();
tableModelSanPham.addColumn(“Mã Sản Phẩm”);
tableModelSanPham.addColumn(“Tên Sản Phẩm”);
tableModelSanPham.addColumn(“Đơn Giá Sản Phẩm”);
tblSanPham=new JTable(tableModelSanPham);
}
public JPanel getPnMain()
{
return pnMain;
}
}

[/code]

Bây giờ trong package communityuni.com.ui ta tạo một lớp giao diện tên SanPhamUI kế thừa JFrame có coding như sau:

[code language=”java”]

package communityuni.com.ui;

import javax.swing.*;

/**
* Created by cafe on 04/06/2017.
*/
public class SanPhamUI extends JFrame {
public SanPhamUI(String title)
{
super(title);
setContentPane(new SanPhamPanel().getPnMain());
}
public void showWindow()
{
setSize(500,500);
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
}

[/code]

Cuối cùng trong package communityuni.com.test ta tạo 1 file Kotlin AppTestSanPhamUI.kt để chạy phần mềm:

[code language=”groovy”]

package communityuni.com.test

import communityuni.com.ui.SanPhamUI

/**
* Created by cafe on 04/06/2017.
*/
fun main(args: Array) {
var gui:SanPhamUI= SanPhamUI(“Trần Duy Thanh- Chương trình quản lý Sản phẩm”)
gui.showWindow()
}

[/code]

Chạy chương trình lên ta sẽ có giao diện như Tui đã yêu cầu ở trên.

Như vậy Tui đã hướng dẫn xong cách làm thế nào để tạo ra một giao diện có JTable trong Kotlin, cách gán sự kiện cho JTable, cách bố trí giao diện cũng như sử dụng Form Layout JGoodies,  Flow Layout. Bài sau Tui sẽ hướng dẫn các bạn bài tổng hợp Kotlin từ cơ bản tới Hướng đối tượng, lưu file, đọc file, Menu trong Kotlin để hiển thị và lưu trữ danh sách dữ liệu, Các bạn chú ý theo dõi nhé.

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/ko320gej2mkl737/HocGUIPhan3.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

Bài 36-Thiết kế giao diện trong Kotlin – phần 2

[polldaddy poll=9764234]

Trong bài 35 Tui đã hướng dẫn các bạn cách làm thế nào để tạo được giao diện trong Kotlin. Trong bài này Tui tiếp tục làm thêm một ví dụ về thiết kế giao diện Giải Phương Trình Bậc 1, sử dụng layout FormLayout (Jgoodies) và cách gán sự kiện cho các Control. Giao diện đơn giản như sau:

Bạn vào link sau để tải thư viện http://www.java2s.com/Code/JarDownload/jgoodies/jgoodies-forms.jar.zip , giải nén ra và lấy thư viên jgoodies-forms.jar (khoảng 86kb) rồi tham chiếu nào vào Project giống như bài 32 mà Tui đã hướng dẫn, sau đó thiết kế giao diện theo cấu trúc dưới đây (Ngay màn hình đầu tiên tạo Giao diện thì nhớ chọn Layout manager là FormLayout(jGoodies):

Sau đó các dòng JPanel của Hệ số a, hệ số b, kết quả, các button bạn chọn Layout Manager là FLowLayout (tức là chỉ có pnMain mặc định là FormLayout(JGoodies).

Đặt tên các Control giống như hình trên (chọn field name trong cửa sổ Property để đặt tên cho Control):

Tiếp theo gán sự kiện cho các Button:

1) Sự kiện cho Button Thoát:

Bấm chuột phải vào Button Thoát/ chọn Create Listener:

Sau đó chọn Action Listener:

Rồi chọn actionPerformed:

Lúc này chương trình sẽ tự động xuất hiện sự kiện cho Button thoát (nó tự tạo trong Constructor):

[code language=”java”]

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
* Created by cafe on 03/06/2017.
*/
public class PTB1UI {
private JTextField txtA;
private JTextField txtB;
private JTextField txtKetQua;
private JButton btnThoat;
private JButton btnGiai;
private JButton btnTiep;
private JPanel pnMain;

public PTB1UI() {
btnThoat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});

}

}

[/code]

Tương tự như vậy ta lặp gán sự kiện cho Button  Giải, Button Tiếp, Đồng thời bổ sung thêm getpnMain để truy suất panel pnMain. Cuối cùng coding sẽ như sau:

[code language=”java”]

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
* Created by cafe on 03/06/2017.
*/
public class PTB1UI {
private JTextField txtA;
private JTextField txtB;
private JTextField txtKetQua;
private JButton btnThoat;
private JButton btnGiai;
private JButton btnTiep;
private JPanel pnMain;

public PTB1UI() {
btnThoat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnGiai.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
double a=Double.parseDouble(txtA.getText());
double b=Double.parseDouble(txtB.getText());
if(a==0 && b==0)
txtKetQua.setText(“Vô số nghiệm”);
else if(a==0 && b!=0)
txtKetQua.setText(“Vô nghiệm”);
else
txtKetQua.setText(“x=”+(-b/a));
}
});
btnTiep.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txtA.setText(“”);
txtB.setText(“”);
txtKetQua.setText(“”);
txtA.requestFocus();
}
});
}

public JPanel getPnMain()
{
return pnMain;
}
}

[/code]

Cuối cùng ta tạo một Kotlin File để chạy phần mềm (TestPhuongTrinhBac1UI.kt):

[code language=”groovy”]

import javax.swing.JFrame

/**
* Created by cafe on 03/06/2017.
*/
fun main(args: Array) {
var frm:JFrame = JFrame(“Trần Duy Thanh”)
frm.contentPane= PTB1UI().pnMain
frm.defaultCloseOperation=JFrame.EXIT_ON_CLOSE
frm.setSize(300,250)
frm.setLocationRelativeTo(null)
frm.isVisible=true
}

[/code]

Chạy chương trình lên ta sẽ có giao diện như yêu cầu ở trên:

Như vậy Tui đã hướng dẫn xong cách làm thế nào để tạo ra một giao diện trong Kotlin, cách gán sự kiện, cách bố trí giao diện cũng như sử dụng Form Layout JGoodies. Bài sau Tui sẽ hướng dẫn các bạn bài JTable để hiển thị danh sách dữ liệu, Các bạn chú ý theo dõi nhé.

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/066jaszvy7oo5up/HocGUIPhan2.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

Bài 35-Thiết kế giao diện trong Kotlin – phần 1

[polldaddy poll=9764234]

Tính tới thời điểm 03/06/2017 thì Kotlin hiện tại chưa phát triển GUI Framework riêng mà đang sử dụng JVM để thiết kế giao diện (dùng awt, swing, javafx). Công cụ IntelliJ IDEA có hỗ trợ Drag & Drop giúp Lập Trình Viên có thể kéo thả thiết kế giao diện lúc Design Time.

Ta tạo một Projec tên là HocGUIPhan1, sau đó New một GUI Form như cách sau:

Bấm chuột phải vào Project/ chọn New / rồi cọn GUI Form, màn hình New GUI Form sẽ xuất hiện như dưới đây:

Form Name: Đặt tên cho Lớp giao diện, ví dụ HelloWorldUI

Base layout manager: Chọn layout mặc định cho giao diện, dĩ nhiên ta có thể thay đổi sau đó. Layout chính là nơi tổ chức sắp xếp các Control trên giao diện. Tại màn hình này, Intelliz IDEA cung cấp một số Layout sau:

Vì lần đầu thử nghiệm, cứ để mặc định như IDEA chọn. Và Tui cũng không đi sâu vào giải thích từng thành phần, vì nó có sẵn trong Java Swing, các bạn muốn chi tiết thì đăng ký học tại đây:

Create bound class: Nên checked để nó tự động tạo ra lớp tương tác giao diện.

Sau đó bạn nhấn OK để tạo, giao diện sẽ như hình dưới đây:

Ở màn hình trên Tui đánh dấu 5 phần, mỗi phần có những chức năng và ý nghĩa khác nhau:

Mục 1: Là cấu trúc tổ chức các Lớp, thư viện

Mục 2: Cửa sổ Component Tree, là nơi cho ta biết cấu trúc layout của các control trên giao diện, nó cũng cho phép ta kéo thả control vào đây.

Mục 3: Cửa sổ Properties, là nơi cho phép ta cấu hình các thông số cho control như: Màu mè, chữ, kích thước, tên …

Mục 4: Là nơi thiết kế giao diện, cho phép ta kéo thả control vào đây

Mục 5: Cửa sổ Pallette, là màn hình lưu trữ danh sách các control, nó cho phép ta Drag các Control trong này và Drop ra mục 4.

Bây giờ Ta thử kéo thả đại một vài Control ra như sau:

Ta túm JLabel kéo thả vào mục 4, rồi trong mục 3 chỉnh Font (bấm vào nút …) nó sẽ ra màn hình Font ta tha hồ chọn, cấu hình sao giống như trên là OK. Các bạn muốn hiểu chi tiết ngọn ngành về Java Swing thì đăng ký học tại đây để dễ dàng học GUI cho Kotlin:

Bây giờ làm sao để chạy được màn hình này? Trước tiên ta cần đặt tên cho JPanel ở ngoài cùng trước, ví dụ đặt tên thành pnMain:

Lúc này bạn quan sát lớp HelloWorldUI sẽ tự động xuất hiện khai báo biến của control này:

Sau đó sửa lại coding như sau:

[code language=”java”]

import javax.swing.*;

/**
* Created by cafe on 03/06/2017.
*/
public class HelloWorldUI {
private JPanel pnMain;
public JPanel getPnMain()
{
return pnMain;
}
}

[/code]

Sau đó new 1 file Kotlin (app_test_gui.kt):

Bổ sung thêm các lệnh dưới đây để tải Giao diện:

[code language=”groovy”]

import javax.swing.JFrame

/**
* Created by cafe on 03/06/2017.
*/
fun main(args: Array) {
//Tạo đối tượng màn hình loại JFrame
var helloGui:JFrame = JFrame(“Chương trình Hello World”)
//gán giao diện chính trong HelloWorldUI cho JFrame
helloGui.contentPane=HelloWorldUI().pnMain
//thiết lập cho phép bấm vào X để đóng cửa sổ
helloGui.defaultCloseOperation=JFrame.EXIT_ON_CLOSE
//thiết lập kích thước cửa sổ
helloGui.setSize(500,400)
//cho màn hình mặc định nằm giữa Desktop
helloGui.setLocationRelativeTo(null)
//hiển thị giao diện:
helloGui.isVisible=true
}

[/code]

Chạy chương trình lên ta có giao diện:

Như vậy Tui đã hướng dẫn xong cách làm thế nào để tạo ra một giao diện trong Kotlin, bài sau Tui sẽ làm thêm 1 ví dụ về giao diện Giải phương trình bậc 1, sử dụng layout FormLayout jgoodies cũng như cách gán sự kiện cho các control. Các bạn chú ý theo dõi nhé

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/yveku2qc4vchwvl/HocGUIPhan1.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

Bài 34-Đọc JSon tỉ giá hối đoái của Ngân Hàng Đông Á trong Kotlin – Bài 3

[polldaddy poll=9764234]

Tui đã trình bày kỹ cách sử dụng JSon trong Kotlin ở các bài 32, bài 33 . Tui muốn minh họa thêm một ví dụ cuối cùng về đọc JSon trong Kotlin, trong bài này Tui sẽ coding lấy Tỉ giá hối đoái của Ngân Hàng Đông Á cung cấp:

bạn vào link này để xem Webservice API cung Tỉ giá hối đoái của DongA Bank: http://dongabank.com.vn/exchange/export

Đây là kết quả (nó luôn chính xác và thời gian thực nhé các bạn, hàng ngày Ngân hàng Đông Á luôn cập nhật giá khi có sự thay đổi):

({“items”:[{“type”:”CAD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/CAD.gif”,”muatienmat”:”16720″,”muack”:”16740″,”bantienmat”:”16930″,”banck”:”16930″},{“type”:”XAU”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/XAU.gif”,”muatienmat”:”3632000″,”muack”:”3621000″,”bantienmat”:”3655000″,”banck”:”3621000″},{“type”:”AUD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/AUD.gif”,”muatienmat”:”16790″,”muack”:”16800″,”bantienmat”:”16980″,”banck”:”17000″},{“type”:”CHF”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/CHF.gif”,”muatienmat”:”23430″,”muack”:”23450″,”bantienmat”:”23730″,”banck”:”23730″},{“type”:”CNY”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/CNY.gif”,”muatienmat”:”3000″,”muack”:”3000″,”bantienmat”:”3500″,”banck”:”3500″},{“type”:”EUR”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/EUR.gif”,”muatienmat”:”25460″,”muack”:”25480″,”bantienmat”:”25750″,”banck”:”25750″},{“type”:”GBP”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/GBP.gif”,”muatienmat”:”29070″,”muack”:”29110″,”bantienmat”:”29450″,”banck”:”29450″},{“type”:”HKD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/HKD.gif”,”muatienmat”:”2550″,”muack”:”2900″,”bantienmat”:”2940″,”banck”:”2940″},{“type”:”JPY”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/JPY.gif”,”muatienmat”:”204.3″,”muack”:”204.6″,”bantienmat”:”206.7″,”banck”:”206.7″},{“type”:”NZD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/NZD.gif”,”muatienmat”:””,”muack”:”16120″,”bantienmat”:””,”banck”:”16320″},{“type”:”PNJ_DAB”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/PNJ_DAB.gif”,”muatienmat”:”3495000″,”muack”:”3495000″,”bantienmat”:”3545000″,”banck”:”3545000″},{“type”:”SGD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/SGD.gif”,”muatienmat”:”16320″,”muack”:”16340″,”bantienmat”:”16540″,”banck”:”16540″},{“type”:”THB”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/THB.gif”,”muatienmat”:”595″,”muack”:”625″,”bantienmat”:”655″,”banck”:”655″},{“type”:”USD”,”imageurl”:”http:\/\/www.dongabank.com.vn\/images\/flag\/USD.gif”,”muatienmat”:”22680″,”muack”:”22680″,”bantienmat”:”22750″,”banck”:”22750″}]})

Như chúng ta đã học ở những bài lý thuyết Json thì đây không phải là cấu trúc Json. Vì cấu trúc JSon chỉ chấp nhận 2 loại đó là: Dữ liệu được để trong {} hoặc được để trong [] . Còn ở đây Ngân Hàng Đông Á lại để () ở ngoài. Do đó lúc đọc dữ liệu về ta chỉ cần Remove 2 ký tự này đi là xong.

Tiếp tục phân tích cấu trúc của Json ở trên:

Rõ ràng ở ngoài là 1 đối tượng JSonObject, bên trong nó có một JSonArray đặt tên là items. Mỗi đối tượng trong mảng Items có 6 thuộc tính như ở trên.

Bây giờ ta tạo một Project, và đưa thư viện gson-2.8.1.jar như các bài trước vào (bạn tự làm vì Tui đã hướng dẫn rất chi tiết ở các bài trước ), rồi tiến hành tạo các lớp sau:

Lớp Item để lưu trữ các dữ liệu bên trong mảng items (chú ý là các thuộc tính phải copy paste y xì từ dữ liệu JSon mà Đông Á Bank cung cấp):

[code language=”groovy”]

/**
* Created by cafe on 03/06/2017.
*/
class Item {
var type:String=””
var imageurl:String=””
var muatienmat:String=””
var muack:String=””
var bantienmat:String=””
var banck:String=””
override fun toString(): String {
return “Mã Tiền Tệ : “+type+”\n”+
“Mua tiền mặt :”+muatienmat+”\n”+
“Bán tiền mặt :”+bantienmat+”\n”+
“Mua chuyển khoản :”+muack+”\n”+
“Bán chuyển khoản :”+banck+”\n”+
“Hình đại diện :”+imageurl+”\n”
}
}

[/code]

Tiếp theo tạo 1 Lớp tỉ giá để lưu trữ mảng items

Các bạn cần nhớ là với GSon nó không quan tâm tên Lớp là gì(đặt tên gì cũng được), nó quan tâm tên thuộc tính (phải đặt chính xác như dữ liệu Json cũng cấp)

[code language=”groovy”]

/**
* Created by cafe on 03/06/2017.
*/
class TiGia {
var items:MutableList = mutableListOf()
}

[/code]

Cuối cùng tạo hàm main để kiểm tra lấy dữ liệu từ Ngân Hàng Đông Á, chú ý ta dùng HttpURLConnection để lấy dữ liệu từ Webservice API mà Đông Á Cung cấp.:

[code language=”groovy”]

import com.google.gson.Gson
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL

/**
* Created by cafe on 03/06/2017.
*/
fun main(args: Array) {
var url:URL=URL(“http://dongabank.com.vn/exchange/export”)
var connection:HttpURLConnection= url.openConnection() as HttpURLConnection
var isr:InputStreamReader= InputStreamReader(connection.inputStream,”UTF-8″)
var br:BufferedReader=BufferedReader(isr)
var s= br.readText()
br.close()
isr.close()
s=s.replace(“(“,””)
s=s.replace(“)”,””)
var gs:Gson= Gson()
var dstg:TiGia=gs.fromJson(s,TiGia::class.java)
for (item in dstg.items)
{
println(item)
println(“——————————-“)
}
}

[/code]

Khi chạy hàm main ở trên thì ta có kết quả sau:

Mã Tiền Tệ : CAD
Mua tiền mặt :16720
Bán tiền mặt :16930
Mua chuyển khoản :16740
Bán chuyển khoản :16930
Hình đại diện :http://www.dongabank.com.vn/images/flag/CAD.gif——————————-
Mã Tiền Tệ : XAU
Mua tiền mặt :3632000
Bán tiền mặt :3655000
Mua chuyển khoản :3621000
Bán chuyển khoản :3621000
Hình đại diện :http://www.dongabank.com.vn/images/flag/XAU.gif——————————-
Mã Tiền Tệ : AUD
Mua tiền mặt :16790
Bán tiền mặt :16980
Mua chuyển khoản :16800
Bán chuyển khoản :17000
Hình đại diện :http://www.dongabank.com.vn/images/flag/AUD.gif——————————-
Mã Tiền Tệ : CHF
Mua tiền mặt :23430
Bán tiền mặt :23730
Mua chuyển khoản :23450
Bán chuyển khoản :23730
Hình đại diện :http://www.dongabank.com.vn/images/flag/CHF.gif

——————————-
Mã Tiền Tệ : CNY
Mua tiền mặt :3000
Bán tiền mặt :3500
Mua chuyển khoản :3000
Bán chuyển khoản :3500
Hình đại diện :http://www.dongabank.com.vn/images/flag/CNY.gif

——————————-
Mã Tiền Tệ : EUR
Mua tiền mặt :25460
Bán tiền mặt :25750
Mua chuyển khoản :25480
Bán chuyển khoản :25750
Hình đại diện :http://www.dongabank.com.vn/images/flag/EUR.gif

——————————-
Mã Tiền Tệ : GBP
Mua tiền mặt :29070
Bán tiền mặt :29450
Mua chuyển khoản :29110
Bán chuyển khoản :29450
Hình đại diện :http://www.dongabank.com.vn/images/flag/GBP.gif

——————————-
Mã Tiền Tệ : HKD
Mua tiền mặt :2550
Bán tiền mặt :2940
Mua chuyển khoản :2900
Bán chuyển khoản :2940
Hình đại diện :http://www.dongabank.com.vn/images/flag/HKD.gif

——————————-
Mã Tiền Tệ : JPY
Mua tiền mặt :204.3
Bán tiền mặt :206.7
Mua chuyển khoản :204.6
Bán chuyển khoản :206.7
Hình đại diện :http://www.dongabank.com.vn/images/flag/JPY.gif

——————————-
Mã Tiền Tệ : NZD
Mua tiền mặt :
Bán tiền mặt :
Mua chuyển khoản :16120
Bán chuyển khoản :16320
Hình đại diện :http://www.dongabank.com.vn/images/flag/NZD.gif

——————————-
Mã Tiền Tệ : PNJ_DAB
Mua tiền mặt :3495000
Bán tiền mặt :3545000
Mua chuyển khoản :3495000
Bán chuyển khoản :3545000
Hình đại diện :http://www.dongabank.com.vn/images/flag/PNJ_DAB.gif

——————————-
Mã Tiền Tệ : SGD
Mua tiền mặt :16320
Bán tiền mặt :16540
Mua chuyển khoản :16340
Bán chuyển khoản :16540
Hình đại diện :http://www.dongabank.com.vn/images/flag/SGD.gif

——————————-
Mã Tiền Tệ : THB
Mua tiền mặt :595
Bán tiền mặt :655
Mua chuyển khoản :625
Bán chuyển khoản :655
Hình đại diện :http://www.dongabank.com.vn/images/flag/THB.gif

——————————-
Mã Tiền Tệ : USD
Mua tiền mặt :22680
Bán tiền mặt :22750
Mua chuyển khoản :22680
Bán chuyển khoản :22750
Hình đại diện :http://www.dongabank.com.vn/images/flag/USD.gif

——————————-

Như vậy ta đã Ví dụ xong trường hợp đọc JSon từ 1 Webservice, cụ thể là Tỉ giá hối đoái của Ngân Hàng Đông Á, các bạn tự áp dụng vào các dự án cụ thể nhé. Các bài sau Tui sẽ hướng dẫn thiết kế giao diện (GUI) trong Kotlit, các bạn chú ý theo dõi nhé

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/hghudhgyo53ra8g/HocJsonDongABank.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

Bài 33-Xử lý JSon trong Kotlin – Bài 2

[polldaddy poll=9764234]

Trong bài 32 Tui đã trình bày chi tiết cách sử dụng thư viện GSon để Lưu Kotlin Model thành JSon và Đọc JSon thành Kotlin Model như thế nào. Ở bài này Tui tiếp tục làm thêm một ví dụ phức tạp hơn về JSon trong Kotlin, đó là tạo ra 2 lớp có mối quan hệ Master-Detail, đây là một trong những trường hợp thường gặp nhiều nhất trong quá trình triển khai dự án thật. Cụ thể Tui sẽ bổ sung thêm một Lớp Danh Mục, nó có mối quan hệ với Lớp Sản Phẩm: Một Danh Mục có nhiều Sản phẩm và Một Sản phẩm thuộc về một danh Mục nào đó. Để qua đây chúng ta tìm hiểu xem GSon tạo ra file JSon như thế nào cũng như phục hồi lại Kotlin Model ra sao.

Bài này Tui sẽ đi trực tiếp vào kỹ thuật lập trình luôn, còn lý thuyết các bạn tự xem lại bài 32 nhé.

Lớp Sản Phẩm có cấu trúc như sau:

[code language=”groovy”]

import java.io.Serializable

/**
* Created by cafe on 03/06/2017.
*/
class SanPham {
var MaSanPham:Int=0
var TenSanPham:String=””
var DonGia:Double=0.0
constructor()
constructor(MaSanPham: Int, TenSanPham: String, DonGia: Double) {
this.MaSanPham = MaSanPham
this.TenSanPham = TenSanPham
this.DonGia = DonGia
}
override fun toString(): String {
return MaSanPham.toString()+”\t”+TenSanPham+”\t”+DonGia
}
}

[/code]

Cấu trúc của lớp Danh Mục:

[code language=”groovy”]

import java.io.Serializable

/**
* Created by cafe on 03/06/2017.
*/
class DanhMuc {
var MaDanhMuc:Int=0
var TenDanhMuc:String=””
var SanPhams:MutableList = mutableListOf()
constructor()
constructor(MaDanhMuc: Int, TenDanhMuc: String) {
this.MaDanhMuc = MaDanhMuc
this.TenDanhMuc = TenDanhMuc
}
override fun toString(): String {
var s=””
for (sp in SanPhams)
s+=”\t”+sp.toString() + “\n”
var infor=”Danh Mục:[“+MaDanhMuc.toString()+ “\t”+TenDanhMuc+”]”
infor+=”\nCác Sản phẩm của danh Mục này là:\n”+s
return infor
}
fun ThemSanPham(sp:SanPham)
{
SanPhams.add(sp)
}
}

[/code]

Như Tui đã nói ở bài 32, GSon không quan tâm cấu trúc và mối quan hệ giữa các lớp mà bạn viết như thế nào. Nó “cân” hết (đừng bị đệ quy là được, nếu ko nó bị stack over flow)

Giờ ta tiếp tụ tạo lớp JSonFileFactory, nó có cấu trúc như dưới đây (giống nhau trong mọi trường hợp nếu bạn dùng kiểu dữ liệu là Any)

[code language=”groovy”]

import com.google.gson.Gson
import java.io.FileWriter
import java.io.FileReader
import com.google.gson.reflect.TypeToken

/**
* Created by cafe on 02/06/2017.
*/
class JSonFileFactory {
/**
* @author Trần Duy Thanh
* @param data: Dữ liệu là Danh sách sản phẩm muốn lưu
* @param path: Đường dẫn lưu trữ
* @return true nếu lưu thành công, false nếu lưu thất bại
*/
fun LuuFile(data:MutableList,path:String):Boolean
{
try {
val gs= Gson()
val file=FileWriter(path)
gs.toJson(data,file)
file.close()
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}
/**
* @author Trần Duy Thanh
* @param path:đường dẫn muốn đọc dữ liệu
* @return Danh sách sản phẩm MutableList
*/
fun DocFile(path:String):MutableList
{
var data:MutableList = mutableListOf()
try
{
val gson = Gson()
var file=FileReader(path)
data = gson.fromJson<MutableList>(file,
object : TypeToken<MutableList>()
{
}.type
)
file.close()
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return data
}
}

[/code]

Cuối cùng ta tạo hàm main để kiểm tra:

[code language=”groovy”]

fun main(args: Array) {
var database:MutableList = mutableListOf()

var dmDienTu:DanhMuc=DanhMuc(1,”Mặt hàng điện tử”)
database.add(dmDienTu)

var bongden:SanPham=SanPham(1,”Bóng đèn điện Quang”,150.0)
dmDienTu.ThemSanPham(bongden)
var acquy:SanPham=SanPham(2,”Ắc quy Đồng Nai”,250.0)
dmDienTu.ThemSanPham(acquy)
var maydien:SanPham=SanPham(3,”Máy phát điện ABC”,90.0)
dmDienTu.ThemSanPham(maydien)

var dmTieuDung:DanhMuc=DanhMuc(2,”Mặt hàng tiêu dùng”)
database.add(dmTieuDung)

var xabong:SanPham=SanPham(4,”Xà Bông Lifeboy”,15.0)
dmTieuDung.ThemSanPham(xabong)
var nuocruachen:SanPham=SanPham(5,”Nước rửa chén Sunlight”,12.0)
dmTieuDung.ThemSanPham(nuocruachen)

var dmHoaChat:DanhMuc=DanhMuc(3,”Mặt hàng Hóa Chất”)
database.add(dmHoaChat)

var dietmuoi:SanPham=SanPham(6,”Thuốc Diệt Muỗi XYZ”,80.0)
dmHoaChat.ThemSanPham(dietmuoi)
var dietchuot:SanPham=SanPham(7,”Thuốc Diệt Chuỗi ABC”,70.0)
dmHoaChat.ThemSanPham(dietchuot)

for (dm in database)
println(dm)

var kqLuu= JSonFileFactory().LuuFile(database,”d:/dulieudanhmuc.json”)
if(kqLuu)
{
println(“Lưu Json file thành công”)
}
else
{
println(“Lưu Json file thất bại”)
}
}

[/code]

Khi chạy hàm main ở trên thì ta có kết quả sau:

Danh Mục:[1 Mặt hàng điện tử]
Các Sản phẩm của danh Mục này là:
1 Bóng đèn điện Quang 150.0
2 Ắc quy Đồng Nai 250.0
3 Máy phát điện ABC 90.0Danh Mục:[2 Mặt hàng tiêu dùng]
Các Sản phẩm của danh Mục này là:
4 Xà Bông Lifeboy 15.0
5 Nước rửa chén Sunlight 12.0Danh Mục:[3 Mặt hàng Hóa Chất]
Các Sản phẩm của danh Mục này là:
6 Thuốc Diệt Muỗi XYZ 80.0
7 Thuốc Diệt Chuỗi ABC 70.0

Lưu Json file thành công

Bây giờ ta vào ổ D xem tập tin dulieudanhmuc.json có được lưu thành công hay chưa:

Rõ ràng kết quả đã lưu thành công, bạn quan sát thấy cấu trúc Json này có gì khác biệt? Đó là mỗi một đối tượng danh Mục nó một mảng SanPhams–>được GSon tự động tạo ra từ mối quan hệ của 2 Lớp DanhMuc + SanPham

Bây giờ ta sẽ gọi hàm đọc thông tin lên nhé:

[code language=”groovy”]

fun main(args: Array) {
var database:MutableList =
JSonFileFactory().DocFile(“d:/dulieudanhmuc.json”)
for (dm in database)
println(dm)
}

[/code]

Khi chạy hàm main ở trên thì ta có kết quả sau:

Danh Mục:[1 Mặt hàng điện tử]
Các Sản phẩm của danh Mục này là:
1 Bóng đèn điện Quang 150.0
2 Ắc quy Đồng Nai 250.0
3 Máy phát điện ABC 90.0Danh Mục:[2 Mặt hàng tiêu dùng]
Các Sản phẩm của danh Mục này là:
4 Xà Bông Lifeboy 15.0
5 Nước rửa chén Sunlight 12.0Danh Mục:[3 Mặt hàng Hóa Chất]
Các Sản phẩm của danh Mục này là:
6 Thuốc Diệt Muỗi XYZ 80.0
7 Thuốc Diệt Chuỗi ABC 70.0

Có vẻ tới đây các bạn thấy rằng JSon sẽ dễ lập trình hơn Text File, Serialize File, và XML File đúng không? Hiện nay cấu trúc JSon được sử dụng rất nhiều, ngày càng phổ biến và các lập trình viên rất thích điều này.

Như vậy ta đã Ví dụ xong trường hợp phức  tạp của JSon File đó là có mối quan hệ giữa các lớp, các bạn tự áp dụng vào các dự án cụ thể nhé. Bài sau Tui sẽ trình bày thêm cách lấy dữ liệu Json từ Internet trong Kotlin. Cụ thể là lấy Tỉ Giá hối đoái của Ngân Hàng Đông Á, Json có cấu trúc phức tạp, các bạn chú ý theo dõi nhé

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/hk795a31148wsb0/HocJSon_DanhMucSanPham.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)

Bài 32-Xử lý JSon trong Kotlin – Bài 1

[polldaddy poll=9764234]

Các bạn đã nắm được 3 kiểu tương tác File: Text File, Serialize File, XML file. Bây giờ Tui hướng dẫn loại định dạng file cuối cùng rất nổi tiếng hiện nay đó là định dạng Json.

Khái niệm về JSon Tui đã trình bày ở bài 51 của Android, các bạn có thể vào bài này để đọc thêm.

Kotlin cũng có sẵn các lớp để tương tác Json, hoặc có nhiều thư viện ngoài rất nổi tiếng như GSon, Klaxon… giúp chúng ta dễ dàng chuyển Object Model thành Json và từ Json thành Object Model vô cùng lợi hại.

Trong bài này Tui sẽ hướng dẫn các bạn cách dùng GSon trong Kotlin để chuyển đổi qua loại giữa Object Model và Json.

Tính tới thời điểm Tui viết bài học này thì GSon có phiên bản mới nhất là 2.8.1 (update ngày 31/05/2017)

Bạn cần tải thư viện này về rồi reference nó vào Project trong IntelliJ IDEA của bạn.

Để tải GSon 2.8.1 bạn vào link: https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.1/

Tải tập tin gson-2.8.1.jar (dụng lượng khoảng 228kb) từ  link ở trên về máy tính.

Tui nói sơ qua cách thức hoạt động của GSon:

Để chuyển đổi Kotlin Model tới JSon ta làm như sau:

    val gson = Gson()
    val obj = KotlinModel()//lớp nào đó trong Kotlin

// 1. Kotlin object to JSON, and save into a file
    val file=FileWriter("D:\\file.json")
    gson.toJson(obj, file)
    file.close()
// 2. Kotlin object to JSON, and assign to a String
    val jsonInString = gson.toJson(obj)

Để chuyển đổi JSon về Kotlin Model ta làm như sau:

val gson = Gson()

// 1. JSON to Kotlin model, read it from a file.
    val data = gson.fromJson(FileReader("D:\\file.json"), SanPham::class.java)

// 2. JSON to Kotlin Model, read it from a Json String.
    val jsonInString = "{'name' : 'cocacola'}"
    val data= gson.fromJson(jsonInString, SanPham::class.java)

// JSON to JsonElement, convert to String later.
    val json = gson.fromJson(FileReader("D:\\file.json"), JsonElement::class.java)
    val result = gson.toJson(json)

Nếu dữ liệu Json là dạng JsonArray  thì để đưa về Kotlin  Model ta phải làm như sau:

val gson = Gson()
val json = "[{\"name\":\"cocacola\"}, {\"name\":\"pepsi\"}]"
val data:MutableList = gson.fromJson(json, 
        object : TypeToken() 
        {}.type)

Bây giờ Tui sẽ hướng dẫn chi tiết từng bước cụ thể để các bạn có thể dễ dàng hiểu và vận dụng thư viện GSon.

Tạo một Project tên là HocJSonFile, từ Project này ta tạo 1 thư mục(directory) tên là libs để chép thư viện gson-2.8.1.jar vào libs:

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

Đặt tên directory là libs rồi bấm Ok, lúc này thư mục libs sẽ được tạo ra trong Project. Ta chép gson-2.8.1.jar vào thư mục này như hình dưới đây:

Tiếp theo ta cần đưa gson-2.8.1.jar thành thư viện sử dụng trong Project, cách làm như sau:

Bấm chuột phải vào gson-2.8.1.jar rồi chọn add as Library như hình ở trên, màn hình cấu hình sẽ xuất hiện:

Đặt tên(để mặc định) rồi bấm OK, lúc này bạn quan sát có sự thay đổi trong cách hiển thị, thấy được tập các lớp nằm trong thư viện này:

Bây giờ ta tiến hành tạo lớp Sản phẩm giống như các kỹ thuật xử lý text file, serialize file, xml file mà bạn đã được học trước đó:

[code language=”groovy”]

import java.io.Serializable
/**
* Created by cafe on 02/06/2017.
*/
class SanPham:Serializable {
var ma:Int=0
var ten:String=””
var donGia:Double=0.0
constructor()
constructor(ma: Int, ten: String, donGia: Double) {
this.ma = ma
this.ten = ten
this.donGia = donGia
}
override fun toString(): String {
return “$ma\t$ten\t$donGia”
}
}

[/code]

Tiếp tục tạo lớp JSonFileFactory để Lưu và đọc Json bằng GSon:

[code language=”groovy”]

import com.google.gson.Gson
import java.io.FileWriter
import java.io.FileReader
import com.google.gson.reflect.TypeToken

/**
* Created by cafe on 02/06/2017.
*/
class JSonFileFactory {
/**
* @author Trần Duy Thanh
* @param data: Dữ liệu là Danh sách sản phẩm muốn lưu
* @param path: Đường dẫn lưu trữ
* @return true nếu lưu thành công, false nếu lưu thất bại
*/
fun LuuFile(data:MutableList,path:String):Boolean
{
try {
val gs= Gson()
val file=FileWriter(path)
gs.toJson(data,file)
file.close()
return true
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return false
}
/**
* @author Trần Duy Thanh
* @param path:đường dẫn muốn đọc dữ liệu
* @return Danh sách sản phẩm MutableList
*/
fun DocFile(path:String):MutableList
{
var data:MutableList = mutableListOf()
try
{
val gson = Gson()
var file=FileReader(path)
data = gson.fromJson<MutableList>(file,
object : TypeToken<MutableList>()
{
}.type
)
file.close()
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return data
}
}

[/code]

Bạn thấy đấy, Gson giúp chúng ta đơn giản hóa mọi việc lưu và đọc dữ liệu. Đây là một trong những thư viện nổi tiếng, được sử dụng rất nhiều trong các dự án, và định dạng dữ liệu JSon ngày càng phổ biến, có thể hơn cả XML vốn đã nổi đình nổi đám trước đó.

Cuối cùng ta tạo hàm main để kiểm tra:

[code language=”groovy”]

fun main(args: Array) {

var data:MutableList = mutableListOf()
var sp1=SanPham(1,”Coca cola”,15.5)
data.add(sp1)
var sp2=SanPham(2,”Sting”,25.0)
data.add(sp2)
var sp3=SanPham(3,”Redbull”,17.0)
data.add(sp3)
var kqLuu= JSonFileFactory().LuuFile(data,”d:/dulieusanpham.json”)
if(kqLuu)
{
println(“Lưu Json file thành công”)
}
else
{
println(“Lưu Json file thất bại”)
}
}

[/code]

Khi chạy hàm main ở trên thì ta có kết quả sau:

Lưu Json file thành công

Bây giờ ta vào ổ D xem tập tin dulieusanpham.json có được lưu thành công hay chưa:

Rõ ràng kết quả đã lưu thành công, bây giờ ta sẽ gọi hàm đọc thông tin lên nhé:

[code language=”groovy”]

fun main(args: Array) {
var data:MutableList = JSonFileFactory().DocFile(“d:/dulieusanpham.json”)
for (sp in data)
println(sp)
}

[/code]

Khi chạy hàm main ở trên thì ta có kết quả sau:

1 Coca cola 15.5
2 Sting 25.0
3 Redbull 17.0

Như vậy ta đã lưu và đọc JSon File thành công, các bạn tự áp dụng vào các dự án cụ thể nhé, cấu trúc JSon File trong trường hợp này nó sẽ tự động build dựa vào cấu trúc Class và mối quan hệ giữa các Class mà bạn tạo ra.

Bài sau Tui sẽ trình bày thêm cách lấy dữ liệu Json từ Internet trong Kotlin, các Json có cáu trúc phức tạp, các bạn chú ý theo dõi nhé

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/7jgvvzyschy77gg/HocJSonFile.rar

Hẹn gặp các bạn ở những bài tiếp theo

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

Trần Duy Thanh (http://communityuni.com/)