这几天开着代理研究android(主要是长期以来 developer.android.com 被墙撞……),总算初步掌握了android的图形处理方式,准备在Blog上写一些经验性的总结。


一、Android Virtual Device仿真界面对应快捷


模拟项 设备键 PC键 Home HOME Menu (left softkey) F2 or Page-up button Star (right softkey) Shift-F2 or Page Down Back ESC Call/dial button F3 Hangup/end call button F4 Search F5 Power button F7 Audio volume up button KEYPAD_PLUS, Ctrl-5 Audio volume down button KEYPAD_MINUS, Ctrl-F6 Camera button Ctrl-KEYPAD_5, Ctrl-F3 Switch to previous layout orientation (for example, portrait, landscape) KEYPAD_7, F11 Switch to next layout orientation (for example, portrait, landscape) KEYPAD_9, F12 Toggle cell networking on/off F8 Toggle code profiling F9 (only with -trace startup option) Toggle fullscreen mode Alt-Enter Toggle trackball mode F6 Enter trackball mode temporarily (while key is pressed) Delete DPad left/up/right/down KEYPAD_4/8/6/2 DPad center click KEYPAD_5 Onion alpha increase/decrease KEYPAD_MULTIPLY(*) / KEYPAD_DIVIDE(/)


二、要想在View中绘制图形,至少需要用到以下三者

1、Bitmap

Android中使用Bitmap承载图像资源,其默认支持4种图像格式(调用方式,Bitmap.Config.XXXX),分别为ALPHA8、RGB565、ARGB4444、ARGB8888,单就显示效果而言以ARGB8888效果最佳,ALPHA8效果最差,当然占用资源与之相反。

不过Bitmap本身的运算量非常小,其具体操作大量依赖于native,如下所示:

private static native Bitmap nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable); private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable); private static native void nativeDestructor(int nativeBitmap); private static native void nativeRecycle(int nativeBitmap); private static native boolean nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage); private static native void nativeErase(int nativeBitmap, int color); private static native int nativeWidth(int nativeBitmap); private static native int nativeHeight(int nativeBitmap); private static native int nativeRowBytes(int nativeBitmap); private static native int nativeConfig(int nativeBitmap); private static native boolean nativeHasAlpha(int nativeBitmap); private static native int nativeGetPixel(int nativeBitmap, int x, int y); private static native void nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height); private static native void nativeSetPixel(int nativeBitmap, int x, int y, int color); private static native void nativeSetPixels(int nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height); private static native void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst); private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src); private static native Bitmap nativeCreateFromParcel(Parcel p); // returns true on success private static native boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable, Parcel p); // returns a new bitmap built from the native bitmap's alpha, and the paint private static native Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY);

2、Canvas

Android中使用Canvas对应标准Java应用中的Graphics,其具体操作与Graphics类似,但实际功能却较Graphics为少,需要同Paint配合使用。

与Bitmap近似,Canvas主要实现依赖于OpenGL ES,其本身的运算量非常之小,Canvas本地接口如下所示:


private static native int initRaster(int nativeBitmapOrZero); private static native int initGL(); private static native void native_setBitmap(int nativeCanvas, int bitmap); private static native void nativeSetViewport(int nCanvas, int w, int h); private static native int native_saveLayer(int nativeCanvas, RectF bounds, int paint, int layerFlags); private static native int native_saveLayer(int nativeCanvas, float l, float t, float r, float b, int paint, int layerFlags); private static native int native_saveLayerAlpha(int nativeCanvas, RectF bounds, int alpha, int layerFlags); private static native int native_saveLayerAlpha(int nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags); private static native void native_concat(int nCanvas, int nMatrix); private static native void native_setMatrix(int nCanvas, int nMatrix); private static native boolean native_clipRect(int nCanvas, float left, float top, float right, float bottom, int regionOp); private static native boolean native_clipPath(int nativeCanvas, int nativePath, int regionOp); private static native boolean native_clipRegion(int nativeCanvas, int nativeRegion, int regionOp); private static native void nativeSetDrawFilter(int nativeCanvas, int nativeFilter); private static native boolean native_getClipBounds(int nativeCanvas, Rect bounds); private static native void native_getCTM(int canvas, int matrix); private static native boolean native_quickReject(int nativeCanvas, RectF rect, int native_edgeType); private static native boolean native_quickReject(int nativeCanvas, int path, int native_edgeType); private static native boolean native_quickReject(int nativeCanvas, float left, float top, float right, float bottom, int native_edgeType); private static native void native_drawRGB(int nativeCanvas, int r, int g, int b); private static native void native_drawARGB(int nativeCanvas, int a, int r, int g, int b); private static native void native_drawColor(int nativeCanvas, int color); private static native void native_drawColor(int nativeCanvas, int color, int mode); private static native void native_drawPaint(int nativeCanvas, int paint); private static native void native_drawLine(int nativeCanvas, float startX, float startY, float stopX, float stopY, int paint); private static native void native_drawRect(int nativeCanvas, RectF rect, int paint); private static native void native_drawRect(int nativeCanvas, float left, float top, float right, float bottom, int paint); private static native void native_drawOval(int nativeCanvas, RectF oval, int paint); private static native void native_drawCircle(int nativeCanvas, float cx, float cy, float radius, int paint); private static native void native_drawArc(int nativeCanvas, RectF oval, float startAngle, float sweep, boolean useCenter, int paint); private static native void native_drawRoundRect(int nativeCanvas, RectF rect, float rx, float ry, int paint); private static native void native_drawPath(int nativeCanvas, int path, int paint); private native void native_drawBitmap(int nativeCanvas, int bitmap, float left, float top, int nativePaintOrZero, boolean autoScale, float densityScale); private native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, RectF dst, int nativePaintOrZero); private static native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, Rect dst, int nativePaintOrZero); private static native void native_drawBitmap(int nativeCanvas, int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, int nativePaintOrZero); private static native void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, int nMatrix, int nPaint); private static native void nativeDrawBitmapMesh(int nCanvas, int nBitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, int nPaint); private static native void nativeDrawVertices(int nCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, int nPaint); private static native void native_drawText(int nativeCanvas, char[] text, int index, int count, float x, float y, int paint); private static native void native_drawText(int nativeCanvas, String text, int start, int end, float x, float y, int paint); private static native void native_drawPosText(int nativeCanvas, char[] text, int index, int count, float[] pos, int paint); private static native void native_drawPosText(int nativeCanvas, String text, float[] pos, int paint); private static native void native_drawTextOnPath(int nativeCanvas, char[] text, int index, int count, int path, float hOffset, float vOffset, int paint); private static native void native_drawTextOnPath(int nativeCanvas, String text, int path, float hOffset, float vOffset, int paint); private static native void native_drawPicture(int nativeCanvas, int nativePicture); private static native void finalizer(int nativeCanvas);

3、Paint

用以设定图像的绘制方式,可以同Canvas一道看作Graphics功能的拆分,只有当Canvas与Paint共同使用时,才可能实现Graphics的大部分功能(并非全部)。

Paint依旧大量依赖于本地接口,具体函数如下:

private static native int native_init(); private static native int native_initWithPaint(int paint); private static native void native_reset(int native_object); private static native void native_set(int native_dst, int native_src); private static native int native_getStyle(int native_object); private static native void native_setStyle(int native_object, int style); private static native int native_getStrokeCap(int native_object); private static native void native_setStrokeCap(int native_object, int cap); private static native int native_getStrokeJoin(int native_object); private static native void native_setStrokeJoin(int native_object, int join); private static native boolean native_getFillPath(int native_object, int src, int dst); private static native int native_setShader(int native_object, int shader); private static native int native_setColorFilter(int native_object, int filter); private static native int native_setXfermode(int native_object, int xfermode); private static native int native_setPathEffect(int native_object, int effect); private static native int native_setMaskFilter(int native_object, int maskfilter); private static native int native_setTypeface(int native_object, int typeface); private static native int native_setRasterizer(int native_object, int rasterizer); private static native int native_getTextAlign(int native_object); private static native void native_setTextAlign(int native_object, int align); private static native float native_getFontMetrics(int native_paint, FontMetrics metrics); private static native int native_getTextWidths(int native_object, char[] text, int index, int count, float[] widths); private static native int native_getTextWidths(int native_object, String text, int start, int end, float[] widths); private static native void native_getTextPath(int native_object, char[] text, int index, int count, float x, float y, int path); private static native void native_getTextPath(int native_object, String text, int start, int end, float x, float y, int path); private static native void nativeGetStringBounds(int nativePaint, String text, int start, int end, Rect bounds); private static native void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, int count, Rect bounds); private static native void finalizer(int nativePaint);


Android绘图的基本示例: canvas.drawBitmap(bitmap, 0, 0, paint) // canvas代表画布 bitmap作为图像资源 paint决定绘图方式

3、Android的窗口构成,即Activity + View = Form。

Android以Activity类作为窗体容器,以View(或SurfaceView等)绘制所有的按钮及界面,以AndroidManifest.xml配置相关的初始化资源,Android图形应用的一切通过它们衍生开来。

由于Android的线程安全机制并不等同于标准Java应用,invalidate()不能循环绘制View(偶实际试验了,丢到Thread里就死|||),所以View的图形绘制需要通过Handler处理,它将在函数内实现了一个类似于递归的循环线程,结构如下(具体实现请见Android提供的snake示例):

sendMessageDelayed->handleMessage->handleMessage 再次呼叫 sendMessageDelayed->handleMessage 被呼叫->循环

这种线程结构虽然简单且高效,但是很多常见的线程手段却无法在其中实现,因此只适合简单的应用(比如计时器,定时绘图等),复杂的线程结构则需要SurfaceView。

SurfaceView是View的子类,它继承自View。与只能使用Handler方式循环绘制图像的View不同,SurfaceView通过调用一对lockCanvas()和unlockCanvasAndPost()进行绘图,具体实现可见Google提供的LunarLander示例。

根据鄙人实测,View的Handler方式较SurfaceView的lockCanvas()与unlockCanvasAndPost()方式FPS为高,惟独可控性太差,本人推荐在简单应用使用View,在复杂应用使用SurfaceView。

另外xml中屏幕纵、横核定项如下:

screenOrientation="portrait" 纵向屏幕

screenOrientation="landscape" 横向屏幕

与之对应的代码设定方式是在Activity中setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)与setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)

PS:Android中所有图形设备都基于OpenGL ES,默认存在Z-Buffer,绝对不会出现Java桌面应用中的闪烁问题,算是一项进步。

下面我给出一个简单的Android绘图示例,也是LGame-Simple在Android中的最初原型代码,我们可以看到Android与标准Java桌面应用的差异实际上是极为细微的。


源码下载地址:http://code.google.com/p/loon-simple/downloads/list


PS:此用例中所有[LA]开头Java文件为Android下专用,否则皆为LGame-Simple通用。

LAGameView.java(对应LGame-Simple中的GameView)

package org.loon.framework.android.game; import system.images.LAGraphicsUtils; import android.app.Activity; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn * @version 0.1.0 */ public class LAGameView extends SurfaceView implements SurfaceHolder.Callback { // SurfaceView继承自View private final static long MAX_INTERVAL = 1000L; final static private int fpsX = 5; final static private int fpsY = 20; private transient boolean start, isFPS, running; private transient int width, height; private transient long maxFrames, curTime, startTime, offsetTime, curFPS, calcInterval; private transient double frameCount; private SurfaceHolder surfaceHolder; private CanvasThread mainLoop; private LAImage screen; private LAGraphics canvasGraphics; private LAHandler handler; private Rect rect; public LAGameView(Activity activity) { this(activity, false); } public LAGameView(Activity activity, boolean isLandscape) { super(activity.getApplicationContext()); LASystem.gc(); LASystem.setupHandler(activity, this); this.handler = LASystem.getSystemHandler(); this.handler.setLandscape(isLandscape); this.setOnCreateContextMenuListener(handler); this.setOnClickListener(handler); this.setOnFocusChangeListener(handler); this.setOnKeyListener(handler); this.setOnLongClickListener(handler); this.setOnTouchListener(handler); this.screen = new LAImage(width = handler.getWidth(), height = handler .getHeight()); this.rect = new Rect(0, 0, width, height); System.out.println("width=" + width + ",height=" + height); this.mainLoop = new CanvasThread(); this.surfaceHolder = getHolder(); this.surfaceHolder.addCallback(this); this.surfaceHolder.setSizeFromLayout(); this.setRunning(true); this.setFPS(LASystem.DEFAULT_MAX_FPS); this.canvasGraphics = screen.getLAGraphics(); this.setFocusable(true); this.setFocusableInTouchMode(true); this.requestFocus(); handler.getActivity().setContentView(this); } public void setScreen(ILAScreen screen) { this.handler.setScreen(screen); } public void destroy() { if (mainLoop != null) { mainLoop = null; } LAGraphicsUtils.destroyImages(); LASystem.gc(); } class CanvasThread extends Thread { public void run() { final LTimerContext timerContext = new LTimerContext(); timerContext.setTimeMillis(startTime = System.currentTimeMillis()); ILAScreen iscreen = null; Canvas canvas = null; do { if (!start) { continue; } iscreen = handler.getScreen(); canvasGraphics.drawClear(); iscreen.createUI(canvasGraphics); curTime = System.currentTimeMillis(); timerContext.setTimeSinceLastUpdate(curTime - timerContext.getTimeMillis()); timerContext.setSleepTimeMillis((offsetTime - timerContext .getTimeSinceLastUpdate()) - timerContext.getOverSleepTimeMillis()); if (timerContext.getSleepTimeMillis() > 0) { try { Thread.sleep(timerContext.getSleepTimeMillis()); } catch (InterruptedException e) { } timerContext.setOverSleepTimeMillis((System .currentTimeMillis() - curTime) - timerContext.getSleepTimeMillis()); } else { timerContext.setOverSleepTimeMillis(0L); } timerContext.setTimeMillis(System.currentTimeMillis()); iscreen.runTimer(timerContext); if (isFPS) { tickFrames(); canvasGraphics.setColor(Color.WHITE); canvasGraphics.setAntiAlias(true); canvasGraphics.drawString(("FPS:" + curFPS).intern(), fpsX, fpsY); canvasGraphics.setAntiAlias(false); } try { canvas = surfaceHolder.lockCanvas(rect); synchronized (surfaceHolder) { canvas.drawBitmap(screen.getBitmap(), 0, 0, null); } } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } if (isFocusable()) { continue; } try { Thread.sleep(30); } catch (InterruptedException e) { } LASystem.gc(10000, 1); } while (running); destroy(); } private void tickFrames() { frameCount++; calcInterval += offsetTime; if (calcInterval >= MAX_INTERVAL) { long timeNow = System.currentTimeMillis(); long realElapsedTime = timeNow - startTime; curFPS = (long) ((frameCount / realElapsedTime) * MAX_INTERVAL); frameCount = 0L; calcInterval = 0L; startTime = timeNow; } } } public Thread getMainLoop() { return mainLoop; } public void mainLoop() { this.startPaint(); } public void mainStop() { this.endPaint(); } public void startPaint() { this.start = true; } public void endPaint() { this.start = false; } public void setFPS(long frames) { this.maxFrames = frames; this.offsetTime = (long) (1.0 / maxFrames * MAX_INTERVAL); } public long getMaxFPS() { return this.maxFrames; } public long getCurrentFPS() { return this.curFPS; } public void setShowFPS(boolean isFPS) { this.isFPS = isFPS; } public boolean isRunning() { return running; } public void setRunning(boolean running) { this.running = running; } public LAHandler getLHandler() { return handler; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { holder.setFixedSize(width, height); } public void surfaceCreated(SurfaceHolder holder) { mainLoop.start(); } public void surfaceDestroyed(SurfaceHolder holder) { boolean result = true; setRunning(false); while (result) { try { mainLoop.join(); result = false; } catch (InterruptedException e) { } } mainLoop = null; } }

LAScreen.java(对应LGame-Simple中的Screen)

package org.loon.framework.android.game; import system.images.LAGraphicsUtils; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; /** * * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn * @version 0.1.0 */ public abstract class LAScreen implements ILAScreen { private ILAHandler handler; private int width, height; public LAScreen() { this.handler = LASystem.getSystemHandler(); this.width = handler.getWidth(); this.height = handler.getHeight(); } public LAImage getLAImage(String fileName){ return LAGraphicsUtils.loadLAImage(fileName); } public synchronized void createUI(LAGraphics g) { draw(g); } public abstract void draw(LAGraphics g); public synchronized void runTimer(LTimerContext timer) { alter(timer); } public abstract void alter(LTimerContext timer); public void setScreen(ILAScreen screen) { this.handler.setScreen(screen); } public int getWidth() { return width; } public int getHeight() { return height; } public abstract boolean onKey(int keyCode, KeyEvent e); public boolean onKey(View v, int keyCode, KeyEvent event) { return onKey(keyCode, event); } public abstract void onFocusChange(boolean hasFocus); public void onFocusChange(View v, boolean hasFocus) { onFocusChange(hasFocus); } public abstract boolean onLongClick(); public boolean onLongClick(View v) { return onLongClick(); } public abstract boolean onClick(); public void onClick(View v) { onClick(); } public abstract void onCreateContextMenu(ContextMenu menu, ContextMenuInfo menuInfo); public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { onCreateContextMenu(menu, menuInfo); } public abstract boolean onTouch(MotionEvent e); public boolean onTouch(View v, MotionEvent event) { return onTouch(event); } }

Main.java(启动类)

package org.loon.framework.android.game; import android.app.Activity; import android.os.Bundle; /** * * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn * @version 0.1.0 */ public class Main extends Activity { private LAGameView view; public void onCreate(Bundle icicle) { super.onCreate(icicle); view = new LAGameView(this); view.setScreen(new ScreenTest()); view.setShowFPS(true); view.mainLoop(); } protected void onPause() { if (view != null) { view.setRunning(false); } super.onPause(); } protected void onStop() { if (view != null) { view.setRunning(false); } super.onStop(); } }

ScreenTest.java(测试类)

package org.loon.framework.android.game; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ContextMenu.ContextMenuInfo; /** * * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn * @version 0.1.0 */ public class ScreenTest extends LAScreen { private LAImage image; private int tx = 0, ty = 0; public ScreenTest() { image = getLAImage("image.png"); } public void alter(LTimerContext timer) { } public void draw(LAGraphics g) { g.drawImage(image, 55, 55); g.drawString("Touch X=" + ty, 70, 70); g.drawString("Touch Y=" + tx, 70, 90); } public boolean onClick() { return false; } public void onCreateContextMenu(ContextMenu menu, ContextMenuInfo menuInfo) { } public void onFocusChange(boolean hasFocus) { } /** * 手机按键(对应键盘) */ public boolean onKey(int keyCode, KeyEvent e) { return false; } public boolean onLongClick() { return false; } /** * 手机触摸屏(对应鼠标) */ public boolean onTouch(MotionEvent e) { this.tx = (int) e.getX(); this.ty = (int) e.getY(); return false; } }

横屏效果:

零打碎敲学Android(一)—用什么来替代Graphics_第1张图片

纵屏效果:


源码下载地址:http://code.google.com/p/loon-simple/downloads/list

可以看到,此示例虽为LGame-Simple的简写,但最基础的功能已经实现,我们仅需设定一个继承自LAScreen的游戏屏幕,就可以同LGame-Simple中一样,分模块开发游戏,并且随意更换当前游戏模块及更替按键设定了。

实际上如果您愿意,即使偶不出Android版的LGame-Simple,将其自行移植到Android上的难度也绝对比您所想象的要小得多(当然,Android版偶是肯定会出的^.^)

————————————天下大势,分久必合,合久必分—————————————

事实上Android与标准Java应用近乎一致,唯独在多线程方面有些“恶心”,比如View中的invalidate()函数只能在Handler中做类似递归的handleMessage与sleep相互调用,Thread.currentThread().getContextClassLoader()也无法获得jar内部资源,感觉上就像Google要强制你学习它那套samples应用,而不能更改一样。

把LGame-Simple移植到Android上比我想象的要简单,刚刚又试验了一下,除了有关图形接口与线程的部分,其余直接copy过来就可以用,甚至连代码都不必改,所以12月出LGame-Simple的Android版肯定没问题。(事实上,如果偶现在开始什么都不干只写这个的话,大约5天左右就可以搞定……可惜不现实……)

另外偶可能尝试开发一个函数移植器,看看能不能做到PC与Android上的LGame-Simple游戏间自动代码转换,目前看来可行性蛮高的。

更多相关文章

  1. 线程中的消息机制
  2. Android验证码倒计时实现方式总结
  3. Android原生方式实现夜间模式

随机推荐

  1. android studio 更新 Gradle错误解决方法
  2. Android(安卓)网络请求的方式
  3. android 简单快速 倒计时动画
  4. 第15章 Android启动过程 [Android应用开
  5. 如果让我重新设计一款Android(安卓)App
  6. 在Android中自定义IOS风格的按钮
  7. Android中Local Service最本质的作用是什
  8. Android系统调试wifi总结
  9. android 出现gen already exists but is
  10. 设置activity为Dialog类型的设置