【问题标题】:How to access other elements in click listeners in libgdx如何访问 libgdx 中点击侦听器中的其他元素
【发布时间】:2013-08-11 20:56:32
【问题描述】:

如何通过调用 settext 方法修改标签信息的文本?

例如根据按下的按钮,我想适当地设置标签的文本

当我尝试访问标签时出现此错误:

不能在不同的内部类中引用非最终变量 i 方法

        Skin skin = new Skin(Gdx.files.internal("uiskin.json"));
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);
        table = new Table();
        table.setFillParent(true);
        stage.addActor(table);

        String sentence = "One two three four five six seven eight";



        String[] temp = sentence.split(" ");
        ArrayList<String> words = new ArrayList<String>(Arrays.asList(temp));


        info = new Label( "Welcome to Android!", skin );

        for(int i=0; i<words.size();i++)
        {

            TextButton button = new TextButton( words.get(i), skin);
            table.add(button);

            button.addListener(new ClickListener() {
                @Override
                public void clicked(InputEvent event, float x, float y) {
                    Gdx.app.log("button","clicked");
                //info.setText(Integer.toString(i)); How to make this work?
//also how do I know which button is pressed?
                };
            });

        }


        table.row();
        table.add(info);


        Gdx.input.setInputProcessor(stage);

【问题讨论】:

    标签: java android libgdx tablelayout


    【解决方案1】:

    有 4 种不同的方法可以解决此问题。我推荐选项 1。我将介绍所有选项,并在最后提供完整的解决方案。

    1. 将标签声明为最终标签。 Here's a question I answered 匿名类。

      final Label info;
      //and then in your constructor initialize it...
      info = new Label("Welcome to Android",skin);
      
    2. 声明一个扩展ClickListener 的内部类并引用外部类标签实例。这不需要 info 变量是最终变量。

      public class MyLibgdxClass{
         class MyClickListener implements ClickListener{
              public void clicked(InputEvent event, float x, float y) {
                  Gdx.app.log("button","clicked");
                  info.setText(Integer.toString(i));
              };
          }
      }
      
    3. 在自己的文件中创建一个扩展 ClickListener 的类,并将您需要操作的标签传递给它。

      public class MyClickListener implements ClickListener{
          Label info;
          public MyClickListener(Label info){
              this.info = info;
          }
          public void clicked(InputEvent event, float x, float y) {
              Gdx.app.log("button","clicked");
              info.setText(Integer.toString(i));
          };
      }
      
    4. cmets 中的 Jyro117 提出了另一种方法。创建一个临时的最终变量,将其分配给您当前的标签实例,并引用该临时变量。

    我不推荐这种解决方案,我向您展示这个只是为了彻底。原因如下:

    如果您稍后重新分配标签,则需要移除所有按钮的侦听器并每次都创建新的侦听器。如果您计划重新分配您的标签,为什么不将标签声明为最终标签?这不是最好的方法。

        final Label tempLabel = info;
        button.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Gdx.app.log("button","clicked");
                tempLabel.setText(i+"");
            }
        });
    

    至于检测单击了哪个按钮,当您在循环中创建这些按钮时,在本地范围内,它们可以被声明为 final 而不会产生任何后果,因为您可能不会重新分配它们。这是我对完整解决方案的建议。

    //wherever you've declared label info, declare it as final.
    //note you'll need to initialize label in your constructor!
    final Label info;
    
    //...later on...
    for(int i=0; i<words.size();i++)
    {
        //declare button as final, so you can reference it in clicklistener
        final TextButton button = new TextButton( words.get(i), skin);
        //temporary final copy of i, so you can reference in clicklistener
        final int tempI = i;
        table.add(button);
        button.addListener(new ClickListener() {
             @Override
             public void clicked(InputEvent event, float x, float y) {
                 Gdx.app.log("button","clicked");
                 info.setText(tempI+""); //How to make this work?
                 //also how do I know which button is pressed?
                 Gdx.app.log("button",button.getText()+" was pressed.");    
         }
     });
    

    【讨论】:

    • 您缺少关于在循环内访问 i 变量的关键部分。这只需要在循环内设置最终临时变量的技巧。
    • 如果标签被重新分配,这很危险,因为侦听器将使用不同的标签实例。您需要注销旧侦听器,并在每次标签更改时注册一个新侦听器。听起来很乱是不是?如果标签不应该被重新分配,它需要是最终的。
    • 啊,但您没有更改标签的实例。您正在使用相同的实例,但将引用设为 final,以便您可以在匿名类中使用(Java 以这种方式工作真的很痛苦)。我将发布一个解决方案,以便您了解我的意思,因为我看不到如何轻松地在评论中显示代码:(。
    • 您忽略了 OP 代码中未声明的标签。这意味着他可能正在其他地方使用它。这意味着它应该在那里被宣布为最终的。无论如何,我已经在回答中将变量声明为 final。
    【解决方案2】:

    William Morrison 列出了许多创建 ClickListener 的方法。我自己更喜欢匿名类的方法,我知道这是您问题中使用的方法。

    您需要将匿名类之外的所有引用设置为 final,以便您可以在内部引用它们。这就是 java 确保引用不会改变的方式。查看我的代码(和 cmets)以获得完整的解决方案,我只需要更改几件事。 (我的皮肤文件也位于“skin/uiskin.json”,因此如果您希望使用此代码,请记住这一点)。

    import com.badlogic.gdx.ApplicationListener;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.scenes.scene2d.InputEvent;
    import com.badlogic.gdx.scenes.scene2d.Stage;
    import com.badlogic.gdx.scenes.scene2d.ui.Label;
    import com.badlogic.gdx.scenes.scene2d.ui.Skin;
    import com.badlogic.gdx.scenes.scene2d.ui.Table;
    import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
    import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class ClickTest implements ApplicationListener {
        private Stage stage;
        private Skin skin;
    
        @Override public void create() {
            Gdx.app.log("CREATE", "App Opening");
    
            this.skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
            stage = new Stage();
            Gdx.input.setInputProcessor(stage);
            Table table = new Table();
            table.setFillParent(true);
            stage.addActor(table);
    
            String sentence = "One two three four five six seven eight";
            String[] temp = sentence.split(" ");
            List<String> words = new ArrayList<String>(Arrays.asList(temp));
    
            final Label info = new Label("Welcome to Android!", skin);
    
            for (int i = 0; i < words.size(); i++) {
                // make i final here so you can reference it inside
                // the anonymous class
                final int index = i; 
    
                TextButton button = new TextButton(words.get(i), skin);
                table.add(button);
    
                button.addListener(new ClickListener() {
                    @Override public void clicked(InputEvent event, float x, float y) {
                        // When you click the button it will print this value you assign.
                        // That way you will know 'which' button was clicked and can perform
                        // the correct action based on it.
                        Gdx.app.log("button", "clicked " + index);  
    
                        info.setText(Integer.toString(index));
                    };
                });
            }
    
            table.row();
            // Changed this so it actually centers the label.
            table.add(info).colspan(words.size()).expandX(); 
    
            Gdx.input.setInputProcessor(stage);
    
            Gdx.gl20.glClearColor(0f, 0f, 0f, 1);
        }
    
        @Override public void render() {
            this.stage.act();
            Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
            Gdx.gl20.glEnable(GL20.GL_BLEND);
            this.stage.draw();
        }
    
        @Override public void dispose() {
            Gdx.app.log("DISPOSE", "App Closing");
        }
    
        @Override public void resize(final int width, final int height) {
            Gdx.app.log("RESIZE", width + "x" + height);
            Gdx.gl20.glViewport(0, 0, width, height);
            this.stage.setViewport(width, height, false);
        }
    
        @Override public void pause() { }
    
        @Override public void resume() { }
    }
    

    【讨论】:

    • 他的标签可以在其他地方访问。如果是这样,创建最终的本地实例对他不起作用。在他的原始代码中,标签是简单声明的,只是初始化的。这让我觉得你的代码示例对他不起作用。你完全忽略了这一点。
    【解决方案3】:

    我更喜欢这个版本

    public class MyClass{
    
       float test;    
       TextButton button;
       public void method(){
          button.addListener(new ClickListener() {
                @Override
                public void clicked(InputEvent event, float x, float y) {
                     MyClass.this.test = 10.0f;
                };
          });
       }
    
    }
    

    【讨论】:

    • 为什么你更喜欢这个版本?
    【解决方案4】:

    我为此类目标找到的最简单的策略是创建自己的自定义侦听器类。这样我就可以通过构造函数传递任何我需要传递的东西——包括对我要添加侦听器的对象的引用。

    for(int i = 0; i < buttonArray.length; ++i) {    
        buttonArray[i].addListener(new CustomListener(buttonArray[i]));
    }
    

    创建一个内部类:

    public class CustomListener extends ClickListener {
    
        Object listeningObject;
    
        public CustomListener(Object listeningObject) {
            this.listeningObject = listeningObject;
        }
    
        @Override
        public void clicked(InputEvent event, float x, float y) {
            ((Button)listeningObject).useAButtonMethod();
        }
    }
    

    您可以使用这种通用策略通过构造函数将任何内容传递给自定义侦听器,无论是否最终。

    PS:显然“useAButtonMethod()”不是一种方法,而是表明你可以使用任何你想要的Button方法。

    编辑:在您的情况下,您可能可以执行以下操作:

    for (int i = 0; i < words.size(); i++)
        // your stuff here.
        button.addListener(new CustomListener(info, i));
    }
    

    创建一个内部类:

    public class CustomListener extends ClickListener {
    
        Label label;
        int index;
    
        public CustomListener(Label label, int index) {
            this.label = label;
            this.index = index;
        }
    
        @Override
        public void clicked(InputEvent event, float x, float y) {
            Gdx.app.log("button", "clicked " + index);
            label.setText(Integer.toString(index));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 2017-08-28
      • 2013-07-23
      • 1970-01-01
      • 2022-09-27
      • 2013-09-24
      相关资源
      最近更新 更多