Saturday, October 15, 2016

Vòng đời ứng dụng Android - Android Life Cycle

Vòng đời của một ứng dụng Android là kiến thức cơ bản mà bất kỳ lập trình viên Android nào cần phải biết. Đây là kiến thức rất quan trọng để phát triển các ứng dụng Android
Sau đây mình xin trình bày một ứng dụng  nhỏ  Life Cycle App về vòng đời của một Android Application
Các thành phần chính của ứng dụng: 
  1. Một Toast-msg để hiện thị trạng thái của ứng dụng : onCreate, onStart, onPause, onResume, onDestry, onRestart, onStop
  2. Một EditText: cho người dùng nhập màu sắc của background
  3.  SharePreferences lưu trạng thái của ứng dụng, mỗi khi ứng dụng tắt đi bật lại sẽ lưu màu background lần cuối cùng mà người dùng nhập
  4.  Button Exit: kết thúc ứng dụng
  5. TextView: hiện thị màu background hiện tại
  6. Khi chạy chương trình, nhiệm vụ của bạn là quan sát trình tự của thông điệp hiển thị khi ứng dụng:
  • Load lần đầu tiên
  • Click HOME button
  • Click BACK hoặc Exit button
  • Chạy lại ứng dụng khi xét màu bacground
Bắt đầu code nào :D

Bước 1: 
  Tạo ứng dụng android trên Android Studio tên Life Cycle Android.  Hiện tại mình đang dùng phiên bản Android Studio 2.2.1
Bước 2: 
  Tạo giao diện cho ứng dụng trong file activity_main.xml gồm 3 thành phần: 
  • Button
  • EditText
  • TextView
activity_main.xml


 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools"  
   android:id="@+id/activity_main"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   android:paddingBottom="@dimen/activity_vertical_margin"  
   android:paddingLeft="@dimen/activity_horizontal_margin"  
   android:paddingRight="@dimen/activity_horizontal_margin"  
   android:paddingTop="@dimen/activity_vertical_margin"  
   android:orientation="vertical"  
   tools:context="com.hust.tuanbk.lifecycleandroid.MainActivity">  
   <EditText  
     android:layout_width="match_parent"  
     android:layout_height="wrap_content"  
     android:hint="Input Background(Red, Green, Blue, While)"  
     android:id="@+id/inputColor"/>  
   <Button  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:id="@+id/btExit"  
     android:layout_marginTop="20dp"  
     android:layout_gravity="center"  
     android:text="Exit"/>  
   <TextView  
     android:id="@+id/textView"  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:text="Color"/>  
 </LinearLayout>  

Trong MainActivity.java
Có các hàm chính sau

  1. Xử lý sự kiện cho Button Exit

  2.   //set Button Event  
         btExit.setOnClickListener(new View.OnClickListener() {  
           @Override  
           public void onClick(View v) {  
             finish();  
           }  
         });  
    

  3. Lưu trạng thái của ứng dụng dùng SharePreferences

  4.   private void saveStateData(String chosenColor){  
         SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE);  
         SharedPreferences.Editor myPrefEditor = myPrefContainer.edit();  
         String key = "chosenBackgroundColor";  
         String value = textView.getText().toString();  
         myPrefEditor.putString(key, value);  
         myPrefEditor.commit();  
       } //save data  
    

  5. Update trạng thái của ứng dụng

  6.  private void updateMeUsingSavedStateData() {  
        // (in case it exists) use saved data telling backg color  
         SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE);  
         String key = "chosenBackgroundColor";  
         String defaultValue = "white";  
         if (( myPrefContainer != null ) && myPrefContainer.contains(key)){  
           String color = myPrefContainer.getString(key, defaultValue);  
           setBackgroundColor(color, myScreen);  
         }  
       }//updateMeUsingSavedStateData  
    

Ta sẽ override các hàm trạng thái của ứng dụng: show Toast trong hàm Lưu trạng thái trong hàm onPause() và update trạng thái ứng dụng trong hàm onStart()

   @Override  
   protected void onDestroy() {  
     super.onDestroy();  
     Toast.makeText(getApplicationContext(), "onDestroy", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onPause() {  
     super.onPause();  
     //save state data (background color)  
     String chosenColor = textView.getText().toString();  
     saveStateData(chosenColor);  
     Toast.makeText(getApplicationContext(), "onPause", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onRestart() {  
     super.onRestart();  
     Toast.makeText(getApplicationContext(), "onRestart", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onStart() {  
     super.onStart();  
     updateMeUsingSavedStateData();  
     Toast.makeText(getApplicationContext(), "onStart", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onStop() {  
     super.onStop();  
     Toast.makeText(getApplicationContext(), "onStop", Toast.LENGTH_LONG).show();  
   }  

MainActivity.java

 package com.hust.tuanbk.lifecycleandroid;  
 import android.app.Activity;  
 import android.content.SharedPreferences;  
 import android.os.Bundle;  
 import android.support.v7.app.AppCompatActivity;  
 import android.text.Editable;  
 import android.text.TextWatcher;  
 import android.view.View;  
 import android.widget.Button;  
 import android.widget.EditText;  
 import android.widget.LinearLayout;  
 import android.widget.TextView;  
 import android.widget.Toast;  
 import java.util.Locale;  
 public class MainActivity extends AppCompatActivity {  
   Button btExit;  
   EditText editText;  
   TextView textView;  
   LinearLayout myScreen;  
   String PREFNAME = "myPrefFile";  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_main);  
     btExit = (Button) findViewById(R.id.btExit);  
     editText = (EditText) findViewById(R.id.inputColor);  
     textView = (TextView) findViewById(R.id.textView);  
     myScreen = (LinearLayout)findViewById(R.id.activity_main);  
     //set Button Event  
     btExit.setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         finish();  
       }  
     });  
     editText.addTextChangedListener(new TextWatcher() {  
       @Override  
       public void beforeTextChanged(CharSequence s, int start, int count, int after) {  
       }  
       @Override  
       public void onTextChanged(CharSequence s, int start, int before, int count) {  
       }  
       @Override  
       public void afterTextChanged(Editable s) {  
         //set background to selected color  
         String chosenColor = s.toString().toLowerCase(Locale.US);  
         textView.setText(chosenColor);  
         setBackgroundColor(chosenColor, myScreen);  
       }  
     });  
     Toast.makeText(getApplicationContext(), "onCreate", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onDestroy() {  
     super.onDestroy();  
     Toast.makeText(getApplicationContext(), "onDestroy", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onPause() {  
     super.onPause();  
     //save state data (background color)  
     String chosenColor = textView.getText().toString();  
     saveStateData(chosenColor);  
     Toast.makeText(getApplicationContext(), "onPause", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onRestart() {  
     super.onRestart();  
     Toast.makeText(getApplicationContext(), "onRestart", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onStart() {  
     super.onStart();  
     updateMeUsingSavedStateData();  
     Toast.makeText(getApplicationContext(), "onStart", Toast.LENGTH_LONG).show();  
   }  
   @Override  
   protected void onStop() {  
     super.onStop();  
     Toast.makeText(getApplicationContext(), "onStop", Toast.LENGTH_LONG).show();  
   }  
   private void saveStateData(String chosenColor){  
     SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE);  
     SharedPreferences.Editor myPrefEditor = myPrefContainer.edit();  
     String key = "chosenBackgroundColor";  
     String value = textView.getText().toString();  
     myPrefEditor.putString(key, value);  
     myPrefEditor.commit();  
   } //save data  
   private void updateMeUsingSavedStateData() {  
    // (in case it exists) use saved data telling backg color  
     SharedPreferences myPrefContainer = getSharedPreferences(PREFNAME, Activity.MODE_PRIVATE);  
     String key = "chosenBackgroundColor";  
     String defaultValue = "white";  
     if (( myPrefContainer != null ) && myPrefContainer.contains(key)){  
       String color = myPrefContainer.getString(key, defaultValue);  
       setBackgroundColor(color, myScreen);  
     }  
   }//updateMeUsingSavedStateData  
   private void setBackgroundColor(String chosenColor, LinearLayout myScreen) {  
     //hex color codes: 0xAARRGGBB AA:transp, RR red, GG green, BB blue  
     if (chosenColor.contains("red"))  
       myScreen.setBackgroundColor(0xffff0000); //Color.RED  
     if (chosenColor.contains("green"))  
       myScreen.setBackgroundColor(0xff00ff00); //Color.GREEN  
     if (chosenColor.contains("blue"))  
       myScreen.setBackgroundColor(0xff0000ff); //Color.BLUE  
     if (chosenColor.contains("white"))  
       myScreen.setBackgroundColor(0xffffffff); //Color.WHITE  
   }  
 }  

Hình ảnh demo ứng dụng



Kết Luận: Vậy là mình đã demo xong chương trình, các bạn có thể code và tự kiểm tra các trạng thái của ứng dụng nhé

Friday, October 14, 2016

Học máy trong lập trình Android - Face Detection

Có rất nhiều APIs cho Học Máy - Machine Learning trên cloud và mobile
Trong bài này mình sẽ sử dụng Mobile Vision APIs. Mobile Version API hiện tại bao gồm 3 loại: Face Detection API, Barcode Detection API và the Text API

Face Detection API:
API này sử dụng để phát hiện và theo dõi khuôn mặt của người trong ảnh hoặc video nhưng nó chưa cung cấp khả năng nhận dạng khuôn mặt. Nó cho phép phát hiện các điểm trên mặt như mắt, mũi và miệng và phân loại mặt. Phân loại mặt được sử dụng để kiểm tra các đặc điểm như mặt đang cười hoặc nhắm mắt...API này cũng phát hiện được mặt ở các góc độ khác nhau

Hướng dẫn cụ thể
Bước 1:
Dùng Android Studio để tạo Project
Hướng dẫn cài đặt Android Studio
Mình dùng bản 2.2.1
Tạo mới 1 project: AndroidFaceDetection
Import Google Play Services SDK trong file build.gradle

 compile 'com.google.android.gms:play-services-vision:9.6.1'  


Để kích hoạt thư viện có sẵn đại diện cho face dectection, thêm thẻ meta-data sau vào manifest file:

 <meta-data  
   android:name="com.google.android.gms.vision.DEPENDENCIES"  
   android:value="face"/>  

Bước 2: Viết code
File giao diện activity_main.xml:
Tạo một Button và một ImageView


  • Button: khởi tạo xử lý dectetion
  • ImageView: ảnh xử lý, ở đây mình lấy ảnh cố định, bạn có thể xử lý bằng cách chụp ảnh từ camera hoặc xử lý lấy ảnh có sẵn trong điện thoại


 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools"  
   android:id="@+id/activity_main"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   android:paddingBottom="@dimen/activity_vertical_margin"  
   android:paddingLeft="@dimen/activity_horizontal_margin"  
   android:paddingRight="@dimen/activity_horizontal_margin"  
   android:paddingTop="@dimen/activity_vertical_margin"  
   android:orientation="vertical"  
   tools:context="com.hust.tuanbk.facedetection.MainActivity">  
   <Button  
     android:id="@+id/detection"  
     android:layout_width="100dp"  
     android:layout_height="wrap_content"  
     android:text="Detection"  
     />  
   <ImageView  
     android:layout_marginTop="20dp"  
     android:id="@+id/face"  
     android:src="@drawable/face"  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content" />  
 </LinearLayout>  

MainActivity.java

 package com.hust.tuanbk.facedetection;  
 import android.graphics.Bitmap;  
 import android.graphics.BitmapFactory;  
 import android.graphics.Canvas;  
 import android.graphics.Color;  
 import android.graphics.Paint;  
 import android.graphics.RectF;  
 import android.graphics.drawable.BitmapDrawable;  
 import android.os.Bundle;  
 import android.support.v7.app.AlertDialog;  
 import android.support.v7.app.AppCompatActivity;  
 import android.util.SparseArray;  
 import android.view.View;  
 import android.widget.Button;  
 import android.widget.ImageView;  
 import com.google.android.gms.vision.Frame;  
 import com.google.android.gms.vision.face.Face;  
 import com.google.android.gms.vision.face.FaceDetector;  
 import com.google.android.gms.vision.face.Landmark;  
 public class MainActivity extends AppCompatActivity {  
   Button btDetection;  
   ImageView imageFace;  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_main);  
     imageFace = (ImageView) findViewById(R.id.face);  
     btDetection = (Button) findViewById(R.id.detection);  
     btDetection.setOnClickListener(new View.OnClickListener() {  
       @Override  
       public void onClick(View v) {  
         // create a new BitmapFactory.Options object and set inmutable to true.  
         // This is to ensure that the bitmap is mutable so that we are able  
         // to programmatically apply effects to it  
         BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();  
         bitmapOptions.inMutable = true;  
         Bitmap defaultBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.face, bitmapOptions);  
         //Create a Paint object and set the style to stroke.  
         // This ensures that the shape is not filled in because we want to see  
         // the parts of the head that make it into the rectangle  
         Paint rectPaint = new Paint();  
         rectPaint.setStrokeWidth(5);  
         rectPaint.setColor(Color.CYAN);  
         rectPaint.setStyle(Paint.Style.STROKE);  
         //canvas to display our bitmap  
         Bitmap temporaryBitmap = Bitmap.createBitmap(defaultBitmap.getWidth(), defaultBitmap  
             .getHeight(), Bitmap.Config.RGB_565);  
         Canvas canvas = new Canvas(temporaryBitmap);  
         canvas.drawBitmap(defaultBitmap, 0, 0, null);  
         // get to point where we use the FaceDectector API  
         FaceDetector faceDetector = new FaceDetector.Builder(MainActivity.this)  
             .setTrackingEnabled(false)  
             .setLandmarkType(FaceDetector.ALL_LANDMARKS)  
             .build();  
         //Check if the face detector is operational already.  
         // There’s a possibility that it won’t work the first time because a library needs to be downloaded  
         // to the device and it might not have been completed in time when you need to use it.  
         if (!faceDetector.isOperational()) {  
           new AlertDialog.Builder(MainActivity.this)  
               .setMessage("Face Detector could not be set up on your device :(")  
               .show();  
           return;  
         }  
         //create a frame using the default bitmap and call on the face detector to get the face objects.  
         Frame frame = new Frame.Builder().setBitmap(defaultBitmap).build();  
         SparseArray<Face> sparseArray = faceDetector.detect(frame);  
         //drawn rectangle over the faces  
         for (int i = 0; i < sparseArray.size(); i++) {  
           Face face = sparseArray.valueAt(i);  
           float left = face.getPosition().x;  
           float top = face.getPosition().y;  
           float right = left + face.getWidth();  
           float bottom = right + face.getHeight();  
           float cornerRadius = 2.0f;  
           RectF rectF = new RectF(left, top, right, bottom);  
           canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, rectPaint);  
           for (Landmark landmark : face.getLandmarks()) {  
             int x = (int) (landmark.getPosition().x);  
             int y = (int) (landmark.getPosition().y);  
             float radius = 10.0f;  
             canvas.drawCircle(x, y, radius, rectPaint);  
           }  
         }  
         // set ImageView from our layout after which we release the face detector.  
         imageFace.setImageDrawable(new BitmapDrawable(getResources(), temporaryBitmap));  
         faceDetector.release();  
       }  
     });  
   }  
 }  

Kết quả ta được như sau:

Lưu ý: Khi chạy trên máy thật, lần đầu chạy sẽ không phát hiện được ngay vì nó còn download thư viện xuống.
Kết luận: Vậy là đã xong cảm ơn các bạn đã quan tâm. Nhớ theo dõi blog của mình thường xuyên nhé :D
Tham khảo: Moyinoluwa Adeyemi