Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to properly restore view state after rotations

I am tring to handle rotation for complex view manually, including restoring the propper position and size. At this moment i am trying to do it in onLayout (better ideas are velcome). Sometimes it works good, but often first rotation is misplaces or view is drawn without childs.

private int oldOrientation = -1;

  /**
   * Override method to configure the dragged view and secondView layout properly.
   */
  @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    Log.e("mylayout", "onLayout " + df.format(new Date(Calendar.getInstance().getTimeInMillis())));
    if (isInEditMode()) {
      super.onLayout(changed, left, top, right, bottom);
    } else {
      dragView.setVisibility(INVISIBLE);
      if (isDragViewAtTop() && (oldOrientation != getResources().getConfiguration().orientation || oldOrientation == -1)) {
        dragView.layout(left, top, right, transformer.getOriginalHeight());
        secondView.layout(left, transformer.getOriginalHeight(), right, bottom);
        ViewHelper.setY(dragView, top);
        ViewHelper.setY(secondView, transformer.getOriginalHeight());
        ViewHelper.setX(dragView, left);
        ViewHelper.setX(secondView, left);
        oldOrientation = getResources().getConfiguration().orientation;
      } else if (isClosedAtLeft() && (
          oldOrientation != getResources().getConfiguration().orientation
              || oldOrientation == -1)) {
        dragView.layout(left, top, right, transformer.getOriginalHeight());
        secondView.layout(left, transformer.getOriginalHeight(), right, bottom);
        ViewHelper.setY(dragView, top);
        ViewHelper.setY(secondView, transformer.getOriginalHeight());
        ViewHelper.setX(dragView, left);
        ViewHelper.setX(secondView, left);
        closeToLeft();
        oldOrientation = getResources().getConfiguration().orientation;
      } else if (isClosedAtRight() && (
          oldOrientation != getResources().getConfiguration().orientation
              || oldOrientation == -1)) {
        dragView.layout(left, top, right, transformer.getOriginalHeight());
        secondView.layout(left, transformer.getOriginalHeight(), right, bottom);
        ViewHelper.setY(dragView, top);
        ViewHelper.setY(secondView, transformer.getOriginalHeight());
        ViewHelper.setX(dragView, left);
        ViewHelper.setX(secondView, left);
        closeToRight();
        oldOrientation = getResources().getConfiguration().orientation;
      } else if ((oldOrientation != getResources().getConfiguration().orientation
          || oldOrientation == -1)) {
        dragView.layout(left, top, right, transformer.getOriginalHeight());
        secondView.layout(left, transformer.getOriginalHeight(), right, bottom);
        ViewHelper.setY(dragView, top);
        ViewHelper.setY(secondView, transformer.getOriginalHeight());
        ViewHelper.setX(dragView, left);
        ViewHelper.setX(secondView, left);
        smoothSlideTo(SLIDE_BOTTOM);
        oldOrientation = getResources().getConfiguration().orientation;
      }
      dragView.setVisibility(VISIBLE);
    }
  }

In this code, i try to restore initial stte after rotation, when onLayout is called and then move it to place, the view was before rotation (there are 4 states, out of creen to left, out of screen to right, top of scren or bottom right corner).

Edit:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="com.github.pedrovgs.sample"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-sdk android:minSdkVersion="8" />

    <!-- Permissions -->

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <!-- Application configuration -->

    <application
        android:name=".DraggablePanelApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <!-- Maps API KEY -->

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyC1rMU-mkhoyTvBIdTnYU0dss0tU9vtK48" />

        <!-- Main Activity -->

        <activity
            android:name=".activity.MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Places sample -->

        <activity
            android:name=".activity.PlacesSampleActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"

            android:label="@string/places_sample_activity_title" />

        <!-- TV Shows sample -->

        <activity
            android:name=".activity.TvShowsActivity"
            android:label="@string/tv_shows_sample_activity_title" />


        <!-- Youtube Sample -->

        <activity
            android:name=".activity.YoutubeSampleActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"

            android:label="@string/youtube_sample_activity_title" />


        <!-- Video Sample -->

        <activity
            android:name=".activity.VideoSampleActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"

            android:label="@string/video_sample_activity_title" />

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>

sample activity xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:draggable_panel="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fl_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <!-- Movie Thumbnail -->

  <ImageView
      android:id="@+id/iv_thumbnail"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      style="@style/image_view"/>

  <!-- DraggablePanel -->

  <com.github.pedrovgs.DraggablePanel
      android:id="@+id/draggable_panel"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      draggable_panel:x_scale_factor="@dimen/x_scale_factor"
      draggable_panel:y_scale_factor="@dimen/y_scale_factor"
      draggable_panel:top_fragment_height="@dimen/top_fragment_height"
      draggable_panel:top_fragment_margin_right="@dimen/top_fragment_margin"
      draggable_panel:top_fragment_margin_bottom="@dimen/top_fragment_margin"
      draggable_panel:enable_horizontal_alpha_effect="false"/>

</FrameLayout>
like image 718
Yarh Avatar asked Oct 14 '15 12:10

Yarh


2 Answers

If you are trying to go deep in Laying out your Views you do not need this for your View's Activity

 android:configChanges="keyboardHidden|orientation|screenSize"

if that is set after rotation, android care less of whether to recreate your Activity hence re-drawing,-re-measuring,-re-positioning re-etc-etc.

so if you start your app with an ImageView placement [10 100] after rotation boy your ImageView will be assumed to be placed [10 100] sometimes it might even give you weird offscreen co-ordinates.

like image 60
Elltz Avatar answered Oct 25 '22 18:10

Elltz


You have extended a ViewGroup instead of a View and yet haven't added any child to the DraggableView in the xml or in the code. I don't see any addChild or any child related code in DraggableView. You have used topFragment, bottomFragment, etc.. and you used addFragmentToView() to make fragment transaction inside the ViewGroup. Bad idea!

I know it's hard and time consuming. But you need to step back and think about the Design of this view. I would strongly suggest you treat these topFragment,etc.. as children to the DraggableView, and not have a FragmentManager inside this View and perform addTransaction.

Once you start adding the fragments as children to your ViewGroup, lot of complications will just go away. I say this from my past experience. onLayout() is used to arrange the Child Views inside the ViewGroup. Once you rotate the device, onLayout() will again be called and will arrange the child views as you want. It would be very simple. Dragging operation will be made much simple once you start conceptualizing like this.

If you think having Framgents as child in the ViewGroup is absurd, kindly look at the ViewPager code here. This uses fragments as input but still treat them as children to the layout.

  1. Try to handle all stuff related to position in onLayout() itself.
  2. Do not save position data in onSaveInstanceState(). Save only the text or drawable data in this.
  3. It's always advisable not to override onCofigurationChanged().

You may not be able to do this now, but in the longer run, you might want to refactor and do it this way.

Remember the true purpose of onLayout():

Called from layout when this view should assign a size and position to each of its children. Derived classes with children should override this method and call layout on each of their children.

like image 28
Henry Avatar answered Oct 25 '22 19:10

Henry