6

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>
Yarh
  • 4,459
  • 5
  • 45
  • 95
  • why are u not using manifest for it..? You can ignore the rotation if you are not doing something special on rotation... – SRB Bans Oct 14 '15 at 12:59
  • @sourabhbans are you about ` android:configChanges="locale|keyboardHidden|orientation|screenSize" ` ? i use it, thats why i am asking about manual handle of view (not activity) – Yarh Oct 14 '15 at 13:01
  • Ok.. sory i misunderstood.. – SRB Bans Oct 14 '15 at 13:04
  • Did you read http://stackoverflow.com/a/3542895/1377145 ? – Hugo Gresse Oct 22 '15 at 13:01
  • @HugoGresse yes, but like i stated in bounty saveInstanceState is not called on manueal rotation handle – Yarh Oct 22 '15 at 13:09
  • can you add your manifest to the question? – Hugo Gresse Oct 22 '15 at 13:11
  • Why do you manage view restore manually inside onLayout? I think it is easier and more correct to remove android:configChanges tag from manifest and handle common case with onSaveInstanceState & onRestoreInstanceState. – Oleksandr Oct 23 '15 at 07:20
  • @Alexandr my view containe surface which show strong visual lag, when recreated on normal rotation flow. So there is no feeling that drawing goes without pause. Also youtubeplayer stops on normal rotation – Yarh Oct 23 '15 at 07:43
  • @HugoGresse I added manifest – Yarh Oct 23 '15 at 07:45
  • Can't you use Exoplayer and their AspectRatio framelayout https://github.com/google/ExoPlayer/blob/master/library/src/main/java/com/google/android/exoplayer/AspectRatioFrameLayout.java – Hugo Gresse Oct 23 '15 at 08:41
  • @HugoGresse the views task to handle view minimisation and gragging similar to youtube – Yarh Oct 23 '15 at 09:22
  • @HugoGresse are you asking how is it defined in xml? – Yarh Oct 23 '15 at 10:39
  • @HugoGresse id is definded in xml – Yarh Oct 23 '15 at 12:03
  • If your onRestoreInstanceState is not called make sure you have overrode a proper method onRestoreInstanceState(Bundle savedInstanceState) not a onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState). The second one is called only on API23 – forcelain Oct 28 '15 at 05:04
  • Awesome stuff. Are you the same person who forked the original project? – Sufian Feb 17 '16 at 13:15

5 Answers5

2

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.

Elltz
  • 10,730
  • 4
  • 31
  • 59
1

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.

Henry
  • 17,490
  • 7
  • 63
  • 98
0

Try to use onConfigrationChange() override method this method will handle the rotation.

frogatto
  • 28,539
  • 11
  • 83
  • 129
S. Alawadi
  • 144
  • 1
  • 6
0

Assuming you're creating your own custom view. If so, you should use standard mechanisms provided by View class.

@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Restore state
}

@Override
protected Parcelable onSaveInstanceState() {
    // Save state
}
frogatto
  • 28,539
  • 11
  • 83
  • 129
0

I guess this is a good way

@Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        return new YourParcelableObject(superState, yourValues);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        YourParcelableObject savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());

        //restote other values from YourParcelableObject
    }
Eugene Kuzmenko
  • 1,216
  • 10
  • 12