Search code examples
androidandroid-studiocanvasdrawandroid-custom-view

How do you draw a custom view(e.g. Circle) on a View?


For my Pong clone, I am trying to draw a custom View from class Ball onto my View in class GamePanel. However it's nowhere to be seen on the screen at runtime.

public class Ball extends View {

private float posX;
private float posY;
private float radius;

private Paint paint;

public Ball(Context context, float posX, float posY, float radius){
    super(context);
    this.posX = posX;
    this.posY = posY;
    this.radius = radius;
    setWillNotDraw(false);
    init();

}

private void init(){

    paint = new Paint();
    paint.setColor(Color.WHITE);
}

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawCircle(posX, posY, radius, paint);

}

Now this is my class GamePanel.

public class GamePanel extends View implements GestureDetector.OnGestureListener{

private Context context;

//Screen height and width in pixels
private static int width;
private static int height;

private MainThread thread;

private Rect paddle1;
private Rect paddle2;

private Ball ball;

private Paint paint;

//Starting position paddle1
private int xPos1;
private int yPos1;

// Starting position paddle2
private int xPos2;
private int yPos2;

//Width and height of paddles
private int pWidth;
private int pHeight;

private GestureDetectorCompat gestureDetector;

public GamePanel(Context context) {

    super(context);

    this.context = context;

    //Information we will need for positioning our objects inside the panel
    DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();

    width = displayMetrics.widthPixels;
    height = displayMetrics.heightPixels;

    //Make the GamePanel focusable
    setFocusable(true);

    // Thread needs information about the game panel
    thread = new MainThread(this);

    pWidth = 100;
    pHeight = height/2;

    xPos1 = width/15;
    yPos1 = 0;

    xPos2 = width - xPos1 - pWidth;
    yPos1 = 0;

    paddle1 = new Rect(xPos1, yPos1, xPos1 + pWidth, yPos1 + pHeight);
    paddle2 = new Rect(xPos2, yPos2, xPos2 + pWidth, yPos2 + pHeight);

    paint = new Paint();
    paint.setColor(Color.WHITE);

    ball = new Ball(context, 300, 300, 300);

    setBackgroundColor(Color.BLACK);

    this.gestureDetector = new GestureDetectorCompat(context, this);

    thread.setRunning(true);
    thread.start();
}


@Override
protected void onDraw(Canvas canvas) {

    canvas.drawRect(paddle1, paint);
    canvas.drawRect(paddle2, paint);

}

I tried calling invalidate() and postInvalidate() inside my MainThread thread, in the constructor of GamePanel and in the constructor of Ball. The ball is never drawn onto the screen.

As you can see I already faced this problem with the paddles in Pong therefore I created two Rectangles inside GamePanel which is very ugly.

How do I encapsulate the rectangle and the circle (ball) so that it can still be drawn on a Canvas?


Solution

  • You are creating the Ball(circle) view inside GamePanel view. Just because you are creating a view that doesn't means it will be drawn on screen. You need to attach your view to the layout. The simple solution will be to extend your GamePanel to Ball

    public class GamePanel extends Ball {
    
    }
    

    then in onDraw() function of the GamePanel add super.onDraw();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(); //Will draw the circle before drawing rectangle
        canvas.drawRect(paddle1, paint);
        canvas.drawRect(paddle2, paint);    
    }
    

    if you place the super.onDraw() below the drawRect then the circle will be drawn after drawing the rect.

    You also need to understand the basic concept of adding the view to layout.