Java tutorial
/******************************************************************************* * 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.scenes.scene2d.ui; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.input.GestureDetector.GestureListener; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.Layout; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.utils.ScissorStack; /** @author Nathan Sweet * @author mzechner */ public class FlickScrollPane extends Group implements Layout { private final Stage stage; private Actor widget; protected boolean needsLayout; private final Rectangle widgetAreaBounds = new Rectangle(); private final Rectangle scissorBounds = new Rectangle(); private GestureDetector gestureDetector; private boolean scrollX, scrollY; float amountX, amountY; private float maxX, maxY; float velocityX, velocityY; float flingTimer; public boolean bounces = true; public float flingTime = 1f; public float bounceDistance = 50, bounceSpeedMin = 30, bounceSpeedMax = 200; public boolean emptySpaceOnlyScroll; public boolean forceScrollX, forceScrollY; public boolean clamp = true; public FlickScrollPane(Actor widget, Stage stage) { this(widget, stage, null); } public FlickScrollPane(Actor widget, Stage stage, String name) { super(name); this.stage = stage; this.widget = widget; if (widget != null) this.addActor(widget); gestureDetector = new GestureDetector(new GestureListener() { public boolean pan(int x, int y, int deltaX, int deltaY) { amountX -= deltaX; amountY += deltaY; clamp(); return false; } public boolean fling(float x, float y) { if (Math.abs(x) > 150) { flingTimer = flingTime; velocityX = x; } if (Math.abs(y) > 150) { flingTimer = flingTime; velocityY = -y; } return flingTimer > 0; } public boolean touchDown(int x, int y, int pointer) { flingTimer = 0; return true; } public boolean zoom(float originalDistance, float currentDistance) { return false; } public boolean tap(int x, int y, int count) { return FlickScrollPane.this.tap(x, y); } public boolean longPress(int x, int y) { return false; } }); } boolean tap(int x, int y) { focus(null, 0); if (!super.touchDown(x, y, 0)) return false; Actor actor = focusedActor[0]; toLocalCoordinates(actor, point); actor.touchUp(point.x, point.y, 0); return true; } public void toLocalCoordinates(Actor actor, Vector2 point) { if (actor.parent == this) return; toLocalCoordinates(actor.parent, point); Group.toChildCoordinates(actor, point.x, point.y, point); } void clamp() { if (!clamp) return; if (bounces) { amountX = Math.max(-bounceDistance, amountX); amountX = Math.min(maxX + bounceDistance, amountX); amountY = Math.max(-bounceDistance, amountY); amountY = Math.min(maxY + bounceDistance, amountY); } else { amountX = Math.max(0, amountX); amountX = Math.min(maxX, amountX); amountY = Math.max(0, amountY); amountY = Math.min(maxY, amountY); } } public void act(float delta) { if (flingTimer > 0) { float alpha = flingTimer / flingTime; alpha = alpha * alpha * alpha; amountX -= velocityX * alpha * delta; amountY -= velocityY * alpha * delta; clamp(); // Stop fling if hit bounce distance. if (amountX == -bounceDistance) velocityX = 0; if (amountX >= maxX + bounceDistance) velocityX = 0; if (amountY == -bounceDistance) velocityY = 0; if (amountY >= maxY + bounceDistance) velocityY = 0; flingTimer -= delta; } if (bounces && !gestureDetector.isPanning()) { if (amountX < 0) { amountX += (bounceSpeedMin + (bounceSpeedMax - bounceSpeedMin) * -amountX / bounceDistance) * delta; if (amountX > 0) amountX = 0; } else if (amountX > maxX) { amountX -= (bounceSpeedMin + (bounceSpeedMax - bounceSpeedMin) * -(maxX - amountX) / bounceDistance) * delta; if (amountX < maxX) amountX = maxX; } if (amountY < 0) { amountY += (bounceSpeedMin + (bounceSpeedMax - bounceSpeedMin) * -amountY / bounceDistance) * delta; if (amountY > 0) amountY = 0; } else if (amountY > maxY) { amountY -= (bounceSpeedMin + (bounceSpeedMax - bounceSpeedMin) * -(maxY - amountY) / bounceDistance) * delta; if (amountY < maxY) amountY = maxY; } } } private void calculateBoundsAndPositions(Matrix4 batchTransform) { // Get widget's desired width. float widgetWidth, widgetHeight; if (widget instanceof Layout) { Layout layout = (Layout) widget; widgetWidth = layout.getPrefWidth(); widgetHeight = layout.getPrefHeight(); } else { widgetWidth = widget.width; widgetHeight = widget.height; } // Figure out if we need horizontal/vertical scrollbars, scrollX = widgetWidth > width || forceScrollX; scrollY = widgetHeight > height || forceScrollY; // If the widget is smaller than the available space, make it take up the available space. widgetWidth = Math.max(width, widgetWidth); widgetHeight = Math.max(height, widgetHeight); if (widget.width != widgetWidth || widget.height != widgetHeight) { widget.width = widgetWidth; widget.height = widgetHeight; needsLayout = true; } // Set the widget area bounds. widgetAreaBounds.set(0, 0, width, height); // Calculate the widgets offset depending on the scroll state and available widget area. maxX = widget.width - width; maxY = widget.height - height; widget.y = (int) (scrollY ? amountY : maxY) - widget.height + height; widget.x = -(int) (scrollX ? amountX : 0); // Caculate the scissor bounds based on the batch transform, the available widget area and the camera transform. We need to // project those to screen coordinates for OpenGL ES to consume. ScissorStack.calculateScissors(stage.getCamera(), batchTransform, widgetAreaBounds, scissorBounds); } @Override public void draw(SpriteBatch batch, float parentAlpha) { if (widget == null) return; // Setup transform for this group. applyTransform(batch); // Calculate the bounds for the scrollbars, the widget area and the scissor area. calculateBoundsAndPositions(batch.getTransformMatrix()); // BOZO - Call every frame? if (needsLayout) layout(); // Enable scissors for widget area and draw the widget. ScissorStack.pushScissors(scissorBounds); drawChildren(batch, parentAlpha); ScissorStack.popScissors(); resetTransform(batch); } @Override public void layout() { if (!needsLayout) return; needsLayout = false; if (widget instanceof Layout) { Layout layout = (Layout) widget; layout.invalidate(); layout.layout(); } } @Override public void invalidate() { needsLayout = true; } @Override public boolean touchDown(float x, float y, int pointer) { if (pointer != 0) return false; if (emptySpaceOnlyScroll && super.touchDown(x, y, pointer)) return true; return gestureDetector.touchDown((int) x, (int) y, pointer, 0); } @Override public void touchUp(float x, float y, int pointer) { clamp(); gestureDetector.touchUp((int) x, (int) y, pointer, 0); if (focusedActor[pointer] != null) super.touchUp(x, y, pointer); } @Override public void touchDragged(float x, float y, int pointer) { gestureDetector.touchDragged((int) x, (int) y, pointer); super.touchDragged(x, y, pointer); } @Override public Actor hit(float x, float y) { return x > 0 && x < width && y > 0 && y < height ? this : null; } public void setScrollX(float pixels) { this.amountX = pixels; } public float getScrollX() { return amountX; } public void setScrollY(float pixels) { amountY = pixels; } public float getScrollY() { return amountY; } /** Sets the {@link Actor} embedded in this scroll pane. * @param widget the Actor */ public void setWidget(Actor widget) { if (this.widget != null) removeActor(this.widget); this.widget = widget; if (widget != null) addActor(widget); } public Actor getWidget() { return widget; } public boolean isPanning() { return gestureDetector.isPanning(); } public float getVelocityX() { if (flingTimer <= 0) return 0; float alpha = flingTimer / flingTime; alpha = alpha * alpha * alpha; return velocityX * alpha * alpha * alpha; } public float getVelocityY() { return velocityY; } public float getPrefWidth() { return 150; } public float getPrefHeight() { return 150; } public float getMinWidth() { return 0; } public float getMinHeight() { return 0; } public float getMaxWidth() { return 0; } public float getMaxHeight() { return 0; } }