Tìm hiểu Android và viết ứng dụng minh hoạt Game TouchtheBall
1] Xin chào Android 3
Hệ điều hành mở 4
Các ứng dụng có sẵn trên Android 5
Truy cập phần cứng ···
Dịch vụ chạy nền 6
SQLite Database ···
Hệ thống thông báo ···
Tối ưu hóa bộ nhớ và quản l{ tiến trình ···
Android software development kit 7
Kiến trúc ứng dụng 8
Các thư viện của Android ···
2] Xây dựng ứng dụng đầu tiên 11
Các cài đặt cần thiết 12
Tạo mới một Android Project 15
Ba loại ứng dụng trong Android 19
blic AnimationView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event){ return super.onTouchEvent(event); } Vòng tuần hoàn của một game thông thường T ì m h i ể u A n d r o i d | 42 } Phương thức on TouceEvent() được override lại từ lớp cha. Phương thức này là nơi đặt các đoạn code để xử l{ sự kiện. Vì lớp AnimationView mới tạo ra là một View cho nên ta có thể xem nó như là một thẻ (xml tag) và có thể đưa nó trực tiếp vào file layout của ứng dụng (xem ở các chương trên). Mở file main.xml trong thư mục res/layout/ và sửa lại như sau: <com.example.touchtheball.AnimationView xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent"/> Như vậy giờ khi MainActivity được chạy, trên màn hình chỉ có duy nhất một thành phần đó là AnimationView, AnimatinoView này hiển thị toàn mà hình và sẽ vẽ ra toàn bộ hình ảnh trong game. Bằng cách này ta không phải sửa bất kz một dòng code nào trong file MainActivity.java, chỉ để như mặc định Eclipse đã tạo sẵn, vì bây giờ mọi việc xử l{ trong game đã có lớp AnimationView đảm nhiệm. AnimationThread.java Xem lại vòng tuần hoàn của một game, ta thấy ở đó có sự lặp lại vô tận của công việc vẽ và tính toán các số liệu. Điều này đòi hỏi phải có một sự xử l{ đa tiến trình, một tiến trình làm nhiệm vụ tính toán và vẽ, một tiến trình khác làm nhiệm vụ chờ các sự kiện xảy ra (cụ thể trong game là sự kiện touch màn hình) để xử l{. Bên trên ta đã tạo ra lớp AnimationView để hiển thị hình ảnh, và cũng để xử l{ sự kiện, tuy nhiên như thế thì chưa thể chạy đa tiến trình, giờ ta sẽ tạo ra một lớp mới có nhiệm vụ đảm nhiệm xử l{ và hiển thị hình ảnh thay cho lớp AnimationView. Nhưng cần lưu { rằng ta chỉ đặt duy nhất một thẻ vào file XML Layout, cho nên bất kz lớp nào muốn hiển thị hình ảnh lên màn hình thì đều phải thông qua tham chiếu tới AnimationView. class AnimationThread extends Thread { // Biến kiểm soát việc lặp T ì m h i ể u A n d r o i d | 43 private boolean mRun = true; // Biến tham chiếu tới AnimationView – dùng để hiển thị ra màn hình private SurfaceHolder mSurfaceHolder; public AnimationThread(SurfaceHolder surfaceHolder) { this.mSurfaceHolder = surfaceHolder; } public void stop(){ this.mRun = false; } @Override public void run(){ while(this.mRun){ Canvas canvas = this.mSurfaceHolder.lockCanvas(null); // xóa mọi hình ảnh trước đó bằng cách tô màu trắng canvas.drawColor(Color.WHITE); // tiếp tục gọi các hàm vẽ khác... } } public void doTouch(MotionEvent event){ if(event.getAction() == MotionEvent.ACTION_DOWN){ // thực hiện các hàm tính toán... } } } Trong lớp AnimationThread vừa tạo có 3 phương thức quan trọng cần chú {. run() Phương thức này làm nhiệm vụ hiển thị các hình ảnh ra màn hình. Trong phương thức này một vòng lặp while() được sử dụng để duy trì quá trình tuần hoàn (vẽ đi vẽ lại). stop() Phương thức này dùng khi muốn dừng vòng lặp while() ở phương thức run(). doTouch() Phương thức này làm nhiệm vụ xử l{ khi có sự kiện xảy ra. T ì m h i ể u A n d r o i d | 44 Theo như thiết kế lớp AnimationThread ở trên thì phương thức doTouch() dùng để xử l{ sự kiện, nhưng lưu { lớp AnimationThread không làm nhiệm vụ chờ sự kiện xảy ra. Theo như phân tích ở trên về vấn đề đa tiến trình: AnimationThread giờ đây sẽ làm nhiệm vụ hiển thị hình ảnh, AnimationView sẽ làm nhiệm vụ chờ sự kiện. Nhưng mỗi khi có một sự kiện xảy ra AnimationView sẽ không trực tiếp xử l{ mà sẽ gọi lại phương thức doTouch() của AnimationThread để xử l{. Phương thức khởi tạo AnimationThread() cần một SurfaceHolder để tham chiếu tới vùng canvas cần vẽ, và tham số này sẽ được lấy từ AnimationView. Ta cần thay đổi lớp AnimationView một chút: (những dòng in đậm mới được thêm vào) class AnimationView extends SurfaceView { private AnimationThread thread; public AnimationView(Context context, AttributeSet attrs) { super(context, attrs); SurfaceHolder holder = this.getHolder(); holder.addCallback(this); this.thread = new AnimationThread(holder); } @Override public boolean onTouchEvent(MotionEvent event){ thread.doTouch(event); return super.onTouchEvent(event); } } Như vậy giờ ta đã giải quyết được vấn đề cốt lõi nhất để có thể xây dựng một game đơn giản. Xây dựng lớp Ball Công việc tiếp theo là xây dựng lớp Ball. Theo như tinh thần hướng đối tượng thì trong game, mỗi quả bóng sẽ là một đối tượng riêng biệt. Như vậy ta cần đi xây dựng một lớp định nghĩa “thế nào là một quả bóng”, đồng thời lớp này kiêm luôn nhiệm vụ T ì m h i ể u A n d r o i d | 45 vẽ quả bóng (vẽ chính nó) ra màn hình cũng như kiểm tra tọa độ (sẽ được sử dụng trong doTouch()). Lớp Ball được thiết kế như sau: public class Ball { public Ball(Canvas c){} public void draw(Canvas c, Paint p){} public void update(Canvas c){} public Boolean check(int x, int y){} public Boolean checkColor(int color){} public int score(){} } Giải thích các phương thức trong lớp Ball: Ball() Phương thức khởi tạo. Tạo một quả bóng với màu sắc, kích thước, vị trí, hướng di chuyển và tốc độ ngẫu nhiên. Phương thức này cần truyền vào tham số là một Canvas bởi vì khi tạo mới một quả bóng, tọa độ của quả bóng sẽ được xác định ngẫu nhiên, nhưng vẫn cần phải nằm trong vùng màn hình nên ta cần truyền vào Canvas hiện tại để xác định kích thước của Canvas đó, từ đó xác định tọa độ cho quả bóng. draw() Phương thức này dùng để vẽ quả bóng ra màn hình, tham số cần truyền vào là Canvas được sử dụng để vẽ hình lên, và một bút vẽ (Paint) hiện tại. Phương thức này sẽ được gọi bởi phương thức run() trong lớp AnimationThread. update() Phương thức này sẽ xác định lại tọa độ mới cho quả bóng, nhờ phương thức này quả bóng mới có thể bay qua lại trên màn hình theo một quỹ đạo nhất định. Phương thức này cần truyền vào một Canvas, mục đích là để xác định kích thước màn hình như phương thức khởi tạo. check() Phương thức này trả về một giá trị Boolean , được dùng để kiểm tra tọa độ truyền vào (x, y) có nằm trong quả bóng hay không. Được gọi bởi phương thức doTouch() trong lớp AnimationThread. checkColor() Phương thức này trả về một giá trị Boolean, dùng để kiểm tra màu truyền vào (color) có giống với màu hiện tại của quả bóng hay không. Được gọi bởi phương T ì m h i ể u A n d r o i d | 46 thức doTouch() trong lớp AnimationThread. score() Phương thức này trả về một số kiểu Int, dùng để xác định lượng điểm số tương ứng với kích thước quả bóng. Bán kính càng nhỏ thì lượng điểm càng lớn. Sử dụng lớp Ball Sau khi xây dựng xong lớp Ball, ta tiến hành thêm vào code lớp AnimationThread tại một số chỗ. Đầu tiên là thêm vào vài thuộc tính private: // Bút vẽ private Paint mPaint; // Màu hiện tại private int currentColor; // Mảng lưu các quả bóng private ArrayList balls; private int maxball = 15; Tiếp theo là thêm vào phương thức khởi tạo đoạn code khởi tạo bút vẽ và tạo một mảng các quả bóng: public AnimationThread(SurfaceHolder surfaceHolder) { this.mSurfaceHolder = surfaceHolder; // khởi tạo bút vẽ this.mPaint = new Paint(); this.mPaint.setAntiAlias(true); this.mPaint.setARGB(255,255,255,255); // khởi tạo các quả bóng Canvas canvas = this.mSurfaceHolder.lockCanvas(null); for(int i=0; i<this.maxball; i++) this.balls.add(new Ball(canvas)); } Tiếp theo là thêm vào trong phương thức run() những dòng được in đậm: T ì m h i ể u A n d r o i d | 47 public void run(){ while(this.mRun){ Canvas canvas = this.mSurfaceHolder.lockCanvas(null); // xóa mọi hình ảnh trước đó bằng cách tô màu trắng canvas.drawColor(Color.WHITE); for(int i=0;i<this.balls.size();i++){ // di chuyển quả bóng sang vị trí mới, sau đó là vẽ this.balls.get(i).update(canvas); this.balls.get(i).draw(canvas, this.mPaint); } } } Tới đây ta chạy thử game, các quả bóng đã xuất hiện và bay qua lại trên màn hình. Xử lý sự kiện Bước tiếp theo ta sẽ thêm đoạn code xử l{ sự kiện khi người chơi chạm vào màn hình, ta sẽ kiểm tra coi người chơi có chạm vào quả bóng nào không (nếu có nhiều quả bóng nằm đè lên nhau, ưu tiên chọn quả bóng nằm bên trên). Nếu có chạm vào một quả bóng thì ta sẽ tiếp tục kiểm tra màu của quả bóng đó với màu yêu cầu hiện tại có khớp với nhau không. Ta thêm vào trong phương thức doTouch() những dòng được in đậm: public void doTouch(MotionEvent event){ if(event.getAction() == MotionEvent.ACTION_DOWN){ int x = (int)event.getX(); int y = (int)event.getY(); int index = -1; // vì ưu tiên những quả bóng ở trên nên ta sẽ dùng vòng lặp ngược // vì khi vẽ các quả bóng ta dùng vòng lặp xuôi // nên các quả bóng sau sẽ được vẽ đè lên trên for(int i=this.balls.size()-1; i>=0; i--) if(this.balls.get(i).check(x, y)){ index = i; T ì m h i ể u A n d r o i d | 48 break; } if(index != -1){ if(this.balls.get(index).checkColor(this.currentColor)){ // code xử lý nếu quả bóng đúng màu } } } } Như vậy là ta đã cơ bản xây dựng được game. Để game có thể chơi được như những hình minh họa ban đầu ta còn cần phải code thêm nữa. Để có thể hiện ra điểm số khi chạm vào các quả bóng (hiệu ứng điểm hiện ra và biến mất từ từ) ta cần phải code thêm lớp TextScore. Tuy nhiên bài viết chỉ nhằm mục đích gợi mở những nguyên tắc, những lớp cơ bản ban đầu nên xin dừng lại tại đây. Source code hoàn chỉnh của game Touch The Ball có thể download tại Kết thúc Trong chương cuối này ta đã cùng đi xây dựng game đơn giản, đây như là bài thực hành vận dụng các kiến thức mà ta đã tìm hiểu trong suốt tài liệu. Đồng thời biết thêm cách một game thông thường hoạt động như thế nào. Để tìm hiểu sâu hơn về Android, cách tốt nhất là đọc tài liệu từ chính trang chủ tại
File đính kèm:
- Tìm hiểu Android và viết ứng dụng minh hoạt Game TouchtheBall.pdf