如何在安卓系统中动态地设置布局

16
假设有一个名为MainActivity的活动和两个名为layout1layout2的布局,两个布局中都有一些按钮。 默认情况下,MainActivity布局是以下的layout1:
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout1);

我所做的是通过在 layout1 中点击一个按钮,将第二个布局设置如下:

    someBtn1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            setContentView(R.layout.layout2);
        }
    });

layout2中,还有一个按钮可以返回到layout1,如下所示:
    someBtn2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            setContentView(R.layout.layout1);
        }
    });

问题是当我返回到 layout1 时, someBtn1 的 OnClickListener 就不能正常工作了。似乎我需要再次为 layout1 中的 someBtn1 设置 OnClickListener。 我该如何编写代码以确保它们能够完美地运行,并符合最佳实践?
5个回答

26

最佳做法是使用片段而不是更改内容视图。

在您的代码中,使用布局的setContentView会每次重新创建(膨胀)所有视图,因此在someBtn2点击侦听器中调用setContentView(R.layout.layout1)将创建一个没有关联侦听器的新按钮。

如果您不想使用片段,可以这样做:

private View view1, view2;

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  view1 = getLayoutInflater().inflate(R.layout.layout1, null);
  view2 = getLayoutInflater().inflate(R.layout.layout2, null);
  setContentView(view1);

听众将会是:

someBtn1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        setContentView(view2);
    }
});


someBtn2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        setContentView(view1);
    }
});

我想学习片段的方式,你有什么快速示例吗? - Piovezan
请阅读此链接:http://developer.android.com/guide/components/fragments.html,并在SDK示例和Android支持库中查找示例(两者均可通过Android SDK Manager下载)。 - spacifici
如何使底部返回按钮返回到上一个布局而不是上一个活动? - Žilvinas
如果button1和button2在两个视图中具有相同的ID怎么办?我实际上正在为这种情况而苦苦挣扎。我的两个视图上实际上有多个按钮,如果它们执行相同的工作,我不想为每个按钮编写不同的监听器。有没有解决方案? - Nadeem Shaikh
你可以从充气视图中获取按钮(view1.findViewById(...)),然后将一个监听器分配给它,该监听器仅检查引用(view == view1)。 - spacifici

5
如果您只是想玩弄您当前的代码,解决您问题的方法是当布局更改时必须重新声明监听器,如下所示:
someBtn1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        setContentView(R.layout.layout2);

        someBtn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setContentView(R.layout.layout1);
            }
        });
    }
});

someBtn2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        setContentView(R.layout.layout1);

        someBtn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setContentView(R.layout.layout2);
            }
        });
    }
});

避免重复声明监听器的另一种方法是声明两个方法来处理布局更改,并在每个布局中使用按钮的onClick属性,例如:

public void setLayout1(View view) {
    setContentView(R.layout.layout1);
}

public void setLayout2(View view) {
    setContentView(R.layout.layout2);
}

layout1.xml 文件中:
<Button
    android:id="@+id/someBtn1"
    android:onClick="setLayout2"/>

layout2.xml中:
<Button
    android:id="@+id/someBtn2"
    android:onClick="setLayout1"/>

然而,如果你想遵循最佳实践,最好不要在同一个Activity中混合不同的布局。相反,声明两个不同的Activity(每个Activity有自己的布局),并根据点击的按钮调用其中一个Activity。假设你在Activity1中,想要调用Activity2,然后返回到Activity1:
在 Activity1.java 文件中:
someBtn1.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         startActivity(new Intent(this, Activity2.class));
     }
 });

Activity2.java 中:
someBtn2.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         finish();
     }
 });

是的,我知道。但是由于某种原因,我想在同一个活动中动态地使用不同的布局。感谢您的建议。 - iamcrypticcoder
1
我已经改进了我的答案,避免了重复声明监听器,请看一下。 - Piovezan

1

一种做法是在 onCreate(...) 中加载两个视图,然后通过将你不想要的那个视图设置为不可见来在它们之间切换。类似以下这样:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    LayoutParams default_layout_params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    View view1 = inflater.inflate(R.layout.layout1, null);
    addContentView(view1, default_layout_params);           
    View view2 = inflater.inflate(R.layout.layout2, null);
    addContentView(view2, default_layout_params);
    view2.setVisibility(View.INVISIBLE);
    view1.setVisibility(View.VISIBLE);
    view1.bringToFront();

谢谢你的代码。实际上,我需要在一个活动中切换3-4个布局。这就是为什么我还是很困惑应该使用什么。在这种情况下,使用Fragment更好吗? - iamcrypticcoder
我刚刚发布了我已经成功使用了两个布局的代码。我相信这种方法可以适用于更多的布局,但是“更好”和“最好”的选择是主观的,所以我将把选择留给您! - Stochastically

0

当您设置layout2时,您还应该设置OnClickListener到someBtn1,反之亦然,我建议像这样做。但是,如先前的答案所述,通常应避免以这种方式混合布局。

public class MainActivity extends Activity {

    private final View.OnClickListener setLayout1Listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
           setContentView(R.layout.layout2);
           ((Button)findViewById(R.id.Btn2Id)).setOnClickListener(setLayout2Listener);
           //do other stuff
        }
    };

    private final View.OnClickListener setLayout2Listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
           setContentView(R.layout.layout1);
           ((Button)findViewById(R.id.Btn1Id)).setOnClickListener(setLayout1Listener);
           //do other stuff
        }
    };



    @Override
    public void onCreate(final Bundle savedInstance) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout1);
        ((Button)findViewById(R.id.Btn1Id)).setOnClickListener(setLayout1Listener);
        //do other stuff
    }
}

0

在回调layout1时,数据必须重新设置。

您可以将两个布局合并为一个,然后使用ViewFlipper在它们之间进行切换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接