getLocationOnScreen returns old position after rotation

2k Views Asked by At

What I have:

  • An activity with android:configChanges="orientation|screenSize|keyboardHidden"
  • A DialogFragment on it
  • ViewA on DialogFragment

What's the problem:

I'm using ViewA.getLocationOnScreen to get the location on the screen of the view. when I first open the dialog the position is correct. After I rotate the screen, because of android:configChanges the view somehow doesn't update it's position and even if the dialog is correctly centered in the activity the getLocationOnScreen of ViewA points to the older location, before the rotation.

What I've tried.

I overwrote the onConfigurationChanged of the dialog and tried this:

  • ViewA.requestLayout (doesn't do anything)
  • ViewA.getViewTreeObserver().addOnGlobalLayoutListener and on the onGlobalLayout set the topMargin to 1 and call requestLayout again. (this worked but I don't want to set the margin every time I rotate the screen)

What I want to know is how can I force the reposition of the dialog, making getLocationOnScreen return the correct values after a rotation

Note that I don't want to change android:configChanges

2

There are 2 best solutions below

6
On

The view's location on screen has not yet been updated when onConfigurationChanged is called. You need to add an OnLayoutChangeListener to the view to catch the updates you're looking for. See the example below.

TestDialogFragment.java

public class TestDialogFragment extends DialogFragment {

    private static final String TAG = "TestDialogFragment";

    View testView;
    int[] testViewLocation = {0, 0};

    public TestDialogFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.test_fragment, container);
        testView = view.findViewById(R.id.test_view);
        testView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                Log.d(TAG, "onLayoutChange");
                testView.getLocationOnScreen(testViewLocation);
                Log.d(TAG, String.format("%s %s", testViewLocation[0], testViewLocation[1]));
            }
        });
        return view;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged");
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Log.d(TAG, "landscape");
        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
            Log.d(TAG, "portrait");
        }

        testView.getLocationOnScreen(testViewLocation);
        Log.d(TAG, String.format("%s %s", testViewLocation[0], testViewLocation[1]));
    }

}

test_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/test_view"
        android:layout_height="50dp"
        android:layout_width="match_parent"
        android:layout_gravity="center"/>

</FrameLayout>

Log Output

06-24 16:20:05.682  D/TestDialogFragment﹕ onConfigurationChanged
06-24 16:20:05.682  D/TestDialogFragment﹕ portrait
06-24 16:20:05.682  D/TestDialogFragment﹕ 504 601
06-24 16:20:05.852  D/TestDialogFragment﹕ onLayoutChange
06-24 16:20:05.852  D/TestDialogFragment﹕ 84 1021
06-24 16:20:08.695  D/TestDialogFragment﹕ onConfigurationChanged
06-24 16:20:08.695  D/TestDialogFragment﹕ landscape
06-24 16:20:08.695  D/TestDialogFragment﹕ 84 1021
06-24 16:20:08.865  D/TestDialogFragment﹕ onLayoutChange
06-24 16:20:08.865  D/TestDialogFragment﹕ 504 601
06-24 16:20:13.550  D/TestDialogFragment﹕ onConfigurationChanged
06-24 16:20:13.550  D/TestDialogFragment﹕ portrait
06-24 16:20:13.550  D/TestDialogFragment﹕ 504 601
1
On

To try to explain, if you have this android:configChanges="orientation no recreation of Activity will take place, which means Views will not be measured, that is why the old positions are being reported, also to request a full draw on your View you need o call it on the DialogFrament's View parent so for the overall game your DecorView- call invalidate() & requestLayout()