1

This is what i currently have in my MainActivity.java.

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    MyBall ball = new MyBall(this);
    setContentView(ball);
    }
 }

and this is my canvas class

public class MyBall extends View {
    Bitmap myBall;
    int gap = 10;
    String direction = "down";
    float xPos = 0;
    float yPos = 0;

public MyBall(Context context) {
    super(context);
    myBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball3);

 }
protected void onDraw(Canvas canvas){
    super.onDraw(canvas);
    canvas.drawColor(Color.WHITE);


    canvas.drawBitmap(myBall, xPos, yPos, null);
    invalidate();
 }
}

I want the move from the upper left, to the middle right, middle left, to the lower right and then cycle back. I am also having problems when i change the orientation to landscape because the width of the canvas changes and everything is ruined after that.

[EDIT]

 protected void onDraw(Canvas canvas){
    super.onDraw(canvas);
    canvas.drawColor(Color.WHITE);
    if(direction.equals("down"))
    {
            if(yPos < canvas.getHeight()/2 - myBall.getWidth()){
                xPos+=gap;
                yPos+=gap;
            }
        else {
                direction = "up";
                Log.d("direction", xPos + "help");
            }

    }
    if(direction.equals("up")){
        if (xPos >  myBall.getWidth()) {
            xPos-=gap;
            yPos+=gap;
        }

    }

    canvas.drawBitmap(myBall, xPos, yPos, null);
    invalidate();
}

This is where i am stuck. The ball goes to the middle left, but when the orientation and changed to landscape the ball goes down and doesn't go up.

Ralph Macalino
  • 121
  • 1
  • 4
  • 13
  • Don't call invalidate in `onDraw()`. Use some other source for your animation frames. – tynn May 08 '16 at 10:20
  • Here is some useful info : http://developer.android.com/training/custom-views/custom-drawing.html – Saeed Sharman May 08 '16 at 10:34
  • @tynn what exactly do you mean by that? I am new to this so i use the invalidate so that the ondraw method will be called again so the image will be updated. – Ralph Macalino May 08 '16 at 11:08
  • That's what I mean. Don't do it in `onDraw()`. Use a timer or something similar for the updates. Or maybe have a look at [View Animation](https://developer.android.com/guide/topics/graphics/view-animation.html) – tynn May 08 '16 at 11:22

2 Answers2

3

The Ball Class

To get this to work without going crazy and getting your code all disgusting and messy, you'll need to define a good class for your Ball, this also adds for flexibility if you ever want to add extra balls to the canvas or whatever. As you can see we have all the pertinent information for the instance of the ball, including the direction and the color, then all we need to do from our custom View is tell the ball to move, and draw the oval it contains with the paint.

public class Ball{
    public int[] direction = new int[]{1,1}; //direction modifier (-1,1)
    public int x,y,size;
    public int speed = 10;
    public Paint paint;
    public RectF oval;

    public Ball(int x, int y, int size, int color){
        this.x = x;
        this.y = y;
        this.size = size;
        this.paint = new Paint();
        this.paint.setColor(color);
    }

    public void move(Canvas canvas) {
        this.x += speed*direction[0];
        this.y += speed*direction[1];
        this.oval = new RectF(x-size/2,y-size/2,x+size/2,y+size/2);

        //Do we need to bounce next time?
        Rect bounds = new Rect();
        this.oval.roundOut(bounds); ///store our int bounds

        //This is what you're looking for ▼
        if(!canvas.getClipBounds().contains(bounds)){
            if(this.x-size<0 || this.x+size > canvas.getWidth()){
                direction[0] = direction[0]*-1;
            }
            if(this.y-size<0 || this.y+size > canvas.getHeight()){
                direction[1] = direction[1]*-1;
            }
        }
    }
}

How are we calculating the direction?

You might be confused as to how we calculate when we collide with the bounds. There are several approaches to this but in our case we are checking if the rectangle bounding our ball is still contained inside the canvas. If not it means that we've somehow escaped the bounds of the canvas and we check which direction needs to be modified, if we've passed the limits of X-axis or are below zero on it, we multiply the direction with -1, changing the modifier. Same applies for Y-axis

The Custom View

The previous class can be contained in a separate file, or inside the custom View class, that's up to you and your way of coding, but as for the Custom View, we just need to handle the drawing:

public class BouncingBallInside extends View {
    private List<Ball> balls = new ArrayList<>();

    public BouncingBallInside(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public BouncingBallInside(Context context) {
        super(context);
        init();
    }
    private void init(){
        //Add a new ball to the view
        balls.add(new Ball(50,50,100,Color.RED));
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //Draw the balls
        for(Ball ball : balls){
            //Move first
            ball.move(canvas);
            //Draw them
            canvas.drawOval(ball.oval,ball.paint);
        }
        invalidate(); // See note
    }
}

Note

You really should animate this using a Handler and a set interval. But for a simple case, this should suffice.

Juan Cortés
  • 20,634
  • 8
  • 68
  • 91
  • Okay well, this is a bit too much for me and i think i have to read your code for a while to understand it. But i tried it and it worked so this pretty much solved my problem. Thanks for this man! – Ralph Macalino May 08 '16 at 11:59
  • read it, test it, and you'll understand it eventually. Good luck! – Juan Cortés May 08 '16 at 12:01
1

in addition to Jaun Cortes added some randomness in (speed , size ,x and y coordinates ) by introducing the Arrays

public class BouncingBallInside extends View {
    private Ball[] balls = new Ball[10];

/*even new Ball can be added as variable e.g. ballcount passed as new Ball[ballcount] */

    public BouncingBallInside(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public BouncingBallInside(Context context) {
        super(context);
        init();
    }
    private void init(){
        //Add a new ball to the view
        for (int i =0; i <10 ; i++) {
        balls[i] = (new Ball(i*2,i*5,i*3,i*3 , Color.RED));
//        balls.add(new Ball(i*4,i*3,i*2, i*3,Color.GREEN));
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //Draw the balls

        for (int i =0; i <10 ; i++) {
            balls[i].move(canvas);
            //Draw them
            canvas.drawOval(balls[i].oval,balls[i].paint);
        }
        invalidate(); // See note
    }
}

and follows is ball class

    public Ball(int x, int y, int s,int size, int color){
        this.x = x;
        this.y = y;
        this.size = size;
        this.paint = new Paint();
        this.paint.setColor(color);
        this.speed =s;
    }

    public void move(Canvas canvas) {
        this.x += speed*direction[0];
        this.y += speed*direction[1];
        this.oval = new RectF(x-size/2,y-size/2,x+size/2,y+size/2);

        //Do we need to bounce next time?
        Rect bounds = new Rect();
        this.oval.roundOut(bounds); ///store our int bounds

        //This is what you're looking for ▼
        if(!canvas.getClipBounds().contains(bounds)){
            if(this.x-size<0 || this.x+size > canvas.getWidth()){
                direction[0] = direction[0]*-1;
            }
            if(this.y-size<0 || this.y+size > canvas.getHeight()){
                direction[1] = direction[1]*-1;
            }
        }
    }
}

and Main activity class as follows:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final BouncingBallInside bouncingballi = new BouncingBallInside(this);
        setContentView(bouncingballi);

    }

}