16

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

Pedro Oliveira
  • 20,442
  • 8
  • 55
  • 82
  • Are you able to post some of your code? Specifically the code related to _when_ you are checking `getLocationOnScreen`. [This](http://stackoverflow.com/questions/13243914/getlocationonscreen-in-oncreate-just-returns-zeroes) isn't quite your issue but I wonder if it has a similar cause. – mpkuth Jun 24 '15 at 22:19
  • No that's not he case. The view is already drawn and positioned correctly. The problem is with `android:configChanges` that disables the activity recreation. In this case, since the activity isn't destroyed, the dialog isn't destroyed either. That's why the position of the view remains the old one. No code is needed since this is easy to reproduce. – Pedro Oliveira Jun 24 '15 at 22:21
  • Did you end up finding a solution for this? – Nick Korostelev Apr 04 '18 at 22:09
  • If I remember correctly, I had to set a margin twice (e.g. set to 1 and set back to 0) before getting the location on screen. – Pedro Oliveira Apr 04 '18 at 22:12

2 Answers2

0

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()

Elltz
  • 10,730
  • 4
  • 31
  • 59
  • Invalidating and requesting layout on `getActivity().findViewById(R.id.container)` or `DialogFragment.getView().getRootView()` doesn't work. As for the rest of the answer. Yes I already knew that and already said that on the comment section of the question. – Pedro Oliveira Jun 29 '15 at 09:28
-1

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
mpkuth
  • 6,994
  • 2
  • 27
  • 44
  • Thanks for reminding me what a global layout listener is but like I said to you this doesn't solve the problem. Have you read the question? Check "what I've tried" number 2 – Pedro Oliveira Jun 25 '15 at 08:26
  • Also for all that's worth I'm using onClickListener to dump the view position. The view is already drawn and ready when I get its position – Pedro Oliveira Jun 25 '15 at 09:04
  • I have read the question and in "what I've tried" number 2 you noted that you had tried a global layout listener and said that it worked except that you were forced to always set a margin and then call request layout again. My answer uses the global layout listener but you do NOT need to reset the margin and request an extra layout. – mpkuth Jun 30 '15 at 18:51
  • Yes. But it will not work without setting the margin. The layout will just remain the same – Pedro Oliveira Jun 30 '15 at 18:53
  • Ah, I think I understand the issue now. I read your question as having an issue getting an updated value for the view's location on the screen. My answer gives you the correct, updated value for that but your issue is that although the view re-positions itself, it does not re-size itself to match the parent width without manual intervention? – mpkuth Jun 30 '15 at 19:05
  • No. Because the parent layout is the same. Calling requestLayout, invalidate or whatever doesn't work because the view theoretically hasn't changed. Only by setting a margin on the parent layout I was able to get the real position. The view is draw correctly. But its view position is the same as the pre rotation layout – Pedro Oliveira Jun 30 '15 at 19:08