Alex Stonard Alex Stonard - 4 months ago 11
Android Question

Custom Drawable view is not displaying on Android app

I am currently building an Android app which produces a maze based upon a difficulty setting (easy produces a maze of 5x5 with the borders, medium 8x8, hard 11x11). The maze is made up of black squares in positions that constitutes a path from the top-left to the bottom-right among others in other directions as well as a one-square border around the maze. If there is a black square, you cannot move in that direction, etc... At the moment, only the interface is being built, not any user interaction (I will add that later). The code is also a little messy - I am aware of this and will sort it out after the problem has been fixed.

The issue with this is the maze is not displayed - just a white blank space! I am using a XML file and activity for the main interface, and a custom view in the form of a drawable for the maze.

This is the activity/class that creates the interface in the game. The

Level
class that is used here holds data about the current score/maze/user position etc. It has been tested and I am very confident that it works.

public class InGameInterface extends Activity {

Level level;;
MazeView maze;

private boolean[][] currentMaze = null;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.in_game_interface);

level = new Level();

currentMaze = level.getNewMaze();
loadMazeDisplay();
}

private void loadMazeDisplay()
{
maze = new MazeView(this, level, currentMaze);

RelativeLayout mazeHolder = (RelativeLayout) findViewById(R.id.maze);
LayoutParams lP = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

mazeHolder.addView(maze, lP);
}
}


Below is the relevant part of the XML file that I am trying to insert the custom view in. The
<
are missing because Stack Overflow does not like them here.

RelativeLayout
android:id="@+id/maze"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="7dp"
android:layout_marginRight="15dp"
android:layout_weight="2" >
RelativeLayout


Below is the MazeView that constructs and should display the maze.

public class MazeView extends View
{
private Activity context;
private Level level;
private boolean mazeIsPath[][] = null;

private Integer widthInPixels = 0, heightInPixels = 0;
private Integer cellWidthInPixels = 0, cellHeightInPixels = 0;
private Paint cell, ball, background, target;

public MazeView(Context context, Level level, boolean[][] maze)
{
super(context);
this.context = (Activity) context;
this.level = level;
mazeIsPath = maze;

cell = new Paint();
cell.setColor(getResources().getColor(R.color.textColor));

ball = new Paint();
ball.setColor(getResources().getColor(R.color.splashBackground));

background = new Paint();
background.setColor(getResources().getColor(R.color.gameInterfaceBackground));

target = new Paint();
target.setColor(getResources().getColor(R.color.buttonBackground));

widthInPixels = this.getWidth();
heightInPixels = this.getHeight();

setFocusable(true);
this.setFocusableInTouchMode(true);
}

protected void onSizeChanged(Integer newW, Integer newH, Integer oldW, Integer oldH)
{
widthInPixels = newW;
heightInPixels = newH;

super.onSizeChanged(newW, newH, oldW, oldH);
}

protected void onDraw(Canvas canvas)
{
Integer counterX = 0, counterY = 0;

canvas.drawRect(0, 0, widthInPixels, heightInPixels, background);

for(counterX = 0; counterX < level.getWidth(); counterX++)
{
for(counterY = 0; counterY < level.getHeight(); counterY++)
{
if(mazeIsPath[counterX][counterY] != true)
{
canvas.drawRect((float)(cellWidthInPixels * counterX), (float)(cellHeightInPixels * counterX), (float)(cellWidthInPixels * (counterX + 1)), (float)(cellHeightInPixels * (counterY + 1)), cell);
}
}
}

canvas.drawCircle( (float)((cellWidthInPixels * level.getCurrentX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getCurrentX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (2f/3f)), ball);
canvas.drawCircle( (float)((cellWidthInPixels * level.getFinalX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getFinalX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (4f/5f)), target);
}
}


I am wondering if someone can point out where I have gone wrong and how to fix it!




First amendment to onDraw():

protected void onDraw(Canvas canvas)
{
Integer counterX = 0, counterY = 0;
widthInPixels = new Integer(canvas.getWidth());
heightInPixels = new Integer(canvas.getHeight());

canvas.drawRect(0, 0, widthInPixels, heightInPixels, background);

for(counterX = 0; counterX < level.getWidth(); counterX++)
{
for(counterY = 0; counterY < level.getHeight(); counterY++)
{
if(mazeIsPath[counterX][counterY] != true)
{
canvas.drawRect((float)(cellWidthInPixels * counterX), (float)(cellHeightInPixels * counterX), (float)(cellWidthInPixels * (counterX + 1)), (float)(cellHeightInPixels * (counterY + 1)), cell);
}
}
}

canvas.drawCircle( (float)((cellWidthInPixels * level.getCurrentX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getCurrentX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (2f/3f)), ball);
canvas.drawCircle( (float)((cellWidthInPixels * level.getFinalX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getFinalX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (4f/5f)), target);

Log.w("onDraw width", widthInPixels.toString());
Log.w("onDraw height", heightInPixels.toString());
Integer cWidth = new Integer(canvas.getWidth());
Integer cHeight = new Integer(canvas.getHeight());
Log.w("canvas width", cWidth.toString());
Log.w("cavas height", cHeight.toString());
}

Answer Source

i'd say, forget about widthInPixels & heightInPixels as they depend on the layout. To use them you'd have to implement onMeasure (that may be insteresting). However as your view is pretty simple, just use the values from canvas.getWidth() and getHeight(). Try the following to see if it works

private static final int CELLS_PER_SCREEN = 5; // the number of cells that fit on a view

protected void onDraw(Canvas canvas) {

    int totalWidth=canvas.getWidth(), 
        totalHeight=canvas.getHeight(), 
        cellWidthInPixels=totalWidth / CELLS_PER_SCREEN,
        cellHeightInPixels=totalHeight / CELLS_PER_SCREEN;

    canvas.drawRect(0, 0, totalWidth, totalHeight, background);

    for (int counterX = 0, xlen=level.getWidth(); counterX < xlen; counterX++) {
        for (int counterY = 0, ylen=level.getHeight(); counterY < ylen; counterY++) {
            if (!mazeIsPath[counterX][counterY]) {
                canvas.drawRect(
                     (float)(cellWidthInPixels * counterX), 
                     (float)(cellHeightInPixels * counterX), 
                     (float)(cellWidthInPixels * (counterX + 1)), 
                     (float)(cellHeightInPixels * (counterY + 1)), cell
                );
            }
        }
    }

    canvas.drawCircle( (float)((cellWidthInPixels * level.getCurrentX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getCurrentX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (2f/3f)), ball);
    canvas.drawCircle( (float)((cellWidthInPixels * level.getFinalX()) + (0.5f*(cellWidthInPixels))), (float)((cellHeightInPixels * level.getFinalX()) + (0.5f*(cellHeightInPixels))), (float)(cellHeightInPixels * (4f/5f)), target);

    Log.w("onDraw width", widthInPixels.toString());
    Log.w("onDraw height", heightInPixels.toString());
    Integer cWidth = new Integer(canvas.getWidth());
    Integer cHeight = new Integer(canvas.getHeight());
    Log.w("canvas width", cWidth.toString());
    Log.w("cavas height", cHeight.toString());
}

Also some advices for onDraw: OnDraw is called very frequently and it's important to be as optimized as possible.

  • use int rather than Integers, Integer is an Object while int is just a fast primitive type.
  • Pre-calculate stuff prone to be re-usable, specially float multiplications. Your drawCircles() do a lot of math that only change with cellWidthInPixels ... So calculate them once, everytime cellWidthInPixels change