Port J2ME app to Android

想把J2ME移植到Android平台上主要需要注意以下几个Android的新类,类的名字和J2ME下有点容易混淆。

1. Activity 相当于 J2ME 中的MIDlet

eg. public class Game extends Activity 相当于J2ME下面的 public class Game extends MIDlet  

2. SurfaceView 相当于J2ME中的Canvas/GameCanvas

3. Canvas 相当于J2ME中的Graphics,所以在Android中,要在SurfaceView上画图,需要先取得Canvas的对象,这就好比在J2ME中先取得Graphics g然后把参数传给具体的画图函数画到Canvas上面

 

以下是Android的资源架构

DirectoryResource Types
res/anim/ XML files that are compiled into frame by frame animation or tweened animation objects
res/drawable/

.png, .9.png, .jpg files that are compiled into the following Drawable resource subtypes:

To get a resource of this type, use mContext.getResources().getDrawable(R.drawable.imageId )

Note: Image resources placed in here may be automatically optimized with lossless image compression by the aapt tool. For example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette. This will result in an image of equal quality but which requires less memory. So be aware that the image binaries placed in this directory can change during the build. If you plan on reading an image as a bit stream in order to convert it to a bitmap, put your images in the res/raw/ folder instead, where they will not be optimized.

res/layout/ XML files that are compiled into screen layouts (or part of a screen). See Declaring Layout .
res/values/

XML files that can be compiled into many kinds of resource.

Note: Unlike the other res/ folders, this one can hold any number of files that hold descriptions of resources to create rather than the resources themselves. The XML element types control where these resources are placed under the R class.

While the files can be named anything, these are the typical files in this folder (the convention is to name the file after the type of elements defined within):

  • arrays.xml to define arrays
  • colors.xml to define color drawables and color string values . Use Resources.getDrawable() and Resources.getColor(), respectively, to get these resources.
  • dimens.xml to define dimension value . Use Resources.getDimension() to get these resources.
  • strings.xml to define string values (use either Resources.getString or preferably Resources.getText() to get these resources. getText() will retain any rich text styling which is usually desirable for UI strings.
  • styles.xml to define style objects.
res/xml/ Arbitrary XML files that are compiled and can be read at run time by calling Resources.getXML() .
res/raw/ Arbitrary files to copy directly to the device. They are added uncompiled to the compressed file that your application build produces. To use these resources in your application, call Resources.openRawResource() with the resource ID, which is R.raw.somefilename .

Resources are compiled into the final APK file. Android creates a wrapper class, called R, that you can use to refer to these resources in your code. R contains subclasses named according to the path and file name of the source file

Note: Image resources placed in res/drawable/ may be automatically optimized with lossless image compression by the aapt tool. For example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette. This will result in an image of equal quality but which requires less memory. So be aware that the image binaries placed in this directory can change during the build. If you plan on reading an image as a bit stream in order to convert it to a bitmap, put your images in the res/raw/ folder instead, where they will not be optimized.

 

 

 

--------------转帖--------------------

To become familiar with mobile programming plattforms I've implemented a little game (a minesweeper clone) in J2ME. After having finished this I ported this game to Android.



While porting to Android I explored the following features:

  • Rendering images via View.onDraw()
  • Working with multiple Activities (Main Activity, Options Activity)
  • Passing data from one Activity to another
  • Handling Activity states (freeze, restore)
  • Handling custom menues (onCreateOptionsMenu)
  • etc.

It turned out that - as expected ;-) - separating program logic is a good thing. I used the same classes for the game logic.

Disclaimer: Please note, that my intention was to explore some features of the plattforms. There is still room for improvement as I've not implemented a timer, a highscore table etc..
Attached you can find the Eclipse (3.4) projects of both implementations:

The following sources are extracts from the Android implementation:

TrapMain.java

package at.lacherstorfer.trap.android;



import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;



public class TrapMain extends Activity {



private TrapView trapView;

private static int EDIT_OPTIONS = 1;



/** Called when the activity is first created. */

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);



setContentView(R.layout.trap_main_layout);

trapView = (TrapView) findViewById(R.id.trap);



if (icicle != null) {

// We are being restored

Bundle map = icicle.getBundle("trapView");

if (map != null) {

trapView.restoreState(map);

}

}



trapView.doStart();



}



@Override

protected void onSaveInstanceState(Bundle outState) {

// store game state

outState.putBundle("trapView", trapView.saveState());

}



@Override

public boolean onCreateOptionsMenu(Menu menu) {

super.onCreateOptionsMenu(menu);

MenuItem menuItem = menu.add(0, 0, 0, "Start");

menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {



public boolean onMenuItemClick(MenuItem item) {

trapView.doStart();

return true;

}



});



menuItem = menu.add(0, 0, 0, "Options");

menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {



public boolean onMenuItemClick(MenuItem item) {

Intent optionsIntent = new Intent(TrapMain.this, TrapOptions.class);

Bundle extras = new Bundle();

extras.putInt("numberRows", trapView.getNumberRows());

extras.putInt("numberCols", trapView.getNumberCols());

extras.putInt("numberTraps", trapView.getNumberTraps());

optionsIntent.putExtras(extras);

startActivityForResult(optionsIntent, EDIT_OPTIONS);

return true;

}

});

return true;

}



@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode == RESULT_OK) {

trapView.doStart(data.getIntExtra("numberRows", 8),

data.getIntExtra("numberCols", 8), data.getIntExtra("numberTraps", 8));

}

}



}



TrapView.java

package at.lacherstorfer.trap.android;



import android.app.AlertDialog;

import android.app.AlertDialog.Builder;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.drawable.Drawable;

import android.os.Bundle;

import android.util.AttributeSet;

import android.util.Log;

import android.view.KeyEvent;

import android.view.MotionEvent;

import android.view.View;

import at.lacherstorfer.trap.shared.Cell;

import at.lacherstorfer.trap.shared.Field;



public class TrapView extends View {



private Field field;



private int numberRows = 8;

private int numberCols = 8;

private int numberTraps = 8;



private int cursorX;

private int cursorY;



private Drawable cellClosedImage;

private Drawable cell0Image;

private Drawable cell1Image;

private Drawable cell2Image;

private Drawable cell3Image;

private Drawable cell4Image;

private Drawable cellBombImage;

private Drawable cellExplodedImage;

private Drawable cellFlaggedImage;

private Drawable cursorImage;



public TrapView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

loadImages(context);

}



public TrapView(Context context, AttributeSet attrs) {

super(context, attrs);

loadImages(context);

}



public TrapView(Context context) {

super(context);

loadImages(context);

}



public void loadImages(Context context) {

cellClosedImage = context.getResources().getDrawable(

R.drawable.cellclosed);

cell0Image = context.getResources().getDrawable(R.drawable.cell0);

cell1Image = context.getResources().getDrawable(R.drawable.cell1);

cell2Image = context.getResources().getDrawable(R.drawable.cell2);

cell3Image = context.getResources().getDrawable(R.drawable.cell3);

cell4Image = context.getResources().getDrawable(R.drawable.cell4);

cellBombImage = context.getResources().getDrawable(R.drawable.cellbomb);

cellExplodedImage = context.getResources().getDrawable(

R.drawable.cellexploded);

cellFlaggedImage = context.getResources().getDrawable(

R.drawable.cellflagged);

cursorImage = context.getResources().getDrawable(R.drawable.cursor);

setFocusable(true);

}



public Bundle saveState() {

Bundle map = new Bundle();

map.putInt("numberRows", Integer.valueOf(numberRows));

map.putInt("numberCols", Integer.valueOf(numberCols));

map.putInt("numberTraps", Integer.valueOf(numberTraps));

map.putInt("cursorX", Integer.valueOf(cursorX));

map.putInt("cursorY", Integer.valueOf(cursorY));

int[] fieldState = field.getState();

for (int i = 0; i < fieldState.length; i++) {

map.putInt("field-" + i, fieldState[i]);

}

return map;

}



public void restoreState(Bundle icicle) {

numberRows = icicle.getInt("numberRows");

numberCols = icicle.getInt("numberCols");

numberTraps = icicle.getInt("numberTraps");

cursorX = icicle.getInt("cursorX");

cursorY = icicle.getInt("cursorY");

int[] fieldState = new int[icicle.size() - 5];

for (int i = 0; i < fieldState.length; i++) {

fieldState[i] = icicle.getInt("field-" + i);

}

field = new Field(fieldState);

}



@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

Log.i("TrapView", "pressed key=" + keyCode);

boolean handled = false;

if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {

cursorX -= cursorX > 0 ? 1 : 0;

handled = true;

} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {

cursorX += cursorX < numberCols - 1 ? 1 : 0;

handled = true;

} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {

cursorY -= cursorY > 0 ? 1 : 0;

handled = true;

} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {

cursorY += cursorY < numberRows - 1 ? 1 : 0;

handled = true;

} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER

|| keyCode == KeyEvent.KEYCODE_ENTER) {

fire();

handled = true;

} else if (keyCode == KeyEvent.KEYCODE_SPACE) {

flag();

handled = true;

}

if (handled) {

postInvalidate();

}

return handled;

}



@Override

public boolean onTouchEvent(MotionEvent event) {

boolean handled = false;

if (event.getAction() == MotionEvent.ACTION_DOWN) {

cursorX = (int) (event.getX() / 15);

cursorX = cursorX > numberCols ? numberCols - 1 : cursorX;

cursorY = (int) (event.getY() / 15);

cursorY = cursorY > numberRows ? numberRows - 1 : cursorY;

handled = true;

}

postInvalidate();

return handled;

}



private void fire() {

if (field == null)

return; // game not started yet

int cellValue = field.fireAt(cursorY, cursorX);

if (cellValue == Cell.TRAP) {

field.reveal();

Builder b = new AlertDialog.Builder(this.getContext());

b.setTitle("Trap");

b.setIcon(0);

b.setMessage("Game Over");

b.show();

} else if (field.isSolved()) {

Builder b = new AlertDialog.Builder(this.getContext());

b.setTitle("Trap");

b.setIcon(0);

b.setMessage("Congratulation, You Win!");

b.show();

}

}



private void flag() {

if (field == null)

return; // game not started yet

field.flagAt(cursorY, cursorX);

if (field.isSolved()) {

Builder b = new AlertDialog.Builder(this.getContext());

b.setTitle("Trap");

b.setIcon(0);

b.setMessage("Congratulation, You Win!");

b.show();

}

}



@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);



if (field == null)

return; // game not started yet



// draw the field

for (int row = 0; row < numberRows; row++) {

for (int col = 0; col < numberCols; col++) {

Cell cell = field.getCellAt(row, col);

Drawable cellImage = getDrawable(cell);

cellImage.setBounds(/* left */col * 15, /* top */row * 15, /* right */

col * 15 + 15, /* bottom */row * 15 + 15);

cellImage.draw(canvas);

}

}



// draw the cursor

cursorImage.setBounds(cursorX * 15, cursorY * 15, cursorX * 15 + 15,

cursorY * 15 + 15);

cursorImage.draw(canvas);

}



public void doStart(int numberRows, int numberCols, int numberTraps) {

this.numberRows = numberRows;

this.numberCols = numberCols;

this.numberTraps = numberTraps;

field = new Field(numberRows, numberCols, numberTraps);

postInvalidate();

}



public void doStart() {

if (field == null) {

field = new Field(numberRows, numberCols, numberTraps);

} else {

field.restart();

}

postInvalidate();

}



private Drawable getDrawable(Cell cell) {

if (cell.isOpen()) {

switch (cell.getValue()) {

case Cell.TRAP:

return cellBombImage;

case 0:

return cell0Image;

case 1:

return cell1Image;

case 2:

return cell2Image;

case 3:

return cell3Image;

case 4:

return cell4Image;

}

} else if (cell.isClosed()) {

return cellClosedImage;

} else if (cell.isFlagged()) {

return cellFlaggedImage;

} else if (cell.isExploded()) {

return cellExplodedImage;

} else {

throw new RuntimeException("Internal Error");

}

return null;

}



public int getNumberRows() {

return numberRows;

}



public int getNumberCols() {

return numberCols;

}



public int getNumberTraps() {

return numberTraps;

}



}



TrapOptions.java

package at.lacherstorfer.trap.android;



import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;

import android.widget.EditText;



public class TrapOptions extends Activity {



private EditText etNumberCols;

private EditText etNumberRows;

private EditText etNumberTraps;



/** Called when the activity is first created. */

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.trap_options_layout);

etNumberRows = (EditText) findViewById(R.id.number_rows);

etNumberCols = (EditText) findViewById(R.id.number_cols);

etNumberTraps = (EditText) findViewById(R.id.number_traps);

Bundle extras = getIntent().getExtras();

etNumberRows.setText(""+extras.getInt("numberRows"));

etNumberCols.setText(""+extras.getInt("numberCols"));

etNumberTraps.setText(""+extras.getInt("numberTraps"));

}



@Override

public boolean onCreateOptionsMenu(Menu menu) {

super.onCreateOptionsMenu(menu);



MenuItem menuItem = menu.add(0, 0, 0, "Ok");

menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {

public boolean onMenuItemClick(MenuItem item) {

int numberRows = Integer.parseInt(etNumberRows.getText().toString());

int numberCols = Integer.parseInt(etNumberCols.getText().toString());

int numberTraps = Integer.parseInt(etNumberTraps.getText().toString());

Bundle b = new Bundle();

b.putInt("numberRows", numberRows);

b.putInt("numberCols", numberCols);

b.putInt("numberTraps", numberTraps);

Intent resultIntent = new Intent();

resultIntent.putExtras(b);

setResult(RESULT_OK, resultIntent);

finish();

return true;

}

});



menuItem = menu.add(0, 0, 0, "Cancel");

menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {

public boolean onMenuItemClick(MenuItem item) {

setResult(RESULT_CANCELED);

finish();

return true;

}

});

return true;

}



}



trap_main_layout.xml

<?xml version="1.0" encoding="utf-8"?>



<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent">



<at.lacherstorfer.trap.android.TrapView

android:id="@+id/trap"

android:layout_width="fill_parent"

android:layout_height="fill_parent" />



</FrameLayout>



trap_options_layout.xml

<?xml version="1.0" encoding="utf-8"?>

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent">



<TableRow>

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Number Columns"/>

<EditText android:id="@+id/number_cols"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:maxLength="2" />

</TableRow>

<TableRow>

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Number Rows"/>

<EditText android:id="@+id/number_rows"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:maxLength="2" />

</TableRow>

<TableRow>

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Number Traps"/>

<EditText android:id="@+id/number_traps"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:maxLength="2"/>

</TableRow>

</TableLayout>



AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="at.lacherstorfer.trap.android">

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name="TrapMain" android:label="@string/app_name">

<intent-filter>

<action android:value="android.intent.action.MAIN"

android:name="android.intent.action.MAIN"/>

<category android:value="android.intent.category.LAUNCHER"

android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<activity android:name="TrapOptions"

android:label="@string/app_name"/>

</application>

</manifest>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值