这几天开着代理研究android(主要是长期以来 developer.android.com 被墙撞……),总算初步掌握了android的图形处理方式,准备在Blog上写一些经验性的总结。
一、Android Virtual Device仿真界面对应快捷
view plaincopy to clipboardprint?
模拟项 设备键 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(/)
模拟项 设备键 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,如下所示:
view plaincopy to clipboardprint?
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);
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本地接口如下所示:
view plaincopy to clipboardprint?
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);
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依旧大量依赖于本地接口,具体函数如下:
view plaincopy to clipboardprint?
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);
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桌面应用的差异实际上是极为细微的。
源码下载地址:
PS:此用例中所有[LA]开头Java文件为Android下专用,否则皆为LGame-Simple通用。
LAGameView.java(对应LGame-Simple中的GameView)
view plaincopy to clipboardprint?
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
*
*
*
* 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
ceponline@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;
}
}
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
*
*
*
* 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
ceponline@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)
view plaincopy to clipboardprint?
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
*
*
*
* 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
ceponline@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);
}
}
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
*
*
*
* 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
ceponline@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(启动类)
view plaincopy to clipboardprint?
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
*
*
*
* 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
ceponline@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();
}
}
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
*
*
*
* 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
ceponline@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(测试类)
view plaincopy to clipboardprint?
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
*
*
*
* 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
ceponline@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;
}
}
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
*
*
*
* 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
ceponline@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;
}
}
横屏效果:
纵屏效果:
源码下载地址:
可以看到,此示例虽为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游戏间自动代码转换,目前看来可行性蛮高的。