[置顶] 一步一步学android OpenGL ES2.0编程(1)

分类: android开发2013-01-23 11:01 10295人阅读 评论(9) 收藏 举报

androidAndroidANDROIDOpenGLopenglopenGLOPENGLOpenglOpenGl

目录(?)[+]

(欢迎大家加入android技术交流QQ群:209796692

 

建立OpenGL ES环境

 

为了能在你的Android应用中使用OpenGLES绘画,你必须创建一个view作为容器。而最直接的方式就是从GLSurfaceViewGLSurfaceView.Renderer分别派生一个类。GLSurfaceView作为OpenGL绘制所在的容器,而实际的绘图动作都是在GLSurfaceView.Renderer里面发生的。

使用GLSurfaceView几乎是整合OpenGLES到你的应用的唯一方式。对于一个全屏或近全屏的graphicsview,它是最好的选择。如果只是在某个小部分显示OpenGLES图形则可以考虑TextureView。当然如果你比较变态,你完全可以使用SurfaceView创建一个OpenGLES view

本教程演示如何完成一个最小实现的OpenGLES2.0应用。

Manifest中声明使用OpenGLES

 

为了能使用OpenGLES 2.0 API,你必须在你的manifest中添加以下声明:

如果你的应用要使用纹理压缩功能,你必须还要声明设备需要支持什么样的压缩格式:

OpenGLES Graphics创建一个Activity

 

这个Activity与其它类型应用的Activity并无不同,要说不同,也仅仅是放到ActivitylayoutView不一样,你需要放入的是一个GLSurfaceView

下面的代码演示了使用GLSurfaceView作为主视图的Acitivity的最少代码实现:

public class OpenGLES20 extends Activity {     private GLSurfaceView mGLView;     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);         //创建一个GLSurfaceView实例然后设置为activityContentView.        mGLView = new MyGLSurfaceView(this);        setContentView(mGLView);    }}

:OpenGL ES 2.0需要Android2.2 (API Level 8) 及以上版本。

构键一个GLSurfaceView对象

 

GLSurfaceView中其实不需要做太多工作,实际的绘制任务都在GLSurfaceView.Renderer中了。所以GLSurfaceView中的代码也非常少,你甚至可以直接使用GLSurfaceView,但是,然而,别这样做。因为你需要扩展这个类来响应触摸事件啊孩子。

扩展GLSurfaceView的类像这样写就可以了:

class MyGLSurfaceView extends GLSurfaceView {     public MyGLSurfaceView(Context context){        super(context);         //设置RendererGLSurfaceView        setRenderer(new MyRenderer());    }}

当使用OpenGLES 2.0时,你必须在GLSurfaceView构造器中调用另外一个函数,它说明了你将要使用2.0版的API

// 创建一个OpenGL ES 2.0 contextsetEGLContextClientVersion(2);

另一个可以添加的你的GLSurfaceView实现的可选的操作是设置render模式为只在绘制数据发生改变时才绘制view。使用GLSurfaceView.RENDERMODE_WHEN_DIRTY

// 只有在绘制数据改变时才绘制viewsetRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

此设置会阻止绘制GLSurfaceView的帧,直到你调用了requestRender(),这样会非常高效。

构建一个Renderer

 

此类控制向GLSurfaceView的绘制工作。它有三个方法被Android系统调用来计算在GLSurfaceView上画什么以及如何画。

· 

onSurfaceCreated()仅调用一次,用于设置viewOpenGLES环境。

· 

· 

onDrawFrame()每次View被重绘时被调用。

· 

· 

onSurfaceChanged()如果view的几和形状发生变化了就调用,例如当竖屏变为横屏时。

· 

下面是一个OpenGLES renderer的最基本的实现,它仅在GLSurfaceView画了一个灰色的背景:

public class MyGL20Renderer implements GLSurfaceView.Renderer {     public void onSurfaceCreated(GL10 unused, EGLConfig config) {        //设置背景的颜色        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);    }     public void onDrawFrame(GL10 unused) {        // 重绘背景色        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);    }     public void onSurfaceChanged(GL10 unused, int width, int height) {        GLES20.glViewport(0, 0, width, height);    }}

以上就是所有需要做的东西!上面的代码们创建了一个简单的Android应用,它使用OpenGL显示了一个灰色的屏幕。但这段代码并没有做什么有趣的事情,只是为使用OpenGL绘图做好了准备。

:你可以不明白为什么这些方法们都具有一个GL10参数,但你使用的却是OpengGLES 2.0 API们。这其实是为了使Android框架能简单的兼容各OpenGLES版本而做的。

如果你对OpenGLES API很熟悉,你其实现在已经可以开始进行绘图了。然而,如果你熟悉,就继续学习下一章吧。

 

[置顶] 一步一步学android OpenGL ES2.0编程(2)

分类: android开发2013-01-31 14:42 11972人阅读 评论(6) 收藏 举报

androidAndroidANDROIDOpenGLopenglopenGLOPENGLOpenglOpenGl

目录(?)[+]

(欢迎大家加入android技术交流QQ群:209796692

 

定义形状

会定义在OpenGLES view上所绘制的形状,是你创建高端图形应用杰作的第一步。如果你不懂OpenGLES定义图形对象的一些基本知识,使用OpenGLES可能有一点棘手。

本文解释OpenGLES相对于Android设备屏幕的坐标系统、定义一个形状的基础知识、形状的外观、以及如何定义三角形和正方形。

定义一个三角形

 

OpenGLEs允许你使用坐本在三个维度上定义绘制对象。所以,在你可以绘制一个三角形之前,你必须定义它的坐标。在OpenGL中,典型的方式是为坐标定义一个浮点类型的顶点数组。为了最高效,你应把这些坐标都写进一个ByteBuffer,它会被传到OpenGLES图形管线以进行处理。

class Triangle {    private FloatBuffer vertexBuffer;    // 数组中每个顶点的坐标数    static final int COORDS_PER_VERTEX = 3;    static float triangleCoords[] = { // 按逆时针方向顺序:         0.0f,  0.622008459f, 0.0f,   // top        -0.5f, -0.311004243f, 0.0f,   // bottom left         0.5f, -0.311004243f, 0.0f    // bottom right    };    // 设置颜色,分别为red, green, blue alpha (opacity)    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };    public Triangle() {        // 存放形状的坐标,初始化顶点字节缓冲        ByteBuffer bb = ByteBuffer.allocateDirect(                // (坐标数 * 4)float占四字节                triangleCoords.length * 4);        // 设用设备的本点字节序        bb.order(ByteOrder.nativeOrder());        // ByteBuffer创建一个浮点缓冲        vertexBuffer = bb.asFloatBuffer();        // 把坐标们加入FloatBuffer        vertexBuffer.put(triangleCoords);        // 设置buffer,从第一个坐标开始读        vertexBuffer.position(0);    }}

缺省情况下,OpenGLES 假定[0,0,0](X,Y,Z) GLSurfaceView 帧的中心,[1,1,0]是右上角,[-1,-1,0]是左下角。

注意此形状的坐标是按逆时针方向定义的。绘制顺序很重要,因为它定义了哪面是形状的正面,哪面是反面,使用OpenGLES cullface特性,你可以只画正面而不画反面。

定义一个正方形

 

OpenGL中定义正方形是十分容易的,有很多方法能做的,但是典型的做法是使用两个三角形:

1.使用两个三角形画一个正方形

你要为两个三角形都按逆时针方向定义顶点们,并且将这些坐标值们放入一个ByteBuffer中。为了避免分别为两个三角形定义两个坐标数组,我们使用一个绘制列表来告诉OpenGLES图形管线如果画这些顶点们。下面就是这个形状的代码:

class Square {    private FloatBuffer vertexBuffer;    private ShortBuffer drawListBuffer;    // 每个顶点的坐标数    static final int COORDS_PER_VERTEX = 3;    static float squareCoords[] = { -0.5f,  0.5f, 0.0f,   // top left                                    -0.5f, -0.5f, 0.0f,   // bottom left                                     0.5f, -0.5f, 0.0f,   // bottom right                                     0.5f,  0.5f, 0.0f }; // top right    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // 顶点的绘制顺序    public Square() {        // initialize vertex byte buffer for shape coordinates        ByteBuffer bb = ByteBuffer.allocateDirect(        // (坐标数 * 4)                squareCoords.length * 4);        bb.order(ByteOrder.nativeOrder());        vertexBuffer = bb.asFloatBuffer();        vertexBuffer.put(squareCoords);        vertexBuffer.position(0);        // 为绘制列表初始化字节缓冲        ByteBuffer dlb = ByteBuffer.allocateDirect(        // (对应顺序的坐标数 * 2)short2字节                drawOrder.length * 2);        dlb.order(ByteOrder.nativeOrder());        drawListBuffer = dlb.asShortBuffer();        drawListBuffer.put(drawOrder);        drawListBuffer.position(0);    }}

本例让你见识了用OpenGL如何创建更复杂的形状。通常,你都是使用一群小三(三角形)来绘制对象。下一章,你将学会如何将这些形状画到屏幕上。

 

[置顶] 一步一步学android OpenGL ES2.0编程(3)

分类: android开发2013-02-20 10:02 13677人阅读 评论(13) 收藏 举报

OpenGLES

目录(?)[+]

绘制形状

你定义了要绘制的形状后,你就要画它们了。使用OpenGLES 2.0会形状会有一点点复杂,因为API提供了大量的对渲染管线的控制能力。

本文讲解如何绘制你在前文中定义的那些形状们。

初始化形状

 

在你做任何绘制之前,你必须初始化形状然后加载它。除非形状的结构(指原始的坐标们)在执行过程中发生改变,你都应该在你的Renderer的方法onSurfaceCreated()中进行内存和效率方面的初始化工作。

public void onSurfaceCreated(GL10 unused, EGLConfig config) {    ...    // 初始化一个三角形    mTriangle = new Triangle();    // 初始化一个正方形    mSquare = new Square();}

绘制一个形状

 

使用OpenGLES 2.0画一个定义好的形状需要一大坨代码,因为你必须为图形渲染管线提供一大堆信息。典型的,你必须定义以下几个东西:

· 

VertexShader-用于渲染形状的顶点的OpenGLES 图形代码。

· 

· 

FragmentShader-用于渲染形状的外观(颜色或纹理)的OpenGLES 代码。

· 

· 

Program-一个OpenGLES对象,包含了你想要用来绘制一个或多个形状的shader

· 

你至少需要一个vertexshader来绘制一个形状和一个fragmentshader来为形状上色。这些形状必须被编译然后被添加到一个OpenGLES program中,program之后被用来绘制形状。下面是一个展示如何定义一个可以用来绘制形状的基本shader的例子:

private final String vertexShaderCode =    "attribute vec4 vPosition;" +    "void main() {" +    "  gl_Position = vPosition;" +    "}";private final String fragmentShaderCode =    "precision mediump float;" +    "uniform vec4 vColor;" +    "void main() {" +    "  gl_FragColor = vColor;" +    "}";

Shader们包含了OpenGLShading Language (GLSL)代码,必须在使用前编译。要编译这些代码,在你的Renderer类中创建一个工具类方法:

public static int loadShader(int type, String shaderCode){    // 创建一个vertex shader类型(GLES20.GL_VERTEX_SHADER)    // fragment shader类型(GLES20.GL_FRAGMENT_SHADER)    int shader = GLES20.glCreateShader(type);    // 将源码添加到shader并编译之    GLES20.glShaderSource(shader, shaderCode);    GLES20.glCompileShader(shader);    return shader;}

为了绘制你的形状,你必须编译shader代码,添加它们到一个OpenGLES program 对象然后链接这个program。在renderer对象的构造器中做这些事情,从而只需做一次即可。

:编译OpenGLES shader们和链接linkingprogram们是很耗CPU的,所以你应该避免多次做这些事。如果在运行时你不知道shader的内容,你应该只创建一次code然后缓存它们以避免多次创建。

public Triangle() {    ...    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);    mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program    GLES20.glAttachShader(mProgram, vertexShader);   // vertex shader添加到program    GLES20.glAttachShader(mProgram, fragmentShader); // fragment shader添加到program    GLES20.glLinkProgram(mProgram);                  // 创建可执行的 OpenGL ES program}

此时,你已经准备好增加真正的绘制调用了。需要为渲染管线指定很多参数来告诉它你想画什么以及如何画。因为绘制操作因形状而异,让你的形状类包含自己的绘制逻辑是个很好主意。

创建一个draw()方法负责绘制形状。下面的代码设置位置和颜色值到形状的vertexshaderfragmentshader,然后执行绘制功能。

public void draw() {    // program加入OpenGL ES环境中    GLES20.glUseProgram(mProgram);    // 获取指向vertex shader的成员vPosition的 handle    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");    // 启用一个指向三角形的顶点数组的handle    GLES20.glEnableVertexAttribArray(mPositionHandle);    // 准备三角形的坐标数据    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,                                 GLES20.GL_FLOAT, false,                                 vertexStride, vertexBuffer);    // 获取指向fragment shader的成员vColorhandle     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");    // 设置三角形的颜色    GLES20.glUniform4fv(mColorHandle, 1, color, 0);    // 画三角形    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);    // 禁用指向三角形的顶点数组    GLES20.glDisableVertexAttribArray(mPositionHandle);}

一旦你完成这些代码,画这个对象只需要在RendereronDrawFrame()调用draw()方法。当你运行应用时,它应该看起来这样:

1.没有投射和视口时画三角形

此例子中的代码还有很多问题。首先,它不会给人留下印像。其次,三角形会在你从竖屏变为横屏时被压扁。三角形变形的原因是其顶点们没有跟据屏幕的宽高比进行修正。你可以使用投射和视口更正这个问题。那在下一讲了。

[置顶] 一步一步学android OpenGL ES2.0编程(4)

分类: android开发2013-03-01 06:21 10657人阅读 评论(5) 收藏 举报

androidOpenGLES

目录(?)[+]

应用投影和相机视口

OpenGLES环境中,投影和相机视口使你绘制的对象以更接近物理对象的样子显示。这是通过对坐标精确的数学变换实现的。

· 

投影-这种变换跟据所在GLSurfaceView的宽和高调整对象的坐标。如果没有此变换,对象会被不规则的视口扭曲。投射变换一般只需要在OpenGLview创建或发生变化时调用,代码写在rendereronSurfaceChanged()方法中。

· 

· 

相机视口-此变换基于一个虚拟相机的位置调整对象的坐标。注意OpenGLES并没有定义一个真的相机对象,而是提供了一些工具方法变换绘制对象的显示来模拟一个相机。一个相机视口的变换可能只在创建GLSurfaceView时调用,或跟据用户动作动态调用。

· 

本文讲解了如何创建一个投影和一个相机视口然后应用到GLSurfaceView的形状绘制过程。

定义一个投影

 

投影变换的数据是在GLSurfaceView.Renderer 类的 onSurfaceChanged() 方法中计算。下面的例子跟据GLSurfaceView 的宽和高,使用Matrix.frustumM()方法计算出了一个投影变换Matrix

@Overridepublic void onSurfaceChanged(GL10 unused, int width, int height) {    GLES20.glViewport(0, 0, width, height);    float ratio = (float) width / height;    // 此投影矩阵在onDrawFrame()中将应用到对象的坐标    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);}

以下代码产生了一个投影矩阵mProjMatrix ,你可以把它在 onDrawFrame() 方法中与一个相机视口变换结合。

: 只对你的对象应用一个投影变换一般会导制什么也看不到。通常,你必须也对其应用一个视口变换才能看到东西。

定义一个相机视口

 

再定义一个相机视口变换以使对绘制对象的变换处理变得完整。在下面的例子中,使用方法Matrix.setLookAtM()计算相机视口变换,然后结合前面所计算的投影矩阵。结合后的变换矩阵之后传给要绘制的对象。

@Overridepublic void onDrawFrame(GL10 unused) {    ...    // 设置相机的位置(视口矩阵)    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);    // 计算投影和视口变换    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);    // 绘制形状    mTriangle.draw(mMVPMatrix);}

应用投影和相机视口变换

 

为了使用前面的合并后的投影和相机视口变换矩阵,修改你的图形对象的方法draw(),接受结果矩阵并应用到你的形状上:

public void draw(float[] mvpMatrix) { // 传递计算出的变换矩阵    ...    // 获得形状的变换矩阵的handle    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");    // 应用投影和视口变换    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);    // 绘制三角形    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);    ...}

一旦你正确的计算和应用了投影和视口变换,你的图像将出现在正确的位置,看起来像下面这样:

1.应用了投影和视口变换后绘制的三角形

现在你拥有了一个正确显示你的形状的应用了,是让你的图形动起来的时候了...嘿嘿...

 

[置顶] 一步一步学android OpenGL ES2.0编程(5)

分类: android开发2013-03-08 14:58 7865人阅读 评论(1) 收藏 举报

OpenGLES

目录(?)[+]

添加运动

在屏幕上绘制是OpenGL的基础能力,但是你也可以用其它的Android图形框架类来做,包括CanvasDrawable。 但是OpenGL ES提供了另外的能力,可以在三维上移动和变换对象。总之它能创造很牛B的用户体验。在本文中,你将学会如何使用OpenGL ES为形状添加旋转功能。

转动一个形状

使用OpenGL ES 2.0旋转一个对象也是十分简单地。你创建另外一个变换矩阵(一个旋转矩阵)然后把它合并到你的投影和相机视口变换矩阵就行了:

[java] view plaincopy

1. private float[] mRotationMatrix = new float[16];  

2. public void onDrawFrame(GL10 gl) {  

3.     ...  

4.     // 为三角形创建一个旋转变换  

5.     long time = SystemClock.uptimeMillis() % 4000L;  

6.     float angle = 0.090f * ((int) time);  

7.     Matrix.setRotateM(mRotationMatrix, 0, mAngle, 00, -1.0f);  

8.   

9.     // 把旋转矩阵合并到投影和相机矩阵  

10.     Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);  

11.   

12.     // 画三角形  

13.     mTriangle.draw(mMVPMatrix);  

14. }  

如果你的三角形在此新后转不起来,则要查看是否把GLSurfaceView.RENDERMODE_WHEN_DIRTY 设置注释了,下面马上就讲到。

启用持续渲染

到现在,你应在代码中注释掉设置只在数据改变时才渲染的代码,否则,OpenGL 只有转一次然后等待直到GLSurfaceView 的包含者调用requestRender()

[java] view plaincopy

1. public MyGLSurfaceView(Context context) {  

2.     ...  

3.     // Render the view only when there is a change in the drawing data  

4.     //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 注释掉以自动旋转  

5. }  

除非你不让对象与用户有交互,否则启用这个设置是一个好做法。要准备解除这句的注释了,因为下一讲会用到它。

[置顶] 一步一步学android OpenGL ES2.0编程(6 大结局)

分类: android开发 jni OpenGLES2013-03-14 16:04 10060人阅读 评论(3) 收藏 举报

目录(?)[+]

响应触摸事件

使你的OpenGL ES应用能响应触摸的关键是扩展你实现的GLSurfaceView 代码,覆写onTouchEvent() 方法来监听触摸事件。 

本文向你展示如何监听用户的触摸事件以使用户可以旋转某个OpenGL ES对象。

设置一个触摸监听器

为了使你的OpenGL Es应用响应触摸事件,你必须在你的GLSurfaceView 类中实现onTouchEvent()事件。下面的例子演示了如何监听MotionEvent.ACTION_MOVE 事件然后把它们转换成一个形状的旋转角度。

[java] view plaincopy

1. @Override  

2. public boolean onTouchEvent(MotionEvent e) {  

3.     // MotionEvent带有从触摸屏幕来的输入的详细信息以及其它输入控制  

4.     // 此处,你只需对触摸位置的改变感兴趣即可。  

5.   

6.     float x = e.getX();  

7.     float y = e.getY();  

8.   

9.     switch (e.getAction()) {  

10.         case MotionEvent.ACTION_MOVE:  

11.   

12.             float dx = x - mPreviousX;  

13.             float dy = y - mPreviousY;  

14.   

15.             // reverse direction of rotation above the mid-line  

16.             if (y > getHeight() / 2) {  

17.               dx = dx * -1 ;  

18.             }  

19.   

20.             // reverse direction of rotation to left of the mid-line  

21.             if (x < getWidth() / 2) {  

22.               dy = dy * -1 ;  

23.             }  

24.   

25.             mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR;  // = 180.0f / 320  

26.             requestRender();  

27.     }  

28.   

29.     mPreviousX = x;  

30.     mPreviousY = y;  

31.     return true;  

32. }  

注意在计算完旋转角度之后,本方法调用requestRender() 来告诉renderer要渲染帧了。这样做是很高效的,因为在没有发生旋转时不需要重画帧。然而,在你没有要求只在数据发生改变才重画之前,还不能达到最高效,即别忘了解除这一句的注释:

[java] view plaincopy

1. public MyGLSurfaceView(Context context) {  

2.     ...  

3.     // Render the view only when there is a change in the drawing data  

4.     setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);  

5. }  

曝露出旋转角度

上面的例子要求你向其它类曝露出你的旋转角度,所以你要为你的renderer添加一个public成员。既然renderer的代码运行于主界面之外的单独线程中,你必须声明这个公开变量为volatile. 。下面的代码就是这样做的:

[java] view plaincopy

1. public class MyGLRenderer implements GLSurfaceView.Renderer {  

2.     ...  

3.     public volatile float mAngle;  

4. }  

应用旋转

要应用触摸所产生的旋转, 注释掉产生角度的代码并且添加mAngle,它包活了触摸所产生的角度:

[java] view plaincopy

1. public void onDrawFrame(GL10 gl) {  

2.     ...  

3.     // Create a rotation for the triangle  

4.     // long time = SystemClock.uptimeMillis() % 4000L;  

5.     // float angle = 0.090f * ((int) time);  

6.     Matrix.setRotateM(mRotationMatrix, 0, mAngle, 00, -1.0f);  

7.   

8.     // 合并旋转矩阵到投影和相机视口矩阵  

9.     Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);  

10.   

11.     // 画一个角度  

12.     mTriangle.draw(mMVPMatrix);  

13. }  

当你完成了上述的几步,运行程序然后在陪同幕上拖动你的手指头,你会看到下面这样:

 Figure 1. 跟据触摸输入的转动的三角形(圈圈显示了触摸的位置)

 

更多相关文章

  1. android和j2me之清屏(clearScreen)
  2. [置顶] android 图标的绘制
  3. android和j2me之清屏(clearScreen)
  4. Android绘制简单折线图的步骤
  5. Android中各种形状
  6. android 虚拟摇杆绘制
  7. Android仿美图秀秀给图片加框
  8. Android(安卓)Shape制定圆形和圆环形状
  9. Android(安卓)view 的cache

随机推荐

  1. 操作内存卡的常用代码
  2. android dpi DisplayMetrics
  3. 选择图文表
  4. Android 上下滚动的新闻效果
  5. [android]android自动化测试十之单元测试
  6. Android之十三图片2D翻转
  7. android 按钮的四种点击事件
  8. mono android 非UI线程操作UI线程
  9. android使用keystore打包错误解决方法
  10. Android App: 按键识别