Skip to content

Instantly share code, notes, and snippets.

@MobiDevelop
Last active March 21, 2022 02:00
Show Gist options
  • Save MobiDevelop/5540815 to your computer and use it in GitHub Desktop.
Save MobiDevelop/5540815 to your computer and use it in GitHub Desktop.
A LibGDX ScrollPane with page support. It center-locks on pages after scrolling.
package com.badlogic.gdx.tests.examples;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.tablelayout.Cell;
public class PagedScrollPane extends ScrollPane {
private float pickAmountX;
private float pickDifferenceX = -1;
private boolean wasPanDragFling = false;
private float scrollToPageSpeed = 1000;
private float pageSpacing;
private Table content;
public PagedScrollPane () {
super(null);
content = new Table();
content.defaults().space(50);
setWidget(content);
}
public PagedScrollPane (Skin skin) {
super(null, skin);
content = new Table();
content.defaults().space(50);
setWidget(content);
}
public PagedScrollPane (Skin skin, String styleName) {
super(null, skin, styleName);
content = new Table();
content.defaults().space(50);
setWidget(content);
}
public PagedScrollPane (Actor widget, ScrollPaneStyle style) {
super(null, style);
content = new Table();
content.defaults().space(50);
setWidget(content);
}
public void addPages (Actor... pages) {
for (Actor page : pages) {
content.add(page).expandY().fillY();
}
}
public void addPage (Actor page) {
content.add(page).expandY().fillY();
}
@Override
public void act (float delta) {
super.act(delta);
if (wasPanDragFling && !isPanning() && !isDragging() && !isFlinging()) {
wasPanDragFling = false;
scrollToPage();
} else {
if (isPanning() || isDragging() || isFlinging()) {
wasPanDragFling = true;
}
}
}
@Override
public void setWidth (float width) {
super.setWidth(width);
if (content != null) {
for (Cell cell : content.getCells()) {
cell.width(width);
}
content.invalidate();
}
}
public void setPageSpacing (float pageSpacing) {
if (content != null) {
content.defaults().space(pageSpacing);
for (Cell cell : content.getCells()) {
cell.space(pageSpacing);
}
content.invalidate();
}
}
private void scrollToPage () {
final float width = getWidth();
final float scrollX = getScrollX();
final float maxX = getMaxX();
if (scrollX >= maxX || scrollX <= 0) return;
Array<Actor> pages = content.getChildren();
float pageX = 0;
float pageWidth = 0;
if (pages.size > 0) {
for (Actor a : pages) {
pageX = a.getX();
pageWidth = a.getWidth();
if (scrollX < (pageX + pageWidth * 0.5)) {
break;
}
}
setScrollX(MathUtils.clamp(pageX - (width - pageWidth) / 2, 0, maxX));
}
}
}
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.tests.examples;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Skin.TintedDrawable;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.tests.utils.GdxTest;
import com.esotericsoftware.tablelayout.Value;
public class PagedScrollPaneTest extends GdxTest {
private Skin skin;
private Stage stage;
private Table container;
public void create () {
stage = new Stage(0, 0, false);
skin = new Skin(Gdx.files.internal("data/uiskin.json"));
Gdx.input.setInputProcessor(stage);
container = new Table();
stage.addActor(container);
container.setFillParent(true);
PagedScrollPane scroll = new PagedScrollPane();
scroll.setFlingTime(0.1f);
scroll.setPageSpacing(25);
int c = 1;
for (int l = 0; l < 10; l++) {
Table levels = new Table().pad(50);
levels.defaults().pad(20, 40, 20, 40);
for (int y = 0; y < 3; y++) {
levels.row();
for (int x = 0; x < 4; x++) {
levels.add(getLevelButton(c++)).expand().fill();
}
}
scroll.addPage(levels);
}
container.add(scroll).expand().fill();
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
Table.drawDebug(stage);
}
public void resize (int width, int height) {
stage.setViewport(width, height, false);
}
public void dispose () {
stage.dispose();
skin.dispose();
}
public boolean needsGL20 () {
return false;
}
/**
* Creates a button to represent the level
*
* @param level
* @return The button to use for the level
*/
public Button getLevelButton(int level) {
Button button = new Button(skin);
ButtonStyle style = button.getStyle();
style.up = style.down = null;
Label label = new Label(Integer.toString(level), skin);
label.setFontScale(2f);
label.setAlignment(Align.center);
button.stack(new Image(skin.newDrawable("default-round-large", Color.RED)), label).expand().fill();
skin.add("star-filled", skin.newDrawable("white", Color.YELLOW), Drawable.class);
skin.add("star-unfilled", skin.newDrawable("white", Color.GRAY), Drawable.class);
int stars = MathUtils.random(-1, +3);
Table starTable = new Table();
starTable.defaults().pad(5);
if (stars >= 0) {
for (int star = 0; star < 3; star++) {
if (stars > star) {
starTable.add(new Image(skin.getDrawable("star-filled"))).width(20).height(20);
} else {
starTable.add(new Image(skin.getDrawable("star-unfilled"))).width(20).height(20);
}
}
}
button.row();
button.add(starTable).height(30);
button.setName("Level" + Integer.toString(level));
button.addListener(levelClickListener);
return button;
}
/**
* Handle the click - in real life, we'd go to the level
*/
public ClickListener levelClickListener = new ClickListener() {
@Override
public void clicked (InputEvent event, float x, float y) {
System.out.println("Click: " + event.getListenerActor().getName());
}
};
}
@metaphore
Copy link

Nice idea, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment