Java 如何在代码中滚动滚动窗格?

Java 如何在代码中滚动滚动窗格?,java,libgdx,Java,Libgdx,我正试图在LibGDX中创建一个类似于这样的滚轮组件: 我使用ScrollPane,因为它内置了输入和抛出处理。我有一个滚轮的图像,它分为14个部分,滚动窗格本身缩短了两个部分,因此在左右两侧各有一个部分,可以向任意方向滚动。一旦滚动位置到达任一方向的末端,我想将滚动位置重置回中心。一遍又一遍地这样做会产生一种无限滚动的幻觉(希望如此) 我遇到的问题是如何在代码中定位滚动窗格,以便在图像到达任意一端时重置图像。到目前为止,我尝试设置滚动位置的任何操作都不起作用。我尝试过setScrollX()

我正试图在LibGDX中创建一个类似于这样的滚轮组件:

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

我遇到的问题是如何在代码中定位滚动窗格,以便在图像到达任意一端时重置图像。到目前为止,我尝试设置滚动位置的任何操作都不起作用。我尝试过setScrollX()和scrollTo()方法。我还尝试将滚动窗格的大小设置为各种大小(与图像大小相同,并且比图像小两部分)。在设置滚动值之前,我尝试在滚动窗格上调用layout、invalidate和pack,以确保其布局正确。我认为updateVisualScroll()可能会强制它更新滚动位置,但这也没有效果

无论我做什么,它都会忽略所有更改滚动位置的调用,因此我显然遗漏了一些内容。在下面的代码中,我试图让滚轮从图像的中心开始,相反,它的开始位置一直在左边

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

手动单击和拖动时,滚动确实有效,因此我相信滚动窗格设置正确,我只是无法让它在代码中滚动

这是我的代码,为了简单起见,我去掉了所有的实验代码,因为我的实验都不起作用

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();
    }
}
公共类MyScrollWheel扩展容器{
私有滚动窗格;
私有图像;
私有整数偏移;
公共MyScrollWheel(){
纹理=新纹理(Gdx.files.internal(“internal/scrollwheel.png”);
图像=新图像(纹理);
scrollOffset=(int)(image.getWidth()/14);
滚动窗格=新的滚动窗格(图像);
scrollPane.setOverscroll(false,false);
setActor(滚动窗格);
大小(image.getWidth()-(scrollOffset*2),image.getHeight());

scrollPane.setScrollX(scrollOffset);//好吧,我希望能够得到一些可以构建的东西。我只是扩展了actor,让它接受一个纹理,这样我就可以使用Texture.wrap并让它使用
SpriteBatch.draw()进行绘制
。我现在可以继续滚动它,根据滚动增量,您可以计算出它已滚动了多远。我看不出有任何需要重置控制盘,但如果您真的想重置控制盘,您只需执行
wheel.setScroll(0);

一个限制是它不是一个
可绘制的
,因此不能像
NinePatch
那样缩放。你必须给它一个普通的轮子纹理,按照你想要的大小绘制,你可以添加普通的缩放,手动保持纵横比。然后添加边,也许在边上覆盖一个渐变来创建深度

滚轮:

屏幕中的用法:

因此,只需创建一个可耕的滚轮纹理,并将其包含在ScrollWheel构造函数中。如果使用此代码,它将在屏幕中央绘制滚轮

scroll
变量基本上保持滚动量,因此如果您想将其限制在0到100之间,只需在
setScroll()
中添加此功能即可

if(scroll>100)scroll=100;
如果(滚动<0)滚动=0,则为else;
然后,您可以添加一个步骤。因此,如果您想使用滑块旋转图像,可以通过
scroll*3,6f
scroll*(maxScroll/maxStep)


我真的很喜欢这种方式,我将在将来使用它作为我的滑块:D。我已经对它进行了一些扩展和修改,您可以在这里看到我的实现:

在Menno Gouw的滚轮上扩展,我添加了一些其他功能:

  • 带抛投时间设置的抛投支架
  • 调整车轮灵敏度的精度设置
  • 需要一个可拖动的
  • 兼容在滚动窗格内使用
注意:出于我的目的,我在构造函数中使用了一个标签,但是如果您不想将它绑定到标签上,可以很容易地更改它

这是我在手机上录制的演示滚轮的视频

-编辑1:布局错误现在已经修复(希望如此)。当在滚动窗格中移动时,它现在会更新其位置,并且可绘制内容会被剪切到参与者的边框上

-编辑2:添加了对可用于着色的静止绘图的支持,以及更改方向盘方向的方法(setRightPositiveDirection())

public类滚轮扩展了Actor{
私人可牵引车轮可牵引,车轮遮阳;
自有品牌;
私有int unscaledScrollValueX=0,scrollValueX=0;
私有布尔isNotEdge;
专用整数精度=40;
私有int方向=1;
私有int minValue=Integer.MIN\u值,maxValue=Integer.MAX\u值;
//手动卷轴
专用int分隔符;
私家车轮距;
//投掷
专用浮子燧石,燧石=1f;
私人浮动速度x;
公共滚轮(可拉伸滚轮可拉伸、可拉伸滚轮着色、标签){
this.wheelDrawable=wheelDrawable;
this.wheelShading=wheelShading;
this.label=标签;
wheelWidth=(int)wheelDrawable.getMinWidth();
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...
}
if (scroll > 100) scroll = 100; 
else if (scroll < 0) scroll = 0;
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();
        }
    }
}