Search code examples
androidandroid-layoutbuttonrandomposition

How to set a button's position to random points on screen?


I am trying to make a button appear in random positions on the screen but it keeps going out for some reason. I have tried to play around with the values but it doesn't seem to solve the issue. I searched a lot about this and even tried to follow this but it didn't work. I use the following block of code in the onCreate method of the Activity to implement it.

setContentView(R.layout.button_press);

        button = findViewById(R.id.my_button);
        button.setClickable(true);
        button.setOnClickListener(this);

        final Button button = (Button) findViewById(R.id.my_button);
        displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        loOPenArea = (RelativeLayout) findViewById(R.id.open_area_press);

        int leftMargin = new Random().nextInt(displaymetrics.widthPixels - 5*button.getWidth());
        int topMargin = new Random().nextInt(displaymetrics.heightPixels - 5*button.getHeight());

        ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) button.getLayoutParams();
        p.setMargins(leftMargin, topMargin, 0, 0);

        button.setLayoutParams(p);

And here's the code for the layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ButtonPress"
    android:background="#03B0F5"
    >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/open_area_press">

    <TextView
        android:id="@+id/button_press"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:background="#FFFFFF"
        android:text="@string/button_press"
        android:textSize="50sp"
        android:textColor="#03B0F5"

        />

    <androidx.appcompat.widget.AppCompatButton
        android:id = "@+id/my_button"
        android:layout_width = "150dp"
        android:layout_below="@+id/button_press"
        android:height="150dp"
        android:layout_height = "wrap_content"
        android:background="@drawable/rounded_corners"
        android:text = "Press Here"
        android:textColor="#03B0F5"
        android:textSize="20sp"
        />



</RelativeLayout>

</LinearLayout>

Either sometimes the button doesn't show on the screen at all or it gets squeezed in some place(see bottom right corner):

enter image description here

Making a button appear on a random screen position seems like a trivial task, but I've been trying to solve it for days and look for solutions but none of the solutions seems to work perfectly.


Solution

  • If the code that you posted is called in onCreate() then it is called too soon. You will have to wait for the first layout to determine the placement and size of the button that is being shifted. You can do this with a ViewTreeObserver.OnGlobalLayoutListener.

    // Wait until the first layout to get buton size and placement to compute x/y placement range.
    button.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            button.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            // Capture width and x/y placement ranges here
        }
    }
    

    The second issue is the calculation of the margins. I assume that you want the button to be placed anywhere from its initial position to the right and to the bottom of the screen but not so far that the button is cut off. The calculation would look something like this.

    // Button will have a horizontal margin from zero to the width of its parent less
    // the width of the button less the initial left side of the button which is likely
    // to be the initial margin.
    buttonMaxXMargin = loOPenArea.getWidth() - button.getWidth() - button.getLeft();
    // Similar for the vertical placement.
    buttonMaxYMargin = loOPenArea.getHeight() - button.getHeight() - button.getTop();
    

    This code must run after the global layout listener runs.

    The controlling view regarding the placement of the button is its parent open_area_press. The button will move within the bounds of its parent from its initial x/y position to a maximum position that is the width of the parent less the button's width and the height of the parent less the button's height.

    If you introduce these ideas into your code, you should have a happier outcome.