使用LayoutParams.addRule动态设置视图位置,将其设置为视图的最后位置

5
我正在动态地向相对布局中添加视图,并以编程方式定义它们。这些视图可以在屏幕上移动,因此它们的位置会发生变化。
当我尝试将一个视图(button2)设置为另一个视图(button1)下方时,button2会被放置在button1的旧位置(视图在移动之前默认的位置)。我已经链接了图片,希望能更好地传达这一点。
这是原始布局Original Layout 重新定位Button2后的布局Re-positioned Layout 如果有LinkedList来跟踪布局中所有视图更改和视图属性,则可能会有所不同。
以下是代码函数:
我如何重新定位Button1:
Buttons b = (Buttons) viewIndex;
                                positioningLayout = new RelativeLayout.LayoutParams(b.getLayoutParams());
                                positioningLayout.addRule(RelativeLayout.CENTER_VERTICAL);
                                b.setLayoutParams(positioningLayout);
                                baseLayout.addView(b);

重新定位视图在另一个视图下方 代码片段:
Buttons b = (Buttons) viewIndex;
                                positioningLayout = new RelativeLayout.LayoutParams(b.getLayoutParams());
                                positioningLayout.addRule(RelativeLayout.BELOW, viewIdFromList.intValue());
                                b.setLayoutParams(positioningLayout);
                                b.invalidate();

我将添加视图到布局的方法展示如下。
uiList.addView(new Buttons(this), "BUTTON");
            setDialogView(uiList.getLast());
            showDialog(SET_ID);
            reloadUI();

setDialogView只是将视图传递给Dialog SET_ID,以便我可以手动为视图分配ID(用于测试)。 reloadUI()只需找到添加到背景LinkedList中的最后一个视图,并使用.addView将其添加到relativeLayout中;
如果需要更多的代码,请告诉我。在更改relativeLayout子视图后,我是否漏掉了更新视图布局的调用?似乎视图在视觉上重新定位,但实际的LayoutParams没有更新,因此当您将Button2设置为Button1时,它会获得旧位置。
有没有一种方法可以强制相对布局视图重新定位?
3个回答

4

我认为你应该尝试一个简单的解决方案 - 将b.invalidate()替换为b.requestLayout()。如果需要更多背景知识,请查看此链接。我没有测试过,但我希望它能够工作。所以代码应该是这样的:

Buttons b = (Buttons) viewIndex;
positioningLayout = new RelativeLayout.LayoutParams(b.getLayoutParams());
positioningLayout.addRule(RelativeLayout.BELOW, viewIdFromList.intValue());
b.setLayoutParams(positioningLayout);
b.requestLayout();

也许你可以将其简化为以下内容:
Buttons b = (Buttons) viewIndex;
((RelativeLayout.LayoutParams) b.getLayoutParams()).addRule(RelativeLayout.BELOW, viewIdFromList.intValue());
b.requestLayout();

这个方法有效,谢谢!你能解释一下为什么requestlayout()可以工作而invalidate()不行吗?我原本以为调用requestlayout()时不会刷新视图。 - Sevros
当视图在父布局中的位置或边界发生变化时,应该调用requestLayout(),而当视图的外观发生变化时,则应该调用invalidate()。一方面,当您调用requestLayout()时,视图的onLayout()onMeasure()方法将被触发;另一方面,当您调用invalidate()时,onDraw()方法将被触发。 - romtsn
所以,invalidate() 只是重新绘制视图本身,而 requestLayout() 则更新视图的位置和边界。不用谢,期待您的奖励 :D - romtsn
谢谢,我以为invalidate也会处理layoutParams。这是很好的信息!Stack Overflow让我等待9个小时才能颁发赏金 :/ - Sevros
啊,没问题:) 谢谢你的关注。 - romtsn

1
你似乎在正确的轨道上,但是犯了一个小错误。没有更多的代码,提供实际解决方案相当困难。但我会尝试指出错误。
要在RelativeLayout中以编程方式相对定位项目,您必须为每个项目分配唯一的id。
例如,
Button b = new Button(this);
b.setId(1);

Button c = new Button(this);
c.setId(2);

每个元素都应该有唯一的id。
现在,如果你想把按钮b垂直居中放置,
layout.addRule(RelativeLayout.CENTER_VERTICAL);
b.setLayoutParams(layout);

现在,如果您想将按钮c放在它下面,
layout.addRule(RelativeLayout.BELOW, b.getId());
layout.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
b.setLayoutparams(layout);

这将确保按钮c位于按钮b之下。正如您所说,您可以使用任何数据结构来跟踪每个元素的ID。


1
这只是一个关于如何正确移动按钮的详细示例。首先,为 View 创建一个唯一的 id,即在 res/values/ 中创建一个新的 XML 资源文件,并将其命名为 ids.xml:

structure

ids.xml的内容是:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="button_1" type="id"/>
    <item name="button_2" type="id"/>
</resources>

我们需要为您的主布局设置一个id:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/layout_main"> <!-- the id -->

    <!-- content of this layout -->

</RelativeLayout>

然后,让我们创建按钮:Button
    RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.layout_main);

    final Button button1 = new Button(this);
    button1.setId(R.id.button_1);
    button1.setText("BUTTON 1");
    button1.setTextColor(Color.BLACK);

    final Button button2 = new Button(this);
    button2.setId(R.id.button_2);
    button2.setText("BUTTON 2");
    button2.setTextColor(Color.BLACK);

    relativeLayout.addView(button1, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT));
    relativeLayout.addView(button2, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT));

    RelativeLayout.LayoutParams params1 = (RelativeLayout.LayoutParams) button1.getLayoutParams();
    params1.addRule(RelativeLayout.CENTER_IN_PARENT); // set to the center of screen
    button1.setLayoutParams(params1);

    final RelativeLayout.LayoutParams params2 = (RelativeLayout.LayoutParams) button2.getLayoutParams();
    params2.addRule(RelativeLayout.ALIGN_PARENT_TOP);
    params2.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
    button2.setLayoutParams(params2);

    button2.setOnClickListener(new View.OnClickListener() {
        @SuppressLint("NewApi")
        @Override
        public void onClick(View v) {
            /* We need to remove the existing rules and update it!
             * For API 16 and earlier, set the rule to 0, e.g. --> layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
             * For API 17 and higher, call layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
             */

            int api = android.os.Build.VERSION.SDK_INT;
            if (api >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // API level 17
                params2.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
                params2.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
            }else if (api < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                params2.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0);
                params2.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
            }

            params2.addRule(RelativeLayout.CENTER_IN_PARENT);
            params2.addRule(RelativeLayout.BELOW, R.id.button_1); // place below button1
            button2.setLayoutParams(params2);
        }
    });

当你点击button2时,它会移动到button1下方。


谢谢您的迅速回复,但不幸的是这并没有起作用。 - Sevros

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