Setting views dynamically with LayoutParams.addRule setting to the last position of the view

3k views Asked by At

I'm dynamically adding views to a relative layout and programatically defining them. The views can be moved around the screen so their position is changing.

When I try to set a view (button2) to sit below another view (button1), button2 gets placed in the old location of button1 (the default location of where views get added before moved). I've linked images to hopefully convey this better.

This is the Original Layout Original Layout

Layout after Button2 is re-positionedRe-positioned Layout

I have a background LinkedList keeping track of all view changes and view attributes for the layout if that makes a difference.

Here are the code functions:

How i'm re-positioning Button1:

Buttons b = (Buttons) viewIndex;
                                positioningLayout = new RelativeLayout.LayoutParams(b.getLayoutParams());
                                positioningLayout.addRule(RelativeLayout.CENTER_VERTICAL);
                                b.setLayoutParams(positioningLayout);
                                baseLayout.addView(b);

Repositioning views below another view Code fragment:

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

How I'm adding the views to the layout.

uiList.addView(new Buttons(this), "BUTTON");
            setDialogView(uiList.getLast());
            showDialog(SET_ID);
            reloadUI();

setDialogView is just passing the view to the Dialog SET_ID so that I can manually assign an ID to the view (for testing). reloadUI() just finds the last view added to the background LinkedList and adds it to the relativeLayout using .addView;

If you require more code please let me know. Am I missing a call to update the view layouts after making a change to the relativeLayout child views? It seems like the view is getting re-positioned visually but the actual LayoutParams are not updating so when you set Button2 to Button1 it's getting the old position.

Is there a way to force a relative layout view re-position?

3

There are 3 answers

4
romtsn On BEST ANSWER

I think you should try simple solution - replace b.invalidate() with b.requestLayout(). For more background check this link. I didn't test it but I hope it will work. So the code should look like:

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

Or maybe you can simplify this to:

Buttons b = (Buttons) viewIndex;
((RelativeLayout.LayoutParams) b.getLayoutParams()).addRule(RelativeLayout.BELOW, viewIdFromList.intValue());
b.requestLayout();
0
Aritra Roy On

You seem to be in the right track but is doing a small mistake. Without more code it is quite difficult to provide the actual solution. But I will try to point out the mistake.

See, to relatively position items programmatically in a RelativeLayout you must assign unique ids to each of them.

Like,

Button b = new Button(this);
b.setId(1);

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

Each element should have unique ids.

Now if you want to place the Button b vertically in the center,

layout.addRule(RelativeLayout.CENTER_VERTICAL);
b.setLayoutParams(layout);

Now if you want to place Button c below it,

layout.addRule(RelativeLayout.BELOW, b.getId());
layout.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
b.setLayoutparams(layout);

This will certainly place the button c below button b. As you said you can use a any data structure of your choice to keep track of the ids of each elements.

1
Anggrayudi H On

This is only a detail example on how you move the button correctly. So first, create a unique id for the View, i.e. create a new XML resource file in res/values/, and name it ids.xml:

structure

And the content of ids.xml is:

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

We have to set an id to your main layout:

<?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>

Then, let's create the Buttons:

    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);
        }
    });

So once you click on button2, it will move to below button1.