Làm app android chỉ bằng html, css và javascript thuần, phần một - Viết code javascript

Trong sê-ri này mình sẽ cùng các bạn xây dựng một ứng dụng android chỉ bằng html, css và javascript. Hiện nay có rất nhiều framework javascript giúp phát triển ứng dụng nhanh hơn, tuy nhiên chúng ta sẽ cùng trở lại với javascript thuần, bắt đầu từ con số không cho đến khi app của chúng ta lên Play Store.

Đây không phải là cách tốt nhất để tạo app android, tuy nhiên với tinh thần học là chính, sê-ri này sẽ giúp bạn để hiểu thêm một chút, rằng javascript tương tác với DOM document như thế nào? Hybird app là gì? Đưa app lên Play Store như thế nào?

Toàn bộ sê-ri này gồm 4 phần:

Phần 1: Viết code javascript
Phần 2: Mông má lại cho đẹp bằng css
Phần 3: App signing và build thành apk
Phần 4: Đưa app lên Google Play

Link github toàn bộ code ở đây:

https://github.com/buivannguyen/girl-puzzle-android-app

Phân tích một chút

Đây là một app giải trí, khi mở app lên sẽ có một cái hình gái xinh, bấm vào cái hình gái xinh nó sẽ cho một câu hỏi, trả lời đúng thì được qua hình gái xinh tiếp theo, sai phải chơi lại từ đầu. Điểm vui của trò này là người chơi không biết sẽ bị hỏi câu gì, nó sẽ hỏi những câu trời ơi đất hỡi như là “Cô gái có hình xăm trên chân trái hay chân phải”.

Các màn hình cơ bản:

Android app

Hình trên là màn hình dạng thô, chưa thêm thắt trang trí cho các bạn dễ hiểu, ở phần 2 chúng ta mới mông má lại cho đẹp sau.

Chúng ta sẽ có các màn hình như sau:

  • Màn hình start, để tải dữ liệu câu hỏi và link ảnh về, khi tải xong sẽ hiện nút “Bắt đầu”
  • Màn hình hiện hình ảnh cho họ xem, hiện số level ở trên đầu
  • Màn hình hiện câu hỏi với 4 đáp án
  • Màn hình game over, thông báo trả lời sai, nút cho chơi lại

Theo phân tích trên, chúng ta viết file index.html như sau:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>

<div class="scene" id="welcome">
    <div>
        <div>
            <p id="loadingText">Đang ti...</p>
            <button id="startButton">Bt đầu</button>
        </div>
    </div>
</div>

<div class="scene" id="image">
    <p>Level: <span id="level">1</span></p>
    <div>
        <div id="imageWrapper"></div>
        <div id="imageLoading">Đang ti...</div>
    </div>
</div>

<div class="scene" id="question">
    <div>
        <div>
            <p id="questionText"></p>
            <button id="answerButton0"></button>
            <button id="answerButton1"></button>
            <button id="answerButton2"></button>
            <button id="answerButton3"></button>
        </div>
    </div>
</div>

<div class="scene" id="gameOver">
    <div>
        <div>
            <p>Rt tiếc bn đã tr li sai</p>
            <button id="restartGame">Chơi li</button>
        </div>
    </div>
</div>

</body>
<script type="text/javascript" src="app.js"></script>
</html>

Như các bạn thấy, mình tạo ra 4 div tương ứng với 4 màn hình với class là .scene để dễ dàng ẩn hiện khi cần thiết. Ban đầu tất cả các div này ẩn (display: none) sau đó muốn hiện màn hình nào thì đặt nó là display: block

Object chính của ứng dụng

Object chính của ứng dụng, gọi cho sang mồm là Application super object là object chứa toàn bộ mọi thứ, từ UI, network, data… tóm lại là tất tần tật những cái gì có trong ứng dụng, các thành phần này tương tác với nhau. Kiểu như bạn làm một website, thì ban đầu bạn tạo là 1 class tên là website sau đó trong class này bạn định nghĩa thêm các class con như form, menu, header, footer… để dễ dàng quản lý.

Hãy xem qua code này và đừng vội hiểu gì cả, mình sẽ giải thích ở bên dưới.

var app = function(){
    this.ui       = new ui(this);
    this.network  = new network(this);
    this.data     = [];
    this.level    = 0;
    this.question = null;
    
    var self      = this;

    // Boot all first time load
    this.boot = function(){
        this.ui.setScene('welcome');
        this.ui.listenEvents();
        this.loadData();
    }

    // Load data from api
    this.loadData = function(){
        this.network.get(
            DATA_URL,
            function(data){
                self.data = data;
                self.ui.hide('loadingText');
                self.ui.show('startButton');
            },
            function(error){
                alert(error);
            }
        )
    }

    // Preload an image
    this.loadImage = function(){
        this.ui.show('imageLoading');
        this.network.loadImage(
            self.data[self.level].image,
            function(image){
                self.ui.hide('imageLoading');
                self.ui.showImage(image);
            }
        );
    }

    // Start new game
    this.start = function(){
        this.ui.setScene('image');
        this.loadImage();
    }

    // Pick a ramdom question then show it
    this.pickAndShowQuestion = function(){
        // Pick a random question
        var questions = this.data[this.level].questions;
        var random    = Math.round(Math.random() * (questions.length - 1));
        this.question = questions[random];

        // Show it
        this.ui.showQuestion(this.question);
        this.ui.setScene('question');
    }

    // Check for answer on button click
    this.checkAnswer = function(answer){
        if (answer == this.question.correct){
            this.nextLevel();
        }
        else {
            this.ui.setScene('gameOver');
        }
    }

    // Next level
    this.nextLevel = function(){
        if (this.level == this.data.length - 1){
            alert("Bạn đã chiến thắng");
            this.restart();
            return;
        }

        this.level++;
        this.ui.updateLevelNumber();
        this.ui.setScene('image');
        this.loadImage();
    }

    // Restart game
    this.restart = function(){
        this.level = 0;
        this.ui.updateLevelNumber();
        this.ui.setScene('welcome');
    }
    
}

Theo code trên, ban đầu mình tạo ra một object tên là app, đừng hỏi mình tại sao lại là var app = function() nhé, vì trong javascript mọi thứ đều là object cả. Hiện tại ES6 đã hõ trợ từ khóa Class thì phải, nhưng sợ không tương thích nên chưa dám dùng :))

Trong object này, mình tạo ra các object như ui để quản lý giao diện, network để làm các việc liên quan đến lấy dữ liệu, các object này mình sẽ giải thích cụ tỉ ở bên dưới.

Ngoài ra mình còn định nghĩa data là một mảng để chứa data đã lấy về, data là một file json bạn có thể xem ở đây. level là một con số để lưu level hiện tại, question để chứa câu hỏi hiện tại, để thuận tiện cho thao tác :)

Giải thích ý nghĩa các method:

  • Boot: Đặt tên là boot cho nó có vẻ chuyên nghiệp tí, đây là hàm chạy lần đầu tiên, hiện màn hình welcome, load data và listen các event cho các nút.
  • loadData: Đây là hàm lấy dữ liệu từ server về, nó sử dụng object network, sẽ giải thích sau. Sau khi lấy được dữ liệu thì ẩn dòng chữ “Loading data” và hiện nút start lên.
  • loadImage: Tải trước một hình ảnh trước khi hiện nó lên, nó sẽ tải hình ảnh ở level hiện tại.
  • pickAndShowQuestion: Chọn ngẫu nhiên một câu hỏi và hiện nó lên màn hình câu hỏi.
  • checkAnswer: Hàm này được gọi khi người dùng bấm trả lời câu hỏi, nó sẽ kiểm tra xem trả lời đúng chưa, nếu đúng, thì nhảy qua level tiếp theo, nếu sai thì hiện màn hình game over.
  • nextLevel: Tải hình ảnh và nhảy sang level tiếp theo.
  • restart: Hàm này được gọi khi người dùng bấm nút “chơi lại”, reset level rồi chuyển về màn hình welcome.

Network object

Đây là object đảm nhiệm vai trò lấy dữ liệu từ server về, tải trước ảnh để hiện chữ “loading image”. Đây chỉ là request ajax thuần mà các bạn đã biết. Sau khi tải dữ liệu xong, nó gọi hàm successCallback

var network = function(app){

    // Get data, this function use very-basic ajax request
    this.get = function(url, successCallback, errorCallback){
        var request = new XMLHttpRequest();
        request.open('GET', url);
        request.onload = function() {
            if (request.status === 200) {
                successCallback(JSON.parse(request.responseText));
            }
            else {
                errorCallback(request.status);
            }
        };
        request.send();
    }

    // Preload an image
    this.loadImage = function(imageUrl, successCallback, errorCallback){
        var img = new Image();
        img.onload = function(){
            successCallback(img);
        }
        img.onerror = function(){
            errorCallback();
        }
        img.src = imageUrl;
    }
}

UI Object

UI (user interface) là object đảm nhiện vai trò quản lý giao diện, nôm na là ẩn cái này hiện cái kia, đồng thời nhận event khi người dùng nhấn nút này nút nọ, rồi gọi hàm ở object chính. Có lẽ không cần giải thích nhiều vì ở đây mình chỉ sử dụng các hàm javascript tương tác với DOM cơ bản thôi.

var ui = function(app){

    this.app = app;

    var self = this;

    // Get HTML element by id
    this.getElement = function(elementId){
        return document.getElementById(elementId);
    }

    // Show an element
    this.show = function(elementId){
        this.getElement(elementId).style.display = 'block';
    }

    // Hide an element
    this.hide = function(elementId){
        this.getElement(elementId).style.display = 'none';
    }

    // Set current scene
    this.setScene = function(sceneName){
        // Get all elements have class .scene
        var elements = document.getElementsByClassName('scene');
        // Then hide them
        [].forEach.call( elements, function(element) {
            element.style.display = 'none';
        });
        // Then show element has id screenName
        this.show(sceneName);
    }

    // Show image
    this.showImage = function(image){
        this.getElement('imageWrapper').innerHTML = '';
        this.getElement('imageWrapper').appendChild(image);
    }

    // Update level number on top of the app
    this.updateLevelNumber = function(){
        this.getElement('level').innerHTML = this.app.level + 1;
    }

    // Show question text and 4 answer buttons
    this.showQuestion = function(question){
        this.getElement('questionText').innerHTML = question.question;
        this.getElement('answerButton0').innerHTML = question.answers[0];   
        this.getElement('answerButton1').innerHTML = question.answers[1];   
        this.getElement('answerButton2').innerHTML = question.answers[2];   
        this.getElement('answerButton3').innerHTML = question.answers[3];
    }

    // Listen click events
    this.listenEvents = function(){
        // Click start button
        this.getElement('startButton').addEventListener('click', function(){
            self.app.start();
        });

        // Click #image div
        this.getElement('image').addEventListener('click', function(){
            self.app.pickAndShowQuestion();
        });

        // Click restartGame button
        this.getElement('restartGame').addEventListener('click', function(){
            self.app.restart();
        });

        // Listen click event for 4 answer buttons
        this.getElement('answerButton0').addEventListener('click', function(){
            self.app.checkAnswer(0);
        });
        this.getElement('answerButton1').addEventListener('click', function(){
            self.app.checkAnswer(1);
        });
        this.getElement('answerButton2').addEventListener('click', function(){
            self.app.checkAnswer(2);
        });
        this.getElement('answerButton3').addEventListener('click', function(){
            self.app.checkAnswer(3);
        });
    }
}

Now we are ready to go

Sau khi đã định nghĩa hết các object, mình tạo luôn object chính ở cuối file app.js trong sự kiện onload của window và gọi ngay hàm boot

/*
 * Now we are ready to go
 */
window.onload = function(){
    var girlPuzzle = new app();
    girlPuzzle.boot();
}

Kết bài

Không cần sử dụng kỹ thuật gì cao siêu, cũng không dùng framework nào cả, thông qua bài viết này bạn đã tự mình học được các kiến thức javascript căn bản, hiểu được javascript làm việc với các thành phần DOM như thế nào. Trong phần sau, mình sẽ cùng các bạn mông má lại cái app này cho đẹp hơn, chứ đưa lên app store xấu như thế này người ta cười cho.

Bạn có thể xem và tải code ở link github trên về chạy thử để hiểu rõ hơn. Hẹn gặp các bạn ở phần 2.

Tag: Lập trình javascript,
Category: Chuyện lập trình,

Video liên quan: