Search code examples
javalayoutlibgdxscene2d

Add plus/minus buttons to table cell LibGDX


I am looking for a way to add plus and minus buttons to table cells in LibGDX. I'm trying to do something like this image:

enter image description here

I noticed that the uiskin files I'm using contain plus and minus buttons (right next to the checkbox) which will do just fine (uiskin.png).

Any tips on how I can add those to my table and make it so they increase/decrease the integer value in that table cell?

I've added a rudimentary sample code with an indication of what I want to do:

@Override
public void create() {
    Stage stage = new Stage();
    Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));

    Table table = new Table(skin);
    table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
    for(int i = 0; i < 10; i++){
        table.add(Integer.toString(i)); //Somehow add plus/minus buttons left 
                                        //and right that increase/decrease the integer value
        table.row();
    }

    stage.addActor(table);

    Gdx.input.setInputProcessor(stage);
}


@Override
public void render() {
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
}

This is just some example code, but if it works here I will certainly be able to figure it out.

Thanks


Solution

  • First, you need an array to store your number values. It's not good practice to use your View (table cells) to store your Model (number values), and besides, Label cells store a String, not an integer, so it would be inconvenient.

    private int[] tableData = new int[10];
    

    Now you need a button that can be associated with a row in your data. Here's one way Create a subclass of ImageButton that can take a row number and mode (subtract or add).

    public class IncrementButton extends ImageButton {
    
        public final int rowNumber;
        public final boolean decrement;
    
        public IncrementButton (Skin skin, String styleName, int rowNumber, boolean decrement){
            super(skin, styleName);
            this.rowNumber = rowNumber;
            this.decrement = decrement;
        }
    }
    

    The plus and minus images in the uiskin atlas you're using are called "tree-minus" and "tree-plus". You need an ImageButton style for each of these that uses one of these as the imageUp property. (imageUp is used as a default if you don't define images for other states such as imageDown.) You can add these to uiskin.json:

    com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle: {
        plus: { down: default-round-down, up: default-round, imageUp: tree-plus },
        minus: { down: default-round-down, up: default-round, imageUp: tree-minus }
    },
    

    Now you can create a ChangeListener for these buttons that will modify the numbers in the data and update the table accordingly. And then set it all up:

    Stage stage = new Stage();
    Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
    
    final Table table = new Table(skin);
    final Label[] labels = new Label[tableData.length]; //keep references to the labels for updating them.
    
    final ChangeListener incrementListner = new ChangeListener() {
        @Override
        public void changed(ChangeEvent event, Actor actor) {
            IncrementButton incrementButton = (IncrementButton)actor;
            int row = incrementButton.rowNumber;
            tableData[row] += incrementButton.decrement ? -1 : 1;
            labels[row].setText(Integer.toString(tableData[row]));
        }
    };
    
    table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
    for(int i = 0; i < tableData.length; i++){
        IncrementButton decrementButton = new IncrementButton(skin, "minus", i, true);
        decrementButton.addListener(incrementListner);
        IncrementButton incrementButton = new IncrementButton(skin, "plus", i, false);
        incrementButton.addListener(incrementListner);
    
        table.add(decrementButton);
        labels[i] = table.add(Integer.toString(i)).getActor();//Add number label and keep reference to it in labels array for the change listener to look up
        table.add(incrementButton);
        table.row();
    }
    
    stage.addActor(table);
    
    Gdx.input.setInputProcessor(stage);
    

    If you want to set min and max values for the data, then you need some arrays for the plus buttons and minus buttons (similar to the labels array) so the change listener can look up and disable/enable the buttons accordingly when a limit is hit.