如何在代码中滚动ScrollPane?

时间:2016-02-18 16:27:17

标签: java libgdx

我正在尝试在LibGDX中制作这样的滚轮组件: enter image description here

我正在使用ScrollPane,因为它内置了输入和fling处理。我有一个分为14个部分的滚轮图像,滚动窗口本身是两个部分更短,所以右边有一个部分,左侧可以向任一方向滚动。一旦滚动位置到达任一方向的末端,我想将滚动位置重置回中心。一遍又一遍地这样做会产生无限滚轮的幻觉(希望如此)。

我遇到的问题是如何在代码中放置ScrollPane,以便在图像到达任一端时重置图像。到目前为止,我没有试图设置滚动位置。我尝试过setScrollX()和scrollTo()方法。我也尝试将滚动窗格的大小设置为各种尺寸(与图像大小相同,两个部分比图像小)。我已经尝试在滚动窗格上调用布局,无效和打包,以确保在设置滚动值之前它已正确布局。我认为updateVisualScroll()可能会强制它更新滚动位置,但这也没有效果。

无论我做什么,它都会忽略我改变滚动位置的所有调用,所以我显然错过了一些东西。在下面的代码中,我试图让滚轮从图像的中心开始,而不是它的起始位置一直在左边。

我还需要能够获取当前滚动位置以检测它何时到达任何一端。我尝试重写act()方法并打印出scrollPane.getX(),但即使我手动点击并拖动它以滚动ScrollPane,此值也始终为“0”。

滚动功能在手动点击和拖动时确实有效,所以我相信ScrollPane设置正确,我无法让它在代码中滚动。

这是我的代码,为简单起见,我将所有实验代码都删除了,因为我的实验都没有。

public class MyScrollWheel extends Container<ScrollPane> {
    private ScrollPane scrollPane;
    private Image image;
    private int scrollOffset;

    public MyScrollWheel(){
        Texture texture = new Texture(Gdx.files.internal("internal/scrollwheel.png"));
        image = new Image(texture);

        scrollOffset = (int)(image.getWidth()/14);

        scrollPane = new ScrollPane(image);
        scrollPane.setOverscroll(false, false);

        setActor(scrollPane);
        size(image.getWidth()-(scrollOffset*2), image.getHeight());

        scrollPane.setScrollX(scrollOffset); // << this doesn't scroll
        scrollPane.updateVisualScroll();
    }
}

2 个答案:

答案 0 :(得分:2)

好吧,我希望能够得到你可以建立的东西。我简单地做的是扩展actor并让它接受一个纹理,这样我就可以使用Texture.wrap并使用SpriteBatch.draw()进行绘制。我现在可以继续滚动它,并根据滚动增量你可以弄清楚它滚动了多远。我没有看到任何重置轮子的需要,但如果你真的想要,你可以wheel.setScroll(0);

一个限制是,它不是Drawable,因此无法像NinePatch那样进行缩放。你必须给它一个普通的轮子纹理画你想要它的大小,你可以添加正常的缩放,并手动保持宽高比。然后添加边并可能在那些边上叠加渐变以创建深度。

滚轮:

public class ScrollWheel extends Actor {
    Texture wheelTexture;
    private int scroll = 0;

    public int getScroll() {
        return scroll;
    }
    public void setScroll(int scroll) {
        this.scroll = scroll;
    }

    public ScrollWheel(Texture texture)
    {
        wheelTexture = texture;
        wheelTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.ClampToEdge);

        setWidth(texture.getWidth());
        setHeight(texture.getHeight());
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        super.draw(batch, parentAlpha);

        batch.draw(wheelTexture, getX(), getY(), scroll, 0,
                wheelTexture.getWidth(), wheelTexture.getHeight());
    }
}
屏幕中的

用法:

public class TestScreen implements Screen {
    Stage stage;
    ScrollWheel wheel;

    public TestScreen() {
        stage = new Stage();
        Table t = new Table();
        t.setFillParent(true);
        stage.addActor(t);

        wheel = new ScrollWheel(new Texture("hud/wheel_part.png"));
        wheel.addListener(new DragListener() {
            @Override
            public void drag(InputEvent event, float x, float y, int pointer) {
                super.drag(event, x, y, pointer);
                wheel.setScroll(wheel.getScroll() + (int)getDeltaX());
            }
        });

        t.add(wheel);
        Gdx.input.setInputProcessor(stage);
    }

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(.3f, .36f, .42f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        stage.act();
        stage.draw();
    }
    //...Other mandatory screen methods...
}

因此,只需创建一个可填充的轮子纹理,并将其包含在ScrollWheel构造函数中。如果你使用这个确切的代码,它会在屏幕的中心绘制轮子。

scroll变量基本上保存了滚动量,因此如果您想要将此值限制在0到100之间,则只需在setScroll()中添加此功能。

if (scroll > 100) scroll = 100; 
else if (scroll < 0) scroll = 0;

然后您可以为其添加一个步骤。因此,如果您想使用滑块旋转图像,可以按scroll * 3,6fscroll * (maxScroll / maxStep)

设置旋转

我真的很喜欢这个结果,我将来会用它作为我的滑块:D。我已经对它进行了扩展和修改,你可以在这里看到我的实现:https://youtu.be/RNLk5B-VfYg

答案 1 :(得分:2)

扩展Menno Gouw的滚轮,我添加了更多功能:

  • 通过设置投掷时间来支持
  • 调整滚轮灵敏度的精确设置
  • 采取可绘制的
  • 兼容在ScrollPane中使用

注意: 出于我的目的,我在构造函数中使用了Label,但如果您不希望它被绑定,可以轻松更改到标签。

以下是我在手机上录制滚动滚轮的视频。

https://www.youtube.com/watch?v=RwVrez4BZsY&feature=youtu.be

- 编辑1:布局错误现已修复(希望如此)。它现在在ScrollPane中移动时更新其位置,并且Drawables被剪切到Actor的边界。

- 编辑2:添加了对阴影的固定绘图和更改滚轮方向的方法的支持(setRightPositiveDirection())。

public class ScrollWheel extends Actor {
    private Drawable wheelDrawable, wheelShading;
    private Label label;

    private int unscaledScrollValueX=0, scrollValueX=0;
    private boolean isNotEdge;
    private int precision=40;
    private int direction=1;
    private int minValue=Integer.MIN_VALUE, maxValue=Integer.MAX_VALUE;
    // MANUAL SCROLL
    private int separator;
    private int wheelWidth;
    // FLING
    private float flingTimer, flingTime=1f;
    private float velocityX;

    public ScrollWheel(Drawable wheelDrawable, Drawable wheelShading, Label label) {
        this.wheelDrawable = wheelDrawable;
        this.wheelShading = wheelShading;
        this.label = label;

        wheelWidth = (int)wheelDrawable.getMinWidth();
        separator = wheelWidth;

        setWidth(wheelDrawable.getMinWidth());
        setHeight(wheelDrawable.getMinHeight());

        // stops ScrollPane from overriding input events
        InputListener stopTouchDown = new InputListener() {
            public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
                event.stop();
                return false;
            }
        };
        addListener(stopTouchDown);

        ActorGestureListener flickScrollListener = new ActorGestureListener() {
            public void pan (InputEvent event, float x, float y, float deltaX, float deltaY) {
                updateScroll(deltaX);
            }
            public void fling (InputEvent event, float x, float y, int button) {
                if (Math.abs(x) > 150) {
                    flingTimer = flingTime;
                    velocityX = x;
                }
            }
            public boolean handle (Event event) {
                if (super.handle(event)) {
                    if (((InputEvent)event).getType() == InputEvent.Type.touchDown) flingTimer = 0;
                    return true;
                }
                return false;
            }
        };
        addListener(flickScrollListener);
    }

    private void updateScroll(float delta){
        unscaledScrollValueX += (delta * direction);
        scrollValueX = (int)(unscaledScrollValueX / precision);

        isNotEdge = true;
        if (scrollValueX <= minValue){
            scrollValueX = minValue;
            unscaledScrollValueX = minValue * precision;
            isNotEdge = false;          
        }
        else if (scrollValueX >= maxValue){
            scrollValueX = maxValue;
            unscaledScrollValueX = maxValue * precision;
            isNotEdge = false;          
        }
        if (isNotEdge){         
            separator += delta;
            if (separator <= 0){
                separator = wheelWidth;
            }
            else if (separator >= wheelWidth) {
                separator = 0;
            }
        }

        updateLabel();      
    }
    private void updateLabel(){
        label.setText("" + scrollValueX);   
    }

    public void setMinValue(int minValue){ this.minValue = minValue; }
    public void setMinValueToNone(){ minValue=Integer.MIN_VALUE; }
    public void setMaxValue(int maxValue){ this.maxValue = maxValue; }
    public void setMaxValueToNone(){ minValue=Integer.MAX_VALUE; }
    public void setFlingTime(float flingTime){ this.flingTime = flingTime; }
    public void setPrecision(int precision){ this.precision = precision; }
    public void setRightPositiveDirection(boolean rightPositive){ direction = (rightPositive) ? 1 : -1; }

    @Override
    public void act(float delta){
        super.act(delta);

        boolean animating = false;
        if (flingTimer > 0) {
            float alpha = flingTimer / flingTime;
            updateScroll(velocityX * alpha * delta);

            flingTimer -= delta;
            if (flingTimer <= 0) {
                velocityX = 0;
            }
            animating = true;
        }

        if (animating) {
            Stage stage = getStage();
            if (stage != null && stage.getActionsRequestRendering()){
                Gdx.graphics.requestRendering();
            }
        }
    }

    @Override
    public void draw(Batch batch, float parentAlpha){
        super.draw(batch, parentAlpha);
        batch.flush();
        if (clipBegin(getX(), getY(), getWidth(), getHeight())){
            wheelDrawable.draw(batch, getX() + separator - wheelWidth, getY(), wheelDrawable.getMinWidth(), wheelDrawable.getMinHeight());      
            wheelDrawable.draw(batch, getX() + separator, getY(), wheelDrawable.getMinWidth(), wheelDrawable.getMinHeight());
            wheelShading.draw(batch, getX(), getY(), wheelShading.getMinWidth(), wheelShading.getMinHeight());
            batch.flush();
            clipEnd();
        }
    }
}