I am new to java 2d graphics and I have problem handling mouseclick event.
Is it possible for you to tell me why there is nothing going on after updating mouse status to clicked ?
What I want to do is to change the image in array at 0 2 to another image. Nothing happens tho. Thanks for your help in advance.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.*;
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.*;
public class Board extends JPanel implements MouseListener {
private static boolean[] keyboardState = new boolean[525];
private static boolean[] mouseState = new boolean[3];
private static Image[][] images;
Image house;
int w = 0;
int h = 0;
int xPos;
int yPos;
ImageIcon ii = new ImageIcon(this.getClass().getResource("house.gif"));
ImageIcon iii = new ImageIcon(this.getClass().getResource("house1.gif"));
public Board() {
house = ii.getImage();
h = house.getHeight(null);
w = house.getWidth(null);
images = new Image[10][10];
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
images[i][j] = house;
}
}
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
g2d.drawImage(images[i][j],w*i,h*j,null);
}
}
//g2d.drawImage(house,15,15,null);
}
public void checkMouse()
{
if(mouseState[0])
{
images[0][2] = iii.getImage();
repaint();
super.repaint();
}
}
#Override
public void mousePressed(MouseEvent e)
{
mouseKeyStatus(e, true);
checkMouse();
}
#Override
public void mouseReleased(MouseEvent e)
{
mouseKeyStatus(e, false);
repaint();
}
public static boolean mouseButtonState(int button)
{
return mouseState[button - 1];
}
private void mouseKeyStatus(MouseEvent e, boolean status)
{
if(e.getButton() == MouseEvent.BUTTON1)
mouseState[0] = status;
else if(e.getButton() == MouseEvent.BUTTON2)
mouseState[1] = status;
else if(e.getButton() == MouseEvent.BUTTON3)
mouseState[2] = status;
}
You need to register a MouseListener for your Board JPanel so that mouseKeyStatus can be called
addMouseListener(this);
Aside: Override paintComponent rather than paint when implementing custom painting in Swing and remember to invoke super.paintComponent(g).
Related
I am writing a space invaders clone for a school project, I am in the process of writing the aliens and their algorithm for movement with the use of an array.
My issue is a bunch of errors occur when I run the code but I can`t find why?
any help would be appreciated and bare in mind I am very inexperienced with game development and java
bellow is my GamePanel class, and then my Alien class
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import entity.Alien;
import entity.Controller;
import entity.Player;
import entity.playerBullet;
public class GamePanel extends JPanel implements Runnable{
public final int screenHeight = 600;
public final int screenWidth = 800;
public final int playerSize = 48;
int fps = 60;
KeyHandler keyH = new KeyHandler();
Thread gameThread;
public Player player = new Player(this,keyH);
public CollisionChecker cChecker = new CollisionChecker(this);
private Controller c = new Controller(null);
Alien alien;
//player default position
int playerX = 100;
int playerY = 100;
int playerSpeed = 4;
public GamePanel() {
this.setPreferredSize(new Dimension (screenWidth, screenHeight));
this.setBackground(Color.black);
this.setDoubleBuffered(true);
this.addKeyListener(keyH);
this.setFocusable(true);
}
public void startGameThread() {
gameThread = new Thread (this);
gameThread.start();
alien.initAlien();
}
public void run() {
double drawInterval = 1000000000/fps; // 0.01666 seconds = 60 times per seconds
double nextDrawTime = System.nanoTime() + drawInterval;
while(gameThread != null) {
System.out.println("this game is runing");
// update information such as character positions
// draw the screen with the updated information
update();
repaint();
try {
double remainingTime = nextDrawTime - System.nanoTime();
remainingTime = remainingTime/1000000;
if(remainingTime<0) {
remainingTime = 0;
}
Thread.sleep ((long) remainingTime);
nextDrawTime += drawInterval;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void update() {
player.update();
c.tick();
alien.moveAliens();
}
public void paintComponent(Graphics g) {
// to draw something
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
player.draw(g2);
c.render(g );
g2.dispose();
alien.render(g );
}
}
Alien class
package entity;
import java.awt.Color;
import java.awt.Graphics;
public class Alien {
boolean isVisible;
boolean moveLeft;
boolean moveRight;
Alien[] a = new Alien[10];
private int x;
private int y;
int ax = 10;
int ay = 10;
public Alien(int x, int y) {
}
public void initAlien() {
for(int i=0;i<a.length; i++) {
a[i] = new Alien(ax,ay);
ax += 40;
if(i==4) {
ax=10;
ay+=40;
}
}
}
public void moveAliens() {
for(int i=0;i<a.length; i++) {
if (a[i].moveLeft ==true) {
a[i].x -=2;
}
if (a[i].moveRight ==true) {
a[i].x+=2;
}
for(int i1 = 0; i<a.length; ) {
if(a[i].x>600) {
for(int j =0;j<a.length; j++) {
a[j].moveLeft = true;
a[j].moveRight = false;
a[j].y += 5;
}
}
if(a[i].x<0) {
for(int j = 0; j< a.length; j++) {
a[j].moveLeft = false;
a[j].moveRight = true;
a[j].y += 5;
}
}
}
}
}
So my university uses the book Java programming from problem analysis to program design by DS Malik(ISBN: 978-1-111-53053-2) and in one of the examples I copied the code word for word. It runs but doesn't work the way its supposed too. What you're supposed to do is be able to click and drag circles that populate the screen in an applet. If anyone is interested in the programming example its 12-10. I have provided the code from eclipse. Any and all help is appreciated.
import javax.swing.*;
import java.awt.event.*;
import java.applet.*;
import java.awt.*;
public class FreeDrawApplet extends JApplet implements MouseMotionListener
{
//instance variables
ColorCircle[] myGraph;
final int NUM_CIRCLES = 7;
final int WIDTH = 400;
final int HEIGHT = 400;
public class ColorCircle
{
private int x;
private int y;
public void setx(int iNewX)
{
x = iNewX;
}
public void sety(int iNewY)
{
x = iNewY;
}
public void paint (Graphics g)
{
g.fillOval(x-10, y-10, 20, 20);
}
public boolean selected(int iXcoord, int iYcoord)
{
if ((iXcoord >= x-10) && (iXcoord <= x+10) && (iYcoord >= y-10) && (iYcoord <= y+10))
return true;
else
return false;
}
}//end of public class circle
public void init ()
{
addMouseMotionListener(this);
myGraph = new ColorCircle[NUM_CIRCLES];
for(int i = 0; i < NUM_CIRCLES; i++)
{
ColorCircle myVertex = new ColorCircle();
myVertex.setx((int)(Math.random() * (WIDTH-50)));
myVertex.sety((int)(Math.random() * (HEIGHT-100)));
myGraph[i] = myVertex;
}
JOptionPane.showMessageDialog(null, "Try to drag any one of the colored circles ", "Information", JOptionPane.PLAIN_MESSAGE);
}//end of method
public void paint(Graphics g)
{
Color[] myColor = {Color.black, Color.red, Color.blue, Color.green, Color.cyan, Color.orange, Color.yellow};
if(NUM_CIRCLES > 0)
for(int i =0; i < NUM_CIRCLES; i++)
{
g.setColor(myColor[i]);
myGraph[i].paint(g);
}
}//end of paint method
public void mouseDragged(MouseEvent event)
{
int iX = event.getX();
int iY = event.getY();
for(int i = 0; i < NUM_CIRCLES; i++)
{
if(myGraph[i].selected(iX, iY))
{
myGraph[i].setx(iX);
myGraph[i].sety(iY);
break;
}
}
repaint();
}
public void mouseMoved(MouseEvent p1)
{
}
}
I'm trying to create a reset button for a grid of rectangles. When running it, you'll be able to click rectangles and turn them blue - the reset button is supposed to turn them all back to white. I'm stuck at what to put in the reset method in Lifeform. It currently does nothing. I appreciate any help!
Class Grid:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.Timer;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Grid extends JPanel{
private int a = 50;
private int b = 50;
private Lifeform[][] Life;
private Lifeform ife;
private boolean[][] life = new boolean[a][b];
private Timer t;
private JButton reset;
private JButton run;
private JPanel panel;
Grid(){
ife = new Lifeform();
run = new JButton("Run");
reset = new JButton("Reset");
reset.addActionListener(new ResetListener());
//creates grid of rectangles
Life = new Lifeform[a][b];
int ypos = 0;
for(int i = 0; i < Life.length; i++){
int xpos = 0;
for(int j = 0; j < Life[0].length; j++){
Rectangle r = new Lifeform();
r.setBounds(xpos, ypos, 50, 50);
Life[i][j] = (Lifeform) r;
xpos += 50;
}
ypos += 50;
}
t = new Timer(64, new Movement());
this.addMouseListener(new mouse());
}
public void paintComponent(Graphics g){
for(Lifeform[] n : Life){
for(Lifeform lf : n){
g.setColor(lf.getColor());
g.fillRect((int)lf.getX(), (int)lf.getY(), (int)lf.getWidth(), (int)lf.getHeight());
}
}
for (int i = 0; i <= 25; i++){
g.drawLine(0, 50*i, 1500, 50*i);
g.setColor(Color.black);
}
for (int i = 0; i <= 25; i++){
g.drawLine(50*i, 0, 50*i, 750);
g.setColor(Color.black);
}
}
private JFrame createGrid(){
JPanel panel = new JPanel();
JFrame frame = new JFrame("Alveolate");
frame.add(run, BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);
frame.add(reset, BorderLayout.SOUTH);
frame.add(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(700,700);
frame.setVisible(true);
return frame;
}
public class mouse implements MouseListener{
//turns rectangles blue
public void mouseClicked(MouseEvent e) {
for(int i = 0; i < Life.length; i++){
for(int j = 0; j < Life[i].length; j++){
Lifeform spot = Life[i][j];
if (spot.contains(e.getPoint())) {
Color b = Color.blue;
if( spot.getColor().equals( Color.blue ) ) {
b = Color.white;
}
spot.setColor(b);
}
}
}
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
public class Movement implements ActionListener{
public void actionPerformed ( ActionEvent e ){
}
updateUI();
}
repaint();
}
}
public void startTimer(){
t.start();
}
public void stopTimer(){
t.stop();
}
private class ResetListener implements ActionListener{
public void actionPerformed( ActionEvent e ) {
ife.reset();
updateUI();
}
}
public static void main(String[] args) {
Grid ABC = new Grid();
ABC.createGrid();
ABC.startTimer();
}
}
Class Lifeform:
import java.awt.Color;
import java.awt.Rectangle;
public class Lifeform extends Rectangle {
private Color c;
public Lifeform() {
c = Color.WHITE;
}
public Lifeform(int width){
reset();
}
public Color getColor() {
return c;
}
public boolean setColor( Color c ) {
boolean rtn = false;
if( c != null ) {
this.c = c;
rtn = true;
}
return rtn;
}
public void reset() {
}
}
It doesn't do anything when you hit the Reset button because reset (presumably the reset() method of Lifeform) is defined as doing nothing: there is no code in that method.
In the reset method of your Lifeform class, you code this:
public void reset() {
c = Color.WHITE;
}
In the actionPerformed method of your ResetListener class, you code this:
public void actionPerformed( ActionEvent e ) {
for(Lifeform[] n : Life) {
for(Lifeform lf : n) {
lf.reset();
}
}
updateUI();
}
And that's how you reset all of the Lifeform instances.
In your Grid class, you've coded:
private Lifeform[][] Life;
and
private boolean[][] life = new boolean[a][b];
Don't ever give two fields the same name like this. It's confusing to the reader of your code.
All field names in Java should start with a lowercase letter. This helps you see the difference between a class name Lifeform and a class instance lf.
In this case, the life boolean probably should be a field in the Lifeform class. A more descriptive name would be isLife.
I know the proper way to paint a JPanel is to create a class that extends JPanel and override the paintComponent method. That's nice, but I don't know what I want to draw before making the JPanels.
I have a main JPanel in a GridLayout. The grids are filled with other JPanels. These JPanels are also tracked in a 2D array. Based on button events or other events I want to be able to simply loop through this array, get the graphics, and repaint. That is not working too nicely however. An example of what I wanted to do is:
for (int row = 0; row < GRID_ROWS; row++) {
for (int col = 0; col < GRID_COLS; col++) {
JPanel tmp_panel = new JPanel();
Graphics g = panelGrid[row][col].getGraphics();
Graphics2D g2d = (Graphics2D)g;
//Do some drawing
panelGrid[row][col] = tmp_panel;
backingPanel.add(panelGrid[row][col]);
}
}
Later in the code, perhaps another event I would like to simply be able to loop through and redraw the panels:
for (int row = 0; row < GRID_ROWS; row++) {
for (int col = 0; col < GRID_COLS; col++) {
Graphics g = panelGrid[row][col].getGraphics();
Graphics2D g2d = (Graphics2D)g;
//Do some drawing
panelGrid[row][col].repaint()
}
}
I never got to writing the second block because the first one always complains about graphics not being initialized.
What you can do is have a prototype panel that uses any class that implements an interface, say Drawable, which has a draw(Graphics g) method that need to be overridden.
public interface Drawable {
public void draw(Graphics g);
}
Your prototype panel call can look something like this.
public class DrawablePanel extends JPanel {
private Drawable drawable;
public DrawablePanel(Drawable drawable) {
this.drawable = drawable;
}
public DrawablePanel() {
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (drawable != null) {
drawable.draw(g2);
}
}
}
Only if the Drawable object is not null, will it paint anything. You can either pass a Drawable object to it on instantiation, or you can set the Drawable object later.
So you can create an array of DrawablePanel and set the the Drawable proptery whenever you want.
An example of a Drawable implementation could be something as simple as
public class Ball implements Drawable {
#Override
public void draw(Graphics g) {
g.fillOval(0, 0, 50, 50);
}
}
UPDATE
Here's an example you can play with
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LoopPanelPaint {
private static final int ROUND_SQR = 0;
private static final int BALL = 1;
private List<Color> colorList;
public LoopPanelPaint() {
JPanel matrixPanel = new JPanel();
DrawablePanel[][] drawPanels = createPanelMatrix(matrixPanel);
colorList = createColorList();
JButton createImages = createButton(drawPanels);
JFrame frame = new JFrame();
frame.add(matrixPanel);
frame.add(createImages, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public JButton createButton(final DrawablePanel[][] panels) {
JButton button = new JButton("Create Images");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
addShapesToPanels(panels);
}
});
return button;
}
private DrawablePanel[][] createPanelMatrix(JPanel panel) {
panel.setLayout(new GridLayout(10, 10));
DrawablePanel[][] panels = new DrawablePanel[10][10];
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
DrawablePanel drawPanel = new DrawablePanel();
panels[i][j] = drawPanel;
panel.add(drawPanel);
}
}
return panels;
}
private void addShapesToPanels(DrawablePanel[][] panels) {
Random random = new Random();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
int type = random.nextInt(2);
Drawable drawable = getRandomDrawable(type);
int colorIndex = random.nextInt(colorList.size());
Color color = colorList.get(colorIndex);
panels[i][j].setColor(color);
panels[i][j].setDrawable(drawable);
}
}
}
private Drawable getRandomDrawable(int type) {
switch (type) {
case ROUND_SQR:
return new RoundSquare();
case BALL:
return new Ball();
default:
return null;
}
}
private List<Color> createColorList() {
List<Color> colors = new ArrayList<>();
colors.add(Color.blue);
colors.add(Color.red);
colors.add(Color.gray);
colors.add(Color.green);
colors.add(Color.orange);
colors.add(Color.magenta);
colors.add(Color.yellow);
colors.add(Color.cyan);
return colors;
}
public class DrawablePanel extends JPanel {
private Drawable drawable;
private Color color = Color.black;
public DrawablePanel(Drawable drawable) {
this.drawable = drawable;
}
public DrawablePanel() {
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
repaint();
}
public void setColor(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(color);
if (drawable != null) {
drawable.draw(g2);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(Drawable.SIZE, Drawable.SIZE);
}
}
public class Ball implements Drawable {
#Override
public void draw(Graphics g) {
g.fillOval(0, 0, Drawable.SIZE, Drawable.SIZE);
}
}
public class RoundSquare implements Drawable {
#Override
public void draw(Graphics g) {
g.fillRoundRect(0, 0, Drawable.SIZE, Drawable.SIZE, 10, 10);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new LoopPanelPaint();
}
});
}
}
interface Drawable {
public static final int SIZE = 50;
public void draw(Graphics g);
}
Hi, I'm developing a snake game. To create the snake I'm using ArrayList. While moving the snake I'm getting the following error: "java.lang.IndexOutOfBoundsException: Index: 3, Size: 3". Below is my program. In Snake.update() method I have problem.
Game.java:
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Game extends JFrame {
public Game(){
add(new GamePanel());
setTitle("Game Test3");
setVisible(true);
setAlwaysOnTop(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
public static void main(String[] args) {
new Game();
}
}
GamePanel.java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener {
public static int width = 300;
public static int height = 400;
private Thread thread;
private Image image;
private Graphics2D g;
private Food food;
private Snake snake;
public GamePanel() {
setPreferredSize(new Dimension(width, height));
setFocusable(true);
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
public void run() {
image = createImage(width, height);
g = (Graphics2D) image.getGraphics();
RenderingHints reneringHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHints(reneringHints);
food = new Food();
snake = new Snake();
while (true) {
gameRender();
gameUpdate();
gameDraw();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void gameDraw() {
Graphics2D g2 = (Graphics2D) getGraphics();
g2.drawImage(image, 0, 0, this);
g2.dispose();
}
private void gameRender() {
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
// food drawing
food.draw(g);
// snake drawing
snake.draw(g);
}
private void gameUpdate() {
food.update();
snake.update();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
snake.setLeft(true);
}
if (key == KeyEvent.VK_RIGHT) {
snake.setRight(true);
}
if (key == KeyEvent.VK_UP) {
snake.setUp(true);
}
if (key == KeyEvent.VK_DOWN) {
snake.setDown(true);
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
snake.setLeft(false);
}
if (key == KeyEvent.VK_RIGHT) {
snake.setRight(false);
}
if (key == KeyEvent.VK_UP) {
snake.setUp(false);
}
if (key == KeyEvent.VK_DOWN) {
snake.setDown(false);
}
}
public void keyTyped(KeyEvent e) {
}
}
//snake body
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
public class Snake {
private int x;
private int y;
private int r;
int body;
Rectangle rectangle;
ArrayList<Rectangle> rc = new ArrayList<Rectangle>();
private boolean left;
private boolean right;
private boolean up;
private boolean down;
public Snake() {
x = 150;
y = 150;
r = 4;
body = 3;
for (int i = 0; i < body; i++) {
rc.add(new Rectangle(x - i * r * 3, y, r * 3, r * 3));
}
}
public void draw(Graphics2D g) {
for (int i = 0; i < body; i++) {
if (i == 0) {
g.setColor(Color.red);
} else {
g.setColor(Color.green);
}
g.fillOval(rc.get(i).x, rc.get(i).y, rc.get(i).width,
rc.get(i).height);
}
}
public void update() {
for (int i = body; i > 0; i--) {
rc.set(i, rc.get(i - 1));
}
if (left) {
rc.get(0).x -= 1;
System.out.println("vbnv");
}
if (right) {
rc.get(0).x += 1;
}
if (up) {
rc.get(0).y -= 1;
}
if (down) {
rc.get(0).y += 1;
}
}
public void setLeft(boolean b) {
left = b;
}
public void setRight(boolean b) {
right = b;
}
public void setUp(boolean b) {
up = b;
}
public void setDown(boolean b) {
down = b;
}
}
Without reading the code I can tell that somewhere you are trying to get non-existent element. Read the message of exception, it tells you that your ArrayList consist of three elements (which means that the last element has index of 2) while you try to get the element with index of 3.
Update: yes, the problem is here:
for (int i = body; i > 0; i--) {
rc.set(i, rc.get(i - 1));
}
body equals to 3, and rc after the constructor invocation has the size of 3. set method replaces already existing element with the specified index (see the documentation), but element with index of 3 doesn't exist. That's the cause of an exception.
Your problem is in this loop:
for (int i = body; i > 0; i--) {
rc.set(i, rc.get(i - 1));
}
During the first iteration around this loop, you will be calling:
rc.set(3, <someValue>);
3 is not a valid index in a list of length 3. The largest valid index is 2.
for (int i = body; i > 0; i--)
In this for, i is assigned with the value of body(which is 3) and it looks like your ArrayList rc has only 3 elements in it. Therefore, when you try to access the index 3 using rc.set(3, something), its giving the IndexOutOfBoundsException.
Always remember, whether it is Arrays or ArrayList, the max possible index accessible in them is always array.length - 1 and ArrayList.size() - 1.