Привет всем, это довольно сложная проблема для меня, которую я пытался выяснить уже некоторое время.Даже объяснить это немного сложно для меня, но я попробую.
Я использую потрошенную версию примера Snake Android.Практически я использую дословно класс TileView и пытаюсь отобразить только несколько плиток на экране.Вместо класса SnakeView я использую класс GameView, в который я включил только тот код, который мне показался необходимым для отображения плитки на экране.Вот класс
public class GameView extends TileView {
/**
* Labels for the drawables that will be loaded into the TileView class
*/
private static final int RED_STAR = 1;
private static final int YELLOW_STAR = 2;
private static final int GREEN_STAR = 3;
/**
* mMoveDelay: number of milliseconds between animations.
*/
private long mMoveDelay = 600;
/**
* mLastMove: tracks the absolute time when the last animation happened.
*/
private long mLastMove;
/**
* Create a simple handler that we can use to cause animation to happen. We
* set ourselves as a target and we can use the sleep()
* function to cause an update/invalidate to occur at a later date.
*/
private RefreshHandler mRedrawHandler = new RefreshHandler();
class RefreshHandler extends Handler {
@Override
public void handleMessage(Message msg) {
GameView.this.update();
GameView.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a GameView based on inflation from XML
*
* @param context
* @param attrs
*/
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
initGameView();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGameView();
}
private void initGameView() {
setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
}
public void initNewGame() {
// set the move delay. This tells the update method how often it should
// refresh the screen.
mMoveDelay = 600;
update();
}
public void update() {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
// clear any tiles on the screen
clearTiles();
updateWalls();
// draws the tiles storred in mCellularArray
//updateCellularArray();
mLastMove = now;
}
mRedrawHandler.sleep(mMoveDelay);
}
private void updateWalls() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, x, 0);
setTile(GREEN_STAR, x, mYTileCount - 1);
}
for (int y = 1; y < mYTileCount - 1; y++) {
setTile(GREEN_STAR, 0, y);
setTile(GREEN_STAR, mXTileCount - 1, y);
}
}
private void updateCellularArray() {
setTile(YELLOW_STAR, 6, 7);
}
}
Теперь происходит следующее: когда вызывается метод updateWalls (), плитки помещаются в представление и отображаются следующим образом:
Теперь, когда я раскомментирую метод updateCellularArray()
и закомментирую updateWalls()
, программа закрывается и выдает исключение NullPointerException.После некоторой отладки я выяснил, что при вызове метода массива updateCellular массив mTileGrid в классе TileView не инициализируется, но это происходит, когда вызывается updateWalls()
.Я полностью сбит с толку относительно того, почему это происходит.Неважно, если я заменю цикл for простым setTile(GREEN_STAR, 3, 3);
, он все равно принудительно закрывается.
Вот класс TileView, который я использую (опять же, это тот же самый в примере Snake, который поставляется сAndroid SDK):
/**
* TileView: a View-variant designed for handling arrays of "icons" or other
* drawables.
*
*/
public class TileView extends View {
/**
* Parameters controlling the size of the tiles and their range within view.
* Width/Height are in pixels, and Drawables will be scaled to fit to these
* dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
*/
protected static int mTileSize;
protected static int mXTileCount;
protected static int mYTileCount;
private static int mXOffset;
private static int mYOffset;
/**
* A hash that maps integer handles specified by the subclasser to the
* drawable that will be used for that reference
*/
private Bitmap[] mTileArray;
/**
* A two-dimensional array of integers in which the number represents the
* index of the tile that should be drawn at that locations
*/
private int[][] mTileGrid;
private final Paint mPaint = new Paint();
public TileView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
public TileView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
/**
* Rests the internal array of Bitmaps used for drawing tiles, and
* sets the maximum index of tiles to be inserted
*
* @param tilecount
*/
public void resetTiles(int tilecount) {
mTileArray = new Bitmap[tilecount];
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mXTileCount = (int) Math.floor(w / mTileSize);
mYTileCount = (int) Math.floor(h / mTileSize);
mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
Log.d("Tomek", "TileGrid array dimensions are: " + mXTileCount + "," + mYTileCount);
mTileGrid = new int[mXTileCount][mYTileCount];
clearTiles();
}
/**
* Function to set the specified Drawable as the tile for a particular
* integer key.
*
* @param key
* @param tile
*/
public void loadTile(int key, Drawable tile) {
Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
tile.setBounds(0, 0, mTileSize, mTileSize);
tile.draw(canvas);
mTileArray[key] = bitmap;
}
/**
* Resets all tiles to 0 (empty)
*
*/
public void clearTiles() {
for (int x = 0; x < mXTileCount; x++) {
for (int y = 0; y < mYTileCount; y++) {
setTile(0, x, y);
}
}
}
/**
* Used to indicate that a particular tile (set with loadTile and referenced
* by an integer) should be drawn at the given x/y coordinates during the
* next invalidate/draw cycle.
*
* @param tileindex
* @param x
* @param y
*/
public void setTile(int tileindex, int x, int y) {
//Log.d("Tomek", "Attempting to set tile: " + x + "," + y);
//Log.d("Tomek", "The current value at " + x + "," + y + " is " + mTileGrid[x][y]);
//Log.d("Tomek", "It will be changed to " + tileindex);
mTileGrid[x][y] = tileindex;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int x = 0; x < mXTileCount; x += 1) {
for (int y = 0; y < mYTileCount; y += 1) {
if (mTileGrid[x][y] > 0) {
canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
mXOffset + x * mTileSize,
mYOffset + y * mTileSize,
mPaint);
}
}
}
}
}
Извините за размещение всего этого кода.Любая помощь будет принята с благодарностью.
Большое спасибо,
Tomek
РЕДАКТИРОВАТЬ: упрощенный класс GameView
РЕДАКТИРОВАТЬ 2: Хорошо после изменения updateCellularArray()
метод к следующему:
private void updateCellularArray() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, 12, 12);
}
}
Наконец, он поместил плитку туда, где я хотел ... альтернативный текст http://img188.imageshack.us/img188/1148/device2z.png
Я начинаю задумываться, есть ли у нее что-то делатьс RefreshHandler.Возможно, когда метод сна RedrawHandler вызывается в методе обновления класса GameView.Я не совсем уверен, как это работает, поэтому я попытаюсь поиграть с этим и посмотреть, что я могу придумать.