Tuesday, May 14, 2013
Wednesday, June 8, 2011
Public ứng dụng của bạn lên Android Market.
Trước hết, bạn phải trả lời một số câu hỏi sau:
1, Bạn đã test kỹ càng ứng dụng của bạn một cách rộng rãi?
Nếu câu trả lời là chưa thì bạn hãy quay trở lại với việc test cho thật kỹ càng, không chỉ trên emulator, bạn cần kiểm tra chúng trong thực tế, trên các thiết bị thật. Nếu có điều kiện, bạn hãy kiểm tra trên các thiết bị khác nhau, của các nhà cung cấp khác nhau, trên các phiên bản OS khác nhau.
Một lỗi nhỏ trong code của bạn có thể làm giảm doanh số bán hàng và danh tiếng của bạn.
2, Ứng dụng của bạn có hiệu năng tốt không?
Hiệu năng là rất quan trọng, đặc biệt nếu ứng dụng của bạn là 1 game. Nếu ứng dụng của bạn không thể đáp ứng trong một trường hợp nào đó, hãy xem lại và tối ưu hóa chúng nếu có thể.
3 Ứng dụng của bạn có tương thích với nhiều phiên bản khác nhau của hệ điều hành?
Hãy nhìn vào biểu đồ bên dưới để có yêu cầu min SDK phù hợp với ứng dụng của bạn.
Tiếp theo chúng ta phải chuẩn bị cho ứng dụng của mình sẵn sàng lên Android Market.
1, Yêu cầu các quyền cần thiết.
Hãy chắc chắn rằng bạn đã yêu cầu các quyền cần thiết trong file cấu hình AndroidManifest.xml. Nếu không ứng dụng của bạn có thể chạy không thật sự suôn sẻ. Ví dụ:
2, Tên và icon của ứng dụng.
Hãy chọn một tên và icon thật đặc biệt cho ứng dụng của bạn, bạn có thể cấu hình nó trong file cấu hình AndroidManifest.xml. Ví dụ:
3, Cấu hình version của ứng dụng.
Hãy cấu hình version ứng dụng của bạn nhằm việc update sau này. Thường nó được cấu hình bằng số nguyên và bắt đầu bằng 1, nhưng bạn có thể tùy biến theo ý bạn có thể là 1.0.0b1 hoặc 2.4.13... Ví dụ:
4, Cấu hình phiên bản hệ điều hành tương thích.
Dựa vào API bạn sử dụng để phát triển ứng dụng, bạn hãy cấu hình phiên bản hệ điều hành tương thích bằng các tham số.
android:minSdkVersion: Phiên bản thấp nhất.
android:targetSdkVersion: Bạn phát triển ứng dụng cho phiên bản này.
android:maxSdkVersion: Phiên bản cao nhất có thể chạy ứng dụng, cái này nên bỏ qua.
5, Xóa bỏ file và log.
Rà soát toàn bộ ứng dụng của bạn, hãy xóa bỏ những file không cần thiết, lời gọi log nhằm debug dữ liệu cá nhân, các file không phải là file resource...
6, Export ứng dụng của bạn với certificate.
Đây là bước quan trọng bới ứng dụng của bạn được đảm bảo ứng dụng của bạn đã được xác thực, vì thế bạn nên tạo một mật khẩu đủ mạnh cho private key của bạn và hãy giữ chúng cẩn thận.
Nếu bạn dùng Eclipse IDE, bạn có thể sử dụng Export Wizard trong eclipse để làm điều này.
a, Bạn chọn File > Export
b, Trong cây thư mục bạn chọn Android -> Export Android Application.
c, Hoàn thành các bước wizard (Bạn có thể xem hình vẽ).
Cách khác bạn có thể sử dụng các tools sau keytool, jarsigner, zipalign...
Bước cuối cùng là đăng ký làm một Market Publisher.
1, Đăng kýa, Đăng ký dưới một Publisher và cấu hình các thông tin cá nhân của bạn http://market.android.com/publish.
b, Đọc và đồng ý các điều khoản của Android Market http://www.android.com/us/developer-distribution-agreement.html.
c, Trả phí là $25 USD qua Google Checkout.
2, Upload ứng dụng của bạn.
Đăng nhập vào tài khoản publisher của bạn, chọn "Upload an Application" điền các thông tin trên form, nếu ứng dụng của bạn có phí, bạn cần đăng ký Google Checkout Merchant. Cuối cùng chọn "Publish" để đăng ứng dụng của bạn.
OK rồi! cảm giác vui vui khi upload ứng dụng đầu tiên của mình lên android market nào... :D
Thursday, May 12, 2011
Chỉ dẫn lập trình game 3D trên Android (P6)
Bài trước mình đã giới thiệu về Java Native Interface. Nhưng hình như các bạn không thể thực hành được bài này.
Và có những email gửi về cho mình nhờ chỉ dẫn cách chạy JNI như thế nào, các bạn đó chạy ví dụ của google mà không được.
Hôm nay tôi sẽ chỉ các bạn cách chạy native như thế nào.
Như các bạn đã biết lập trình game chúng ta cần tận dụng sức mạnh của phần cứng và Java Native Interface là một cách tuyệt vời để thực hiện nó.
Vậy Java Native Interface làm gì?
Có thể các bạn đã biết thực ra Java Native Interface cho phép chúng ta gọi hàm từ thư viện C/C++ khi đó ta có thể giao tiếp nhiều hơn với phần cứng.
Ok, vậy ta có thể hình dung ta cần j rồi, hiểu về C/C++ (Hiểu sâu nhé :D). Một trình biên dịch trên Window (Những ai dùng Linux thì chuyên gia rồi chắc mình không phải hướng dẫn nữa, và mình cũng khuyên các bạn nên chuyển qua Linux (Ubuntu) mà dùng).
Ở đây mình dùng http://www.cygwin.com/. Bạn phải download Android NDK về nhé http://developer.android.com/sdk/ndk/index.html
Eclipse chắc bạn có rồi :D.
Bắt đầu nhé:
Cài đặt cygwin nhé.
Giải nén Android NDK nhé.
Bây giờ ta sẽ chạy một ví dụ của google viết sẵn để hiểu cách làm việc của nó.
Trong thư mục Android NDK bạn vừa giải nén ra có thư mục sample bạn nhìn thấy một số ví dụ ở đây.
Trên eclipse, bạn tạo một project android mới từ source này, ở đây chúng ta chọn hello-jni nhé.
Giờ chúng ta đã có project trên eclipse rồi.
Trên command của windown (cmd), bạn cd tới thư mục của project trên ...
Chạy command:
android update project -p . -s
Nhớ rằng trước đó đã path cho android trong thư mục ...android-sdk-windows\tools và ...android-sdk-windows\platform-tools
Và path của ...cygwin\bin nữa nhé.
Giờ chúng ta bắt đầu build native code sử dụng command ndk-build.
Hãy chắc chắn rằng bạn đang ở ...
Trên command của window bạn chạy ash (Hãy chắc chắn rằng bạn đã set path cho ...cygwin\bin).
Lúc này bạn đang chạy SHELL trên môi trường GNU/Linux chứ không phải command như ở window nữa.
Chạy command su
Của mình là su tannm
Khi đó bạn sẽ nhìn thấy đường dẫn của bạn giống như:
TanNM@TanNM-PC /cygdrive/e/STUDY/android/android-ndk-r5b/samples/hello-jni
Ngon rồi đấy!
Tiếp theo bạn hãy chạy lệnh:
/cygdrive/e/STUDY/android/android-ndk-r5b/ndk-build
Khi đó output sẽ giống như:
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
Compile thumb : hello-jni <= hello-jni.c SharedLibrary : libhello-jni.so Install : libhello-jni.so => libs/armeabi/libhello-jni.so
Quay lại eclipse refresh lại project và chạy.
OK chứ?
Thursday, March 10, 2011
Ra mắt kho game, ứng dụng cho android.
http://ungdungandroid.net
Tuesday, March 1, 2011
Fix GPS cho Samsung galaxy
Cấu hình mặc định của Nó ban đầu là:
1, Settings -> Parameter Settings -> Address -> Server Type: UMTS SLP
2, Settings -> Parameter Settings -> Position mode: option2
3, Settings -> Fix Request Settings -> Session Operation: MS-based
4, Settings -> Fix Request Settings -> Server Option: Default
5, gpsOne XTRA -> Xtra Enable: Disable
1, Bấm *#*#1472365#*#*. Đây là mã để vào menu cấu hình GPS. Trong menu đó bạn lần lượt chọn thay đổi một số giá trị bên dưới.
2, Settings -> Parameter Settings -> Address -> Server Type: 1x MPC
3, Settings -> Parameter Settings -> Position mode: option3
4, Settings -> Fix Request Settings -> Session Operation: Standalone
5, Settings -> Fix Request Settings -> Server Option: Local
6, gpsOne XTRA -> Xtra Enable: Enable
Hehehe... Cuối cùng cũng có GPS để sài. :D
Thursday, February 24, 2011
Tạo image header (hay title bằng image) của ứng dụng trên android
Vì vấn đề thương hiệu, các sếp đã bắt Tên của ứng dụng nên để logo của ứng dụng chứ ko chỉ là để chữ không. Mặc định thì nó chỉ hiển thị tên ứng dụng bạn khai báo trong file Manifest. Nhưng bạn hoàn toàn có thể control nó theo ý của mình.
Trước hết tôi design layout main_header cho Title đó như sau.
Ở đây tôi chỉ đưa một image vào đó, nhưng bạn hoàn toàn có thể cho những thứ mà bạn thích vào đây.
Có layout rồi, nhưng làm thế nào để load được nó đây. Bạn để ý thấy trong thẻ Application của file Manifest có thuộc tính sau android:label="@string/app_name":
Cái này chính là cái hiển thị title của ứng dụng.
Ta sẽ ghi đè lên title như sau.
Tạo một style TitleCustomTheme như sau:
Và thêm thuộc tính android:theme="@style/TitleCustomTheme" vào trong thẻ application như sau.
Trong class Activity ta load layout đã design như sau:
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.main_header);
Xem thử kết quả nhé. Nhắc bạn luôn nhớ rằng, thương hiệu luôn là vấn đề sống còn của doanh nghiệp :D.
Xem ảnh demo:
Tuesday, February 22, 2011
Xây dựng grid layout cho android với mỗi phần tử bao gồm Icon + title.
Giới thiệu sơ qua về dự án của tôi, nó được xây dựng cho mảng bán content cho người dùng giống như Sóc Bay hay một số các phần mềm khác.
Hôm nay tôi sẽ chỉ cách cho các bạn thiết kế layout cho màn hình chính của chương trình. Đó là Grid Layout mỗi phần tử bao gồm các Icon + title bên dưới.
Trước hết chúng ta phải xây dựng được layout cho phần tử bên trong, cái này thì đơn giản quá nhỉ, chỉ cần một ImageView và một TextView là được. Nó trông giống như:
Trong main Activity bạn load View như sau
GridView gridview = (GridView) findViewById(R.id.gridview); gridview.setAdapter(new ImageAdapter(this, dictionary, dictionary.getChildList().size()));
Trong đó class ImageAdapter load các phần tử ta xây dựng bên trên và xây dựng lớp đó như sau
package vn.vdco.mzone; import vn.vdco.mzone.util.DictionaryNode; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; /** * * @author TanNM * */ public class ImageAdapter extends BaseAdapter { private Context mContext; private DictionaryNode dictionary; private int sizeArray; private Integer[] mThumbIds = null; private String[] mTitle = null; /** * * @param c * @param dictionary * @param sizeArray */ public ImageAdapter(Context c, DictionaryNode dictionary, int sizeArray) { this.mContext = c; this.dictionary = dictionary; this.sizeArray = sizeArray; mTitle = new String[sizeArray]; mThumbIds = new Integer[sizeArray]; } public int getCount() { return mThumbIds.length; } public Object getItem(int position) { return null; } public long getItemId(int position) { return 0; } /** * Load layout for Item with Icon and title * @param position * @param convertView * @param parent * @return View * @author tannm */ public View getView(int position, View convertView, ViewGroup parent) { ImageView iv; TextView tv; View v; init(); if (convertView == null) { LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = li.inflate(R.layout.main_icon, null); tv = (TextView)v.findViewById(R.id.icon_text); tv.setText(mTitle[position]); iv = (ImageView)v.findViewById(R.id.icon_image); iv.setImageResource(mThumbIds[position]); } else { v = convertView; } return v; } /** * Load title and icon in config file to array * @author tannm */ private void init() { if(dictionary != null && dictionary.getChildList() != null && dictionary.getChildList().size() >0) { for (int i = 0; i < sizeArray; i++) { DictionaryNode node = (DictionaryNode)dictionary.getChildList().get(i); int resID = this.mContext.getResources().getIdentifier("vn.vdco.mzone:drawable/" + node.getString("$Image$"), null, null); mThumbIds[i] = resID; mTitle[i] = node.mstrValue; } } } }
Ở trên tôi có dùng một đối tượng DictionaryNode để lưu trữ cấu hình các image và title cho các phần tử. Bạn có thể xem cách load nó trong phương thức init().
Cấu trúc của nó kiểu như sau:
MainMenu
NEWS==Đọc báo
$Image$==n
FUNNY==Hài hước
$Image$==hh
....
Khi đó ta sẽ load động các hình ảnh và title từ file cấu hình vào. Chú ý, nếu cái này ko cần thiết bạn nên Fix vì trong điện thoại cần tiết kiệm dung lượng và các sử lý không cần thiết tới mức tối đa do bộ nhớ và CPU nhỏ.
Chúc bạn có một layout tuyệt vời cho ứng dụng của bạn.
Sunday, December 12, 2010
Chỉ dẫn lập trình game 3D trên Android (P5)
Bài hôm nay tôi sẽ giới thiệu cho các bạn cách vẽ một hình đa giác cách xoay chúng như thế nào. Bài trước các bạn đã được học về GLSurfaceView và hãy chắc chắn rằng bạn đã đọc nó vì nó rất quan trọng cho bài hôm nay.
Loạt bài trước tôi cũng đã giới thiệu với các bạn một số khái niệm về đỉnh, tam giác, đa giác. Hôm nay trước khi vào vẽ đa giác tôi sẽ giới thiệu cho các bạn thêm một số khái niệm khác.
1, Cạnh:
Cạnh là một đường thẳng nối 2 đỉnh với nhau. Chúng là đường viền của mặt và đa giác. Trong không gian 3D một cạnh có thể là một đường giao nhau của 2 mặt, do đó khi cạnh này chuyển đổi sẽ dẫn đến sự thay đổi theo của các đỉnh, các mặt kết nối với chúng.
Trong OpenGL ES bạn không định nghĩa các cạnh, thay vào đó bạn định nghĩa các mặt bởi các đỉnh khi đó ta sẽ có các cạnh.
Nếu bạn muốn sửa, thay đổi mộ cạnh bạn có thể thay đổi 2 đỉnh tạo nên cạnh đó.
2, Mặt:
Một cách tổng quát, mặt trong toán học là đa tạp n-1 chiều trong không gian n chiều. Trong tô pô, một mặt hai chiều được gọi là mặt đơn giản nếu nó đồng phôi với một hình vuông trên mặt phẳng hai chiều.
Ví dụ: mặt bán cầu là một mặt đơn giản, còn toàn bộ mặt cầu lại không phải là một mặt đơn giản.
Trong không gian ba chiều, mặt được mặc định là đa tạp hai chiều, là tập hợp những điểm trong hệ tọa độ Đề các ba chiều Oxyz thỏa phương trình: z = f(x,y).
Trong OpenGL ES ta hiểu rằng có thể chia mặt tới mức nhỏ nhất khi đó một mặt chỉ là một tam giác, chúng được tạo lên bởi 3 đỉnh, một chuyển đổi của mặt sẽ ảnh hưởng tới các đỉnh, cạnh liên kết với nó.
Khi uốn khúc trên bề mặt của bạn điều này rất quan trọng để thực hiện các điều khiển đúng hướng của nó. Khi đó bạn phải điều khiển những gì sẽ sảy ra với mặt phía trước và cả mặt phía sau nữa. Tại sao điều này lại rất quan trọng bởi vì hiệu năng của ứng dụng sẽ tăng lên khi bạn không phải vẽ cả 2 mặt mà chỉ thực hiện vẽ mặt phía trước. Đó là ý tưởng tốt sử dụng sự uốn khúc cho tất cả các dự án của bạn. Bạn có thể định nghĩa phía trên của bề mặt bằng cách.
gl.glFrontFace(GL10.GL_CCW); // OpenGL docs
gl.glEnable(GL10.GL_CULL_FACE); // OpenGL docs
gl.glCullFace(GL10.GL_BACK); // OpenGL docs
3, Render:
Thời gian để bạn đưa một cái j đó ra ngoài màn hình, có 2 phương thức để bạn làm điều này, 2 phương thức đó là:
public abstract void glDrawArrays(int mode, int first, int count); // OpenGL docs
và
public abstract void glDrawElements(int mode, int count, int type, Buffer indices); // OpenGL docs
Một số render cơ bản nhất:
GL_POINTS: Render một điểm ra màn hình.
GL_LINE_STRIP: Render một dãy các phân đoạn đường thẳng nối liền nhau, nhưng không nối điểm đầu và điểm cuối.
GL_LINE_LOOP: Giống như GL_LINE_STRIP nhưng không nối điểm đầu và điểm cuối.
GL_LINES: Render một đường thẳng.
GL_TRIANGLES: Render một tam giác.
GL_TRIANGLE_STRIP: Render một loạt các tam giác theo thứ tự VD: Ta có các đỉnh v0, v1, v2, v3, v4, v5 khi đó các tam giác được render là (v0, v1, v2) sau đó là (v2, v1, v3) (lưu ý thứ tự), tiếp theo là (v2, v3, v4) ...
GL_TRIANGLE_FAN: Render một loạt các tam giác giống như GL_TRIANGLE_STRIP nhưng các tam giác được vẽ giống như một hình quạt dựa vào một gốc.
Giờ chúng ta sẽ vẽ thử một tam giác:
Bạn vẫn còn project hôm trước chứ, ta sẽ thay đổi một chút để có thể render ra được một tam giác.
Ta chỉ cần sửa lại lớp VortexRenderer lại như sau:
package vn.vndev.android.opengl; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; /** * * @author TanNM * */ public class VortexRenderer implements GLSurfaceView.Renderer { private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float red = 0f; private float green = 0f; private float blue = 0f; // a raw buffer to hold indices allowing a reuse of points. private ShortBuffer indexBuffer; // a raw buffer to hold the vertices private FloatBuffer vertexBuffer; private short[] indicesArray = { 0, 1, 2 }; private int nrOfVertices = 3; private float angle; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); initTriangle(); } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } public void setAngle(float angle) { this.angle = angle; } @Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(red, green, blue, 1.0f); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set rotation gl.glRotatef(angle, 0f, 1f, 0f); gl.glColor4f(0.2f, 0.5f, 0.2f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, nrOfVertices, GL10.GL_UNSIGNED_SHORT, indexBuffer); } private void initTriangle() { // float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); // short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder()); indexBuffer = ibb.asShortBuffer(); float[] coords = { -0.5f, -0.5f, 0f, // (x1, y1, z1) 0.5f, -0.5f, 0f, // (x2, y2, z2) 0f, 0.5f, 0f // (x3, y3, z3) }; vertexBuffer.put(coords); indexBuffer.put(indicesArray); vertexBuffer.position(0); indexBuffer.position(0); } public void setColor(float r, float g, float b) { red = r; green = g; blue = b; } }
Để hình tam giác của ta quay với tốc độ tùy vào vị chí mà ta click ta set thêm góc giống như
renderer.setAngle((event.getX() + event.getY()) / 20);trong phương thức
public boolean onTouchEvent(final MotionEvent event)của lớp VortexView
Bạn có thể download mã nguồn của ứng dụng về chạy thử.
Friday, December 3, 2010
Chỉ dẫn lập trình game 3D trên Android (P4)
Hôm trước ứng bài demo chạy tốt chứ? Hôm nay tôi sẽ giải thích sơ qua một chút về ứng dụng đó.
1, Trước hết là lớp GLSurfaceView:
Bắt đầu được hỗ trợ trên Android từ phiên bản 1.5.
Lớp này hỗ trợ bạn kết nối vào OpenGL từ view bằng cách quản lý một bề mặt và ghép chúng vào hệ thống view của Android. Nó quản lý một EGL display, cho phép OpenGL thực hiện render vào một bề mặt.
Tạo khả năng giúp OpenGL ES làm việc với vòng đời của Activity.
Tạo khả năng lựa chọn định dạng frame buffer pixel một cách thích hợp.
Quản lý các thread rendering riêng biệt cho phép tạo các chuyển động mượt mà :D.
Cung cấp tools debugging một cách đơn giản cho tracing các lời gọi API OpenGL ES và kiểm tra lỗi.
Bạn muốn tiến xa hơn trong OpenGL thì đây là nơi mà bạn cần phải bắt đầu.
Để sử dụng GLSurfaceView bạn chỉ cần gọi phương thức:
public void setRenderer(GLSurfaceView.Renderer renderer);có trong GLSurfaceView. Để tùy biến các thuộc tính của GLSurfaceView thì trước khi gọi phương thức setRenderer bạn có thể gọi một số các phương thức khác như:
setDebugFlags(int); setEGLConfigChooser(boolean); setEGLConfigChooser(EGLConfigChooser); setEGLConfigChooser(int, int, int, int, int, int); setGLWrapper(GLWrapper);Mặc định GLSurfaceView sẽ tạo một bề mặt với định dạng mặc định là PixelFormat.RGB_565. Nếu bạn cần một bề mặt trong suốt bạn có thể gọi phương thức getHolder().setFormat(PixelFormat.TRANSLUCENT).
Một GLSurfaceView phải được thông báo khi Activity là paused và resumed. GLSurfaceView clients cần phải gọi phương thức onPause() khi activity pauses và gọi onResume() khi activity resumes. Lời gọi cho phép GLSurfaceView thực hiện pause hay resume các thread rendering, và nó cũng cho phép GLSurfaceView release hay khởi tạo lại các đối tượng OpenGL display.
2, GLSurfaceView.Renderer:
Là một render interface.
Render sẽ được gọi trên một thread riêng biệt vì thế hiệu năng rendering sẽ tách rời UI thread. Nhưng các render cần giao tiếp với các UI thread bởi vì UI thread là nơi nhận được các sự kiện input. Các render có thể giao tiếp bằng bất kỳ kỹ thuật java chuẩn nào cho giao tiếp cross-thread, hoặc đơn giản hơn là sử dụng phương thức queueEvent(Runnable).
Khi bạn implementation render này bạn cần khai báo các phương thức sau:
// Gọi khi một bề mặt được khởi tạo hay khởi tạo lại public void onSurfaceCreated(GL10 gl, EGLConfig config) // Gọi để vẽ frame hiện tại. // Phương thức này sẽ chịu trách nhiệm vẽ frame hiện tại. public void onDrawFrame(GL10 gl) // Gọi khi bề mặt thay đổi kích thước public void onSurfaceChanged(GL10 gl, int width, int height)
onSurfaceCreated: Đây là nơi tốt để bạn cài đặt một điều gì đó mà bạn không thường xuyên thay đổi trong vòng đời của rendering.
onDrawFrame: Đây là nơi sự kiện vẽ thực sự diễn ra.
onSurfaceChanged: Nếu thiết bị của bạn cho phép chuyển giữa màn hình nằm ngang hay nằm dọc, bạn sẽ có lời gọi tới phương thức này khi sự chuyển đổi đó diễn ra. Bạn có thể thiết lập lại các tỷ lệ mới ở đây
Bạn có thể download mã nguồn của bài trước và bài này tại đây
Thursday, December 2, 2010
Chỉ dẫn lập trình game 3D trên Android (P3)
Bất kỳ một nhà phát triển game nào cũng cần phải biết OpenGL là kim chỉ nam cho nhưng nhà phát triển game chuyên nghiệp. Bạn khó có thể tìm thấy một game mạnh mẽ nào mà không sử dụng API của OpenGl, bởi vì nó có khả năng tận dụng và tăng tốc phần cứng hơn hầu hết tất cả các phần mềm renderer khác.
OpenGL có thể là một môn học đáng sợ cho người mới bắt đầu (that's true for me :D). Nhưng bạn cũng không thể thành thạo OpenGL tới mức biết được làm thế nào để nó có thể vẽ các phần tử đó với OpenGL API. Bạn chỉ cần sự ham học hỏi, mong muốn tìm hiểu một công cụ mạnh mẽ để xây dựng các ứng dụng đồ họa như game.
Trong giới hạn của bài này tôi không thể chỉ bạn tất cả về OpenGL mà tôi chỉ đưa ra ví dụ sử dụng chúng trong ứng dụng Android, dựa vào đó và các API của OpenGL bạn có thể tùy biến theo ý của bạn.
Tham khảo thêm về OpenGL tại:
http://www.opengl.org/sdk/docs/man/
http://www.falloutsoftware.com/tutorials/gl/gl0.htm
http://mathworld.wolfram.com/OrthographicProjection.html
http://nehe.gamedev.net/
Về thiết bị thì khỏi bàn, hiện nay Android ngày càng trở nên mạnh mẽ cho việc phát triển các ứng dụng đồ họa.
Để tận dụng tối đa hiệu năng của CPU, Google đã đưa OpenGL Embedded System (OpenGL-ES) vào trong hệ điều hành Android.
OpenGL-ES cung cấp một loạt các API làm cho game có hiệu năng sử lý cao, tăng tốc phần cứng. Đây là các Java API, để xây dựng lại chúng từ C là một công việc rất phức tạp. Tôi sẽ chỉ các bạn cách thức làm việc trên cả Java và C.
Bắt đầu bằng một số khái niệm cơ bản về OpenGL
1, Đỉnh: là một điểm trong không gian 3D trong OpenGl có thể chỉ là 2 tọa độ (x, y) hoặc nhiều tọa độ như (x, y, z, w) ở đó w là tùy chọn và mặc định nó có giá trị là 1.0, tương tự trục z cũng là tùy chọn và giá trị mặc định của nó là 0. Tất các các đối tượng được vẽ bởi các đỉnh hay chính là các điểm, do đó kho nói tới điểm cũng chính là 1 đỉnh.
2, Tam giác: được tạo nên bởi 3 điểm, trong OpenGL chúng ta có thể dùng 3 đỉnh để tạo nên 1 tam giác.
3, Đa giác: được tạo nên bởi nhiều hơn 2 điểm, tam giác cũng là 1 đa giác.
Nào trước hết chúng ta bắt đầu chúng với Java:
Tạo một Android project mới, với activity là Vortex và một số các lớp sau:
package vn.vndev.android.opengl; import android.app.Activity; import android.os.Bundle; /** * * @author TanNM * */ public class Vortex extends Activity { private static final String LOG_TAG = Vortex.class.getSimpleName(); private VortexView vortexView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Hien thi toan man hinh this.requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); vortexView = new VortexView(this); setContentView(vortexView); } }
package vn.vndev.android.opengl; import android.content.Context; import android.opengl.GLSurfaceView; import android.view.MotionEvent; /** * * @author TanNM * */ public class VortexView extends GLSurfaceView { private static final String LOG_TAG = VortexView.class.getSimpleName(); private VortexRenderer renderer; /** * * @param context */ public VortexView(Context context) { super(context); renderer = new VortexRenderer(); setRenderer(renderer); } public boolean onTouchEvent(final MotionEvent event) { queueEvent(new Runnable() { public void run() { renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f); } }); return true; } }
package vn.vndev.android.opengl; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; /** * * @author TanNM * */ public class VortexRenderer implements GLSurfaceView.Renderer { private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float red = 0.7f; private float green = 0.5f; private float blue = 0.8f; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); float ratio = (float) w / h; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onDrawFrame(GL10 gl) { gl.glClearColor(red, green, blue, 1.0f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT); } public void setColor(float r, float g, float b) { red = r; green = g; blue = b; } }Ok giờ bạn hãy chạy thử ứng dụng và click lên các vị trí khác nhau của màn hình. Thấy j nhỉ?
Thursday, November 18, 2010
Chỉ dẫn lập trình game 3D trên Android (P2)
JNI là viết tắt của Java Native Interface. Sử dụng JNI chúng ta có thể gọi các hàm được viết trong những ngôn ngữ khác từ Java.
Trước hết bạn tạo một Android project tạo một activity MainActivity:
package com.vndev.jni.activities; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import jni.Natives; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { private static final String LIB = "libch02.so"; private static final String LIB_PATH = "/data/data/com.vndev.jni.activities/files/" + LIB; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); try { // Install lib System.out.println("Installing LIB: " + LIB); // Copy lib from assests folder to files folder: // /data/data/PKG_NAME/files writeToStream(getAssets().open(LIB), openFileOutput(LIB, 0)); // Load Lib System.load(LIB_PATH); // Run it String[] argv = { "MyLib", "arg1", "arg2" }; Natives.LibMain(argv); } catch (Exception e) { e.printStackTrace(); } } /** * Write to a stream * * @param in * @param out * @throws IOException */ public static void writeToStream(InputStream in, OutputStream out) throws IOException { byte[] bytes = new byte[2048]; for (int c = in.read(bytes); c != -1; c = in.read(bytes)) { out.write(bytes, 0, c); } in.close(); out.close(); } }
Trong activity này ta sẽ load thư viện từ C vào bằng cách gọi:
System.load(LIB_PATH);
Activity này gọi một phương thức trong lớp Natives:
Natives.LibMain(argv);
Phương thức này được khai báo:
public static native int LibMain(String[] argv);
Phương thức này có từ khóa native, nó sẽ gọi phương thức LibMain trong thư viên được load ở trên.
Toàn bộ nội dung class Natives sẽ như sau:
package jni; public class Natives { /** * Native Main Doom Loop * @param argv * @return */ public static native int LibMain(String[] argv); /** * This fires on messages from the C layer * @param text */ @SuppressWarnings("unused") private static void OnMessage(String text, int level) { System.out.println("OnMessage text:" + text + " level=" + level); } }
Để tạo file header cho lớp này ta sử dụng javah trong java như sau
Javah -jni Natives
Khi đó ta có file header như sau
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class jni_Natives */ #ifndef _Included_jni_Natives #define _Included_jni_Natives #ifdef __cplusplus extern "C" { #endif /* * Class: jni_Natives * Method: LibMain * Signature: ([Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_jni_Natives_LibMain (JNIEnv *, jclass, jobjectArray); //implements method call by java native #ifdef __cplusplus } #endif #endif
Giờ bạn cần tạo một file C khác dựa vào khai báo trên cài đặt phương thức này trong C và biên dịch chúng, chạy để thấy kết quả.
Download source
Thursday, November 11, 2010
Chỉ dẫn lập trình game 3D trên Android (P1)
Về ngôn ngữ lập trình, không chỉ Java mà bạn cần biết cả ngôn ngữ lập trình C.
Bởi vì Java cho bạn cách thức lập trình hướng đối tượng và nó chạy trên Android, nhưng chỉ có C mới có sức mạnh mà một lập trình viên games cần tới.
Một số kỹ năng cứng bạn cần biết trong Android là:
Activities, view, layout....
Services.
Content providers.
Broadcast receivers.
Một số kỹ năng khác là: Hiểu biết cơ bản về Linux và shell scripting:
Bạn cần biết một số câu lệnh cơ bản như: liệt kê các file, cài đặt một thành phần, một ứng dụng...
Yêu cầu về phần mềm:
Hệ điều hành Ubuntu.
Eclipse.
Android SDK.
Java JDK.
(Còn tiếp)
Wednesday, August 25, 2010
10 Cách Kéo Dài Thời Lượng Pin Cho Android
1. Kiểm tra thành phần nào đang ngốn pin:
Chức năng ‘Battery Use’ (trong menu Settings > About Phone > Battery use) liệt kê các thành phần đang ngốn pin của máy và hiển thị mức độ % sử dụng pin. Số % càng cao nghĩa là thành phần đó ngốn pin càng nhiều. Bạn có thể nhấn vào từng thành phần để điều chỉnh cách thức mà chúng hoạt động hoặc tắt bớt các ứng dụng để tránh lãng phí pin.
Màn hình kích thước càng lớn, độ sáng càng cao thì càng ngốn nhiều pin. Để giảm độ sáng màn hình, bạn vào menu Settings > Sound and Display > Brightness, nhấn vào ô Automatic brightness adjustment để máy tự điều chỉnh độ sáng hoặc dùng thanh trượt bên dưới để tăng/giảm độ sáng màn hình.
Ngoài ra, rút ngắn thời gian đèn màn hình tự động tắt trong phần Screen timeout cũng giúp kéo dài thời gian sử dụng pin.
Kết nối Wi-Fi giúp cho các ứng dụng trong máy, nhất là các ứng dụng có kết nối mạng như Email, Facebook, Twitter, RSS… làm việc trơn tru và hiệu quả nhưng cũng vì thế mà làm cho pin hết nhanh. Nếu bạn đến những nơi không có sóng Wi-Fi hoặc tín hiệu Wi-Fi yếu thì máy sẽ dò tìm Wi-Fi liên tục, làm máy nhanh hết pin hơn. Vì vậy, nếu không cần thiết thì bạn hãy tắt Wi-Fi đi bằng cách kéo thanh trạng thái từ phía trên màn hình xuống và nhấn vào biểu tượng Wi-Fi để tắt nó đi. Làm tương tự với biểu tượng kết nối Bluetooth.
4. Tắt chức năng định vị GPS:
Không phải lúc nào bạn cũng cần đến chức năng xác định vị trí qua GPS, nếu mở GPS liên tục thì pin sẽ rất mau hết. Để tắt GPS, bạn vào menu Settings > Location and security và bỏ dấu chọn ở dòng Use GPS Satellites. Lúc này điện thoại vẫn có thể xác định vị trí của bạn thông qua Wi-Fi và các trạm thu phát sóng nhưng độ chính xác sẽ kém đi so với GPS.
Sử dụng widget Power Control:
‘Power Control’ là một widget có sẵn trong các máy Android dùng để tắt/mở nhanh các kết nối mạng, bao gồm: Wi-Fi, Bluetooth, GPS, Sync và chỉnh độ sáng màn hình. Bạn nhấn và giữ ngón tay lên vùng trống của màn hình ngoài, chọn Android Widgets > Power control để đưa widget này ra ngoài màn hình chờ. Sau này bạn có thể tắt/mở các kết nối nhanh chóng chỉ với 1 cú nhấn.
5. Tắt bớt các ứng dụng tự động đồng bộ hóa dữ liệu (auto sync):
Các ứng dụng có kết nối mạng như Email, Facebook, Twitter… đều cho người dùng điều chỉnh khoảng thời gian chương trình tự động kết nối và tải dữ liệu về. Bạn hãy vào menu Settings (hoặc Configurations của từng chương trình) > Refresh interval và tăng mức thời gian trong đó lên để giảm bớt số lần chương trình tự động tải dữ liệu về.
6. Tắt bớt các phần mềm chạy ngầm:
Các phần mềm đang chạy ngầm không những làm lãng phí pin của máy mà còn khiến máy chạy ì ạch. Bạn có thể dùng một trong các phần mềm như ‘Smart Bar’, ‘TasKiller’ hoặc ‘Advanced Task Manager’ (tải về từ Market) để xem các ứng dụng nào đang chạy ngầm bên trong điện thoại và tắt chúng đi nếu không cần thiết.
7. Gỡ bỏ bớt các ứng dụng không cần thiết:
Vào menu Settings > Applications > Manage Applications để xem danh sách các phần mềm đã cài vào máy, nếu thấy phần mềm nào không cần thiết thì hãy gõ bỏ chúng đi để tránh gây lãng phí tài nguyên của máy và tự động chạy gây ngốn pin. Gỡ bỏ bằng cách chọn vào phần mềm đó, bấm Uninstall.
8. Tắt bớt các widget không sử dụng:
Màn hình nhiều widget mang lại nhiều tiện lợi cho bản thân nhưng nếu sử dụng quá nhiều widget thì chúng sẽ kết nối mạng nhiều lần hơn, máy phải xử lý nặng nề hơn và do đó cũng hao pin hơn. Để gỡ các widget xuống, bạn nhấn và giữ ngón tay lên widget đó cho đến khi phía dưới màn hình hiện ra biểu tượng giỏ rác, bạn kéo widget đó bỏ vào giỏ rác là xong.
9. Tắt chức năng Live Wallpaper:
Live Wallpaper đem lại sức sống cho màn hình điện thoại với những hiệu ứng hình ảnh sống động như thật, nhưng bù lại cũng ngốn pin hơn. Nếu bạn muốn ưu tiên thời lượng dùng pin hơn sự đẹp mắt thì cũng nên tắt chúng đi bằng cách nhấn và giữ ngón tay lên vùng trống của màn hình chờ, chọn Wallpapers và chọn 1 tấm ảnh tĩnh trong phần Gallery hoặc Wallpaper gallery.
10. Giữ điện thoại ở nơi mát mẻ:
Tránh để điện thoại ở những nơi có nhiệt độ cao hoặc tiếp xúc trực tiếp với ánh nắng Mặt Trời trong thời gian dài cũng giúp tránh được tình trạng pin nhanh bị cạn kiệt.
Wednesday, June 9, 2010
Làm việc với Database trên Android (P4)
package com.tannm.doan.taskmanager.database; import android.database.Cursor; public class DataTable { // fields private DataRow dataRow = null; // methods public DataTable(DataRow dataRow) { this.dataRow = dataRow; } public Database getUserDb() { return dataRow.getUserDb(); } public String getTableName() { return dataRow.getTableName(); } public DataRow getDataRow() { return dataRow; } public boolean createTable() { if (getUserDb().tableExists(getTableName())) { return true; } else { return getUserDb() .execSQL( getSqlTableDefinition(getTableName(), dataRow .getTableDef())); } } public String getSqlTableDefinition(String sTableName, DataField[] vecTableDef) { String def = "CREATE TABLE " + sTableName + " ("; for (int i = 0; i < vecTableDef.length; i++) { def += vecTableDef[i].getColumnDefinition(); if (i < (vecTableDef.length - 1)) def += ", "; } def += ")"; return def; } public long insertValues() { long lRowId = getUserDb().getSQLiteDb().insert(getTableName(), null, dataRow.getContentValues()); return lRowId; } public long updateValues(long lRowId) { String sWhere = String.format("_ID = %d", lRowId); long lRowsUpdated = getUserDb().getSQLiteDb().update(getTableName(), dataRow.getContentValues(), sWhere, null); return lRowsUpdated; } public long deleteDataRow(long lRowId) { String sWhere = String.format("_ID = %d", lRowId); long lRowsUpdated = getUserDb().getSQLiteDb().delete(getTableName(), sWhere, null); return lRowsUpdated; } public Cursor locateDataRow(long lRowId) { final String s = "select * from %s where _ID = %d"; String sql = String.format(s, getTableName(), lRowId); Cursor cr = getUserDb().getSQLiteDb().rawQuery(sql, null); // if cursor valid, set first data row as current if ((cr != null) && (cr.getCount() > 0)) cr.moveToFirst(); return cr; } public Cursor locateAlarmDataRow(int iType, long lRefID) { final String s = "select * from %s where Type = %d and RefID = %d"; String sql = String.format(s, getTableName(), iType, lRefID); Cursor cr = getUserDb().getSQLiteDb().rawQuery(sql, null); // if cursor valid, set first data row as current if ((cr != null) && (cr.getCount() > 0)) cr.moveToFirst(); return cr; } public Database.Result updateData(boolean bInsertMode, long lEditRowId) { Database.Result result = Database.Result.errUnknown; if (getUserDb().isOpened()) { try { dataRow.setValuesForDataRow(); } catch (Exception e) { return Database.Result.errCantSetValuesForDataRow; } // select update mode if (bInsertMode) { // insert new data row long lRowId = insertValues(); if (lRowId > 0) { result = Database.Result.Success; } else { result = Database.Result.errCantInsertNewData; } } else { // update existing data row long lRowsUpdated = updateValues(lEditRowId); if (lRowsUpdated == 1) { result = Database.Result.Success; } else { result = Database.Result.errCantUpdateData; } } } else { result = Database.Result.errNoDbAccess; } return result; } public Database.Result deleteData(long iRowId) { Database.Result result = Database.Result.errUnknown; if (getUserDb().isOpened()) { if (getUserDb().tableExists(getTableName())) { long lRowsDeleted = deleteDataRow(iRowId); if (lRowsDeleted == 1) { result = Database.Result.Success; } else { result = Database.Result.errCantDeleteData; } } else { result = Database.Result.errTableNotExists; } } else { result = Database.Result.errNoDbAccess; } return result; } public Database.Result getRowDataForEdit(long lRowId) { Database.Result result = Database.Result.errUnknown; // get requested data row Cursor cr = locateDataRow(lRowId); if (cr == null) { result = Database.Result.errCantGetData; } else { if (cr.getCount() > 0) { if (dataRow.getValuesFromCursor(cr)) { try { dataRow.getValuesFromDataRow(); } catch (Exception e) { return Database.Result.errCantGetValuesFromDataRow; } result = Database.Result.Success; } else { result = Database.Result.errCantGetDataFromTable; } cr.close(); } else { result = Database.Result.errCantFindData; } } return result; } }
Trên đây là toàn bộ kinh nghiệm làm việc với Database mà tôi học được trong quá trình làm việc thực tế. Mong giúp đỡ những người mới làm việc với SQLite trên Android. Chúc các bạn thành công
Làm việc với Database trên Android (P3)
Mỗi một bảng trong SQLite nên tạo một thể hiện kế thừa từ lớp DataRow này.
Một DataRow chứa một số các DataField nên ta khai báo một mảng DataField.
Ngoài ra ta cần phải khai báo một biến trung ContentValues và một thể hiện Database ta đã tạo ở phần 1
package com.tannm.doan.taskmanager.database; import android.content.ContentValues; import android.database.Cursor; public abstract class DataRow { // fields protected Database userdb = null; private DataField[] vecTableDef = null; private ContentValues values = new ContentValues(); // methods public DataRow(Database userdb) { this.userdb = userdb; } public Database getUserDb() { return userdb; } public void setTableDefinition(DataField[] vecTableDef) { this.vecTableDef = vecTableDef; // initialize field parent updateDataFieldsParentRow(this); } public void updateDataFieldsParentRow(DataRow row) { for (int i = 0; i < vecTableDef.length; i++) vecTableDef[i].setParentRow(row); } public void copyTableDefinition(DataRow data) { setTableDefinition(data.vecTableDef); } public DataField[] getTableDef() { return vecTableDef; } public boolean validate() { return false; } public void clearContentValues() { values.clear(); } public ContentValues getContentValues() { return values; } public void setContentValues(ContentValues values) { this.values = values; updateDataFieldsParentRow(this); } public boolean copyContentValues(ContentValues values) { this.values = values; updateDataFieldsParentRow(this); try { getValuesFromDataRow(); return true; } catch (Exception e) { } return false; } public DataField value(int idx) { return vecTableDef[idx]; } public String fieldName(int idx) { return vecTableDef[idx].getName(); } public boolean getValuesFromCursor(Cursor cr) { if ((cr != null) && (cr.getPosition() != -1)) { for (int idx = 0; idx < vecTableDef.length; idx++) { DataField field = value(idx); // check if null value if (cr.isNull(idx)) { field.setNull(); } else { final DataField.Type t = field.getType(); // parse value by type if (t == DataField.Type.INT) field.set(cr.getLong(idx)); if (t == DataField.Type.TEXT) field.set(cr.getString(idx)); if (t == DataField.Type.BOOL) field.set((cr.getInt(idx) == 1) ? true : false); } } return true; } return false; } // sets fields values data (ContentValues values contener) from parent // object fields public abstract void setValuesForDataRow(); // gets data from fields values (ContentValues values contener) to parent // object fields public abstract void getValuesFromDataRow(); public abstract String getTableName(); }
Tuesday, May 25, 2010
Làm việc với Database trên Android (P2)
- Tạo database
- Chạy câu SQL
- Tạo table
- ....
Lần này mình xin giới thiệu để tạo một thể hiện của một trường trong Database SQLite
Trong SQLite chỉ có 3 kiểu dữ liệu là: INT, TEXT, BOOL
Vì thế ta cần tạo một biến enum chứa một bộ các hằng số về kiểu dữ liệu này. Sau đó implement một số phương thức như trong VD sau
package com.tannm.doan.taskmanager.database;
import java.util.Calendar;
import android.content.*;
public class DataField {
// types
public static enum Type {
INT, TEXT, BOOL
};
// fields
private Calendar dateOut = Calendar.getInstance();
// fields
private DataRow dataRow = null;
private ContentValues values = null;
// fields
private int index = 0;
private String sName = "";
private Type fieldType = Type.INT;
private boolean bCanBeNull = true;
private boolean bPrimaryKey = false;
// methods
public DataField(int index, String sName, Type fieldType,
boolean bCanBeNull, boolean bPrimaryKey) {
this.index = index;
this.sName = sName;
this.fieldType = fieldType;
this.bCanBeNull = bCanBeNull;
this.bPrimaryKey = bPrimaryKey;
}
public String getColumnDefinition() {
String s = sName + " " + getSqlType(fieldType);
if (bPrimaryKey)
s += " PRIMARY KEY";
if (!bCanBeNull)
s += " NOT NULL";
return s;
}
public Type getType() {
return fieldType;
}
public int getIndex() {
return index;
}
public String getSqlType(Type value) {
switch (value) {
case INT:
return "INTEGER";
case TEXT:
return "TEXT";
case BOOL:
return "INTEGER";
}
return "TEXT";
}
public void setParentRow(DataRow dataRow) {
this.dataRow = dataRow;
this.values = this.dataRow.getContentValues();
}
public String getName() {
return sName;
}
// getters
public String asString() {
return values.getAsString(sName);
}
public long asLong() {
return values.getAsLong(sName);
}
public boolean asBoolean() {
return (values.getAsLong(sName) == 1);
}
public boolean isNull() {
return (values.get(sName) == null);
}
public Calendar asCalendar() {
dateOut.setTimeInMillis(values.getAsLong(sName));
return dateOut;
}
// setters
public void set(String value) {
values.put(sName, value);
}
public void set(long value) {
values.put(sName, value);
}
public void set(boolean value) {
int i = (value) ? 1 : 0;
values.put(sName, i);
}
public void set(Calendar value) {
values.put(sName, value.getTimeInMillis());
}
public void setNull() {
values.put(sName, (String) null);
}
}
Monday, May 24, 2010
Làm việc với Database trên Android (P1)
+ Sử dụng Shared Preferences
+ Sử dụng file
+ Sử dụng SQLite
Với Shared Preferences (Là lưu dữ liệu dưới dạng một cặp khóa - dữ liệu dưới dạng file xml, rất tiện cho việc lưu trữ cấu hình của ứng dụng) và lưu dữ liệu dưới dạng File thì đơn giản nên mình không đề cập ở đây.
Kiến thức về SQLite mình cũng không nhắc đến nữa, vì mình đã có bài nói về vấn đề này trước đây rồi.
Kinh nghiệm của mình để làm việc với SQLite là:
+ Tạo một lớp quản lý chung với một số tác vụ:
-Tạo database
-Chạy câu SQL
-Tạo table
....
+ Tạo một lớp thể hiện một trường trong database bởi SQLite chỉ hỗ trợ 3 loại dữ liệu nên việc này cũng rất đơn giản.
+ Tạo một lớp thể hiện cho một dòng (row) dữ liệu
+ Tạo một lớp cho thể hiện một table.
Sau đây chúng ta sẽ đi lần lượt từng công việc trên
Đầu tiên ta sẽ đi từ việc tạo một lớp quản lý chung việc tạo database cụ thể bạn có thể xem VD để hiểu việc này
package com.tannm.doan.taskmanager.database; import java.util.Vector; import com.tannm.doan.taskmanager.R; import android.content.*; import android.database.sqlite.*; import android.database.*; public class Database { // types public enum Result { Success, errUnknown, errCantInsertNewData, errCantUpdateData, errCantCreateTable, errNoDbAccess, errCantGetDataFromTable, errCantFindData, errCantGetData, errCantDeleteData, errTableNotExists, errCantSetValuesForDataRow, errCantGetValuesFromDataRow, }; // fields public static final String sTableNameAppointments = "Appointments"; public static final String sTableNameTasks = "Tasks"; public static final String sTableNameNotes = "Notes"; public static final String sTableNameAlarms = "Alarms"; public static final String sTableNameEvent = "Event"; // fields private final String dbName = "TaskManagerDatabase.db"; private Context ctx = null; private SQLiteDatabase db = null; private Result resultDbTablesCreated = Result.errUnknown; // methods public Database(Context context) { ctx = context; open(); createTables(); } public final String getName() { return dbName; } public static int getErrDesc(Result result) { int msgId = R.string.errUnknown; if (result == Result.errCantInsertNewData) msgId = R.string.errCantInsertNewData; if (result == Result.errCantUpdateData) msgId = R.string.errCantUpdateData; if (result == Result.errCantCreateTable) msgId = R.string.errCantCreateTable; if (result == Result.errNoDbAccess) msgId = R.string.errNoDbAccess; if (result == Result.errCantGetDataFromTable) msgId = R.string.errCantGetDataFromTable; if (result == Result.errCantFindData) msgId = R.string.errCantFindData; if (result == Result.errCantGetData) msgId = R.string.errCantGetData; if (result == Result.errCantDeleteData) msgId = R.string.errCantDeleteData; if (result == Result.errTableNotExists) msgId = R.string.errTableNotExists; if (result == Result.errCantSetValuesForDataRow) msgId = R.string.errCantSetValuesForDataRow; if (result == Result.errCantGetValuesFromDataRow) msgId = R.string.errCantGetValuesFromDataRow; return msgId; } public boolean open() { boolean bSuccess = false; // open / create database db = ctx.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null); // test result if (isOpened()) { bSuccess = true; } else { db = null; } return bSuccess; } public void close() { if (isOpened()) db.close(); } public boolean isOpened() { if (db != null) return true; return false; } public SQLiteDatabase getSQLiteDb() { return db; } public boolean delete() { close(); return ctx.deleteDatabase(dbName); } public boolean execSQL(String sql) { boolean bSuccess = false; try { if (isOpened()) db.execSQL(sql); bSuccess = true; } catch (SQLException e) { } return bSuccess; } public boolean tableExists(String sTableName) { boolean bResult = false; if (isOpened()) { String sql = "select name from sqlite_master where type = 'table' and name = '%s'"; sql = String.format(sql, sTableName); Cursor cr = db.rawQuery(sql, null); if (cr.getCount() > 0) bResult = true; cr.close(); } return bResult; } private void createTables() { resultDbTablesCreated = Database.Result.errUnknown; if (isOpened()) { VectorvecDataRows = new Vector (); vecDataRows.add(new DataRowAppointment(this)); vecDataRows.add(new DataRowTask(this)); vecDataRows.add(new DataRowNote(this)); vecDataRows.add(new DataRowAlarm(this)); for (int i = 0; i < vecDataRows.size(); i++) { DataTable dataTable = new DataTable(vecDataRows.get(i)); if (dataTable.createTable()) { resultDbTablesCreated = Database.Result.Success; } else { resultDbTablesCreated = Database.Result.errCantCreateTable; break; } } } else { resultDbTablesCreated = Database.Result.errNoDbAccess; } } public boolean tablesCreated() { return resultDbTablesCreated == Result.Success; } public synchronized boolean databaseReady() { return (isOpened() && tablesCreated()); } public Result tablesCreationResult() { return resultDbTablesCreated; } }
Hãy đọc và suy ngẫm về ứng dụng cho các ví dụ tiếp theo.
(Còn tiếp)
Wednesday, January 13, 2010
Lấy địa chỉ IP của máy android của bạn!
Nhưng mình vừa học được cách lấy ip từ máy android share cho mọi người tham khảo:
Đầu tiên bạn có thể thấy trong lớp WifiInfo có phương thức getIpAddress().
Nhưng kết quả trả về lại là một số nguyên:
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
Hình như đây không phải là cách đúng.
Một cách khác cũng khá triển vọng là bạn có thể tạo một kết nối socket (socket connection) và dùng thể hiện của Socket để lấy địa chỉ ip của máy mình.
try {
Socket socket = new Socket("www.androidvn.net", 80);
Log.i("", socket.getLocalAddress().toString());
} catch (Exception e) {
Log.i("", e.getMessage());
}
Nhưng cách này hình như lại có một số điểm bất lợi:
+ Bạn tạo ra một kết nối, tạo ra một lưu lượng vận chuyển dẫn đến người dùng có thể bị đánh vào cước :D.
+ Doạn chương trình này được quyết định bởi server mà bạn kết nối tới.
+ Tại đây có thể xảy ra Exception.
Tư tưởng đúng cho trường hợp này là tìm và lặp trên tất các các giao tiếp mạng và tất cả địa chỉ IP :D.
public String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
return inetAddress.getHostAddress().toString();
}
}
}
} catch (SocketException ex) {
Log.e(LOG_TAG, ex.toString());
}
return null;
}
Nếu hàm trả về null có nghĩa là không có kết nối nào available.
Nếu hàm trả về một String thì đó là địa chỉ ip hiện tại của thiết bị khi kết nối wifi hay 3G.
Monday, January 11, 2010
Light Racer 2.0 - Day 9 - Adding Items
After working with the AI a bit, I realized that the game feels totally dull to me. I spent a little time thinking about game design and decided to go with levels and items. Items weren't designed in to the game at all so I have to add the whole concept in from scratch. The first item going in is the Speed Boost item. When a player picks it up, their racer will move a notch faster until the end of the round. I have a problem though, the game is coded to run at the speed of the current level, not of the current player.
Here's what I did today:
Built Project Plan
Designed Icon
Implemented basic non-working SpeedBoostItem that could just draw itself
Added SpeedBoostItem to the world
Changed the way motor sounds are handled so they will work at different speeds
Modified game to allow for different speed players
Added a method incrementSpeed() to Player
Added item collision detection
Added random item placement - needs refining
Animated SpeedBoostItem
The hardest thing was adding the concept of speed on to the Player class, which is necessary to have players run at independent speeds. The problem was that all of the AI code and collision detection was using the overall game's speed to calculate distances. I had to change several methods to use the specific player's speed in the calculations, which took about an hour. After that, it was fairly straight-forward.
I drew the images of the item in photoshop:
| |
I created the item class and added it to the world. The main game is responsible for placing the item. After that, the item is responsible for detecting a collision and responding. When it's done, it will set a flag that it is finished and the game will remove it from the world. The collision detection is very simple. Here's how it works:
// check to see if a player is colliding with me, do something to that player
int top = y - (int) (height * .5) - PLAYER_ITEM_TOUCH_PADDING;
int bottom = y + (int) (height * .5) + PLAYER_ITEM_TOUCH_PADDING;
int left = x - (int) (width * .5) - PLAYER_ITEM_TOUCH_PADDING;
int right = x + (int) (width * .5) + PLAYER_ITEM_TOUCH_PADDING;
Player[] players = world.players;
Player player;
int lx, ly, cx, cy;
for (int playerIndex = 0; playerIndex < players.length; playerIndex++) {
player = players[playerIndex];
lx = player.lastX;
ly = player.lastY;
cx = player.curX;
cy = player.curY;
if (cx == lx && (cx >= left && cx <= right)) {
// moving vertical within horizontal bounds
if ((ly >= bottom && cy < bottom) || (cy > top && ly <= top)) {
// collision
doCollision(player);
}
} else if (cy == ly && (cy >= top && cy <= bottom)) {
// moving horizontal within vertical bounds
if ((lx >= right && cx < right) || (cx > left && lx <= left)) {
// collision
doCollision(player);
}
}
}
}
The animation code is the same code I always use for animations
For now the game is just randomly dropping the item all over but once I have all 3 items in, I'll work out the balance of the item drop. For now it really added a new dynamic element to the game. I can see the game getting much more fun in the next few weeks!
Here's a video of the new item working.
Friday, January 8, 2010
Bắt đầu với google maps trên android
http://code.google.com/android/maps-api-signup.html
Trước đó bạn phải kiếm cái MD5 của máy mình đã bằng cách
Kiểm tra thư mục C:Documents and Settings<UserName>.android
Của mình cụ thể là C:Documents and SettingsTanNM.android
Bạn sẽ thấy cái file debug.keystore
copy nó vào trong C:Program FilesJavajdkbin
trên dòng lệnh command bạn cd vào thư mục C:Program FilesJavajdkbin
Chạy câu lệnh:
keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android
Sẽ được một kq giống như:
1. androiddebugkey, Oct 27, 2009, PrivateKeyEntry,
2. Certificate fingerprint (MD5): E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3
Sau đó vào trang
http://code.google.com/android/maps-api-signup.html
điền cái mã MD5:
E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3
vào và submit, ta sẽ có một key giống như:
09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA
Ngon roài!!!!!!
Tiếp đến ta tạo một ứng dụng đơn giản trên myeclipse
file Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ftl.map">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="@string/activity_sample_code"
android:icon="@drawable/icon">
<uses-library android:name="com.google.android.maps" />
<activity android:name="com.ftl.map.view.MapViewDemo"
android:label="MapView">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
</application>
</manifest>
file main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.google.android.maps.MapView
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:enabled="true" android:clickable="true" android:apiKey="09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA" />
</LinearLayout>
file java Activity
package com.ftl.map.view;
import android.os.Bundle;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class MapViewDemo extends MapActivity {
private MapView myMapView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.ftl.map.R.layout.main);
}
@Override
protected boolean isRouteDisplayed() {
return true;
}
}
Chạy nào!!!!!