旋转后如何正确恢复视图状态

how to properly restore view state after rotations

我正在尝试手动处理 complex view 的旋转,包括恢复适当的位置和大小。目前,我正在尝试在 onLayout 中进行(更好的想法是 velcome)。有时效果很好,但通常第一次旋转放错地方或绘制的视图没有孩子。

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);
    }
  }

在这段代码中,我尝试在旋转后恢复初始stte,当调用onLayout然后将其移动到place时,视图在旋转之前(有4种状态,屏幕外到左,屏幕外到右侧,屏幕顶部或右下角)。

编辑:

<?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>

样本activityxml

<?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>

如果您想深入了解 View 的布局,您的 ViewActivity

不需要这个
 android:configChanges="keyboardHidden|orientation|screenSize"

如果在旋转后设置,android 不太关心是否重新创建您的 Activity 因此重新绘制、重新测量、重新定位等。

因此,如果您在轮换后以 ImageView 位置 [10 100] 启动您的应用程序,男孩,您的 ImageView 将被假定为放置 [10 100] 有时它甚至可能会让您在屏幕外感到奇怪坐标。

尝试使用onConfigrationChange()覆盖方法 此方法将处理旋转。

假设您要创建自己的自定义视图。如果是这样,您应该使用 View class.

提供的标准机制
@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Restore state
}

@Override
protected Parcelable onSaveInstanceState() {
    // Save state
}

您扩展了 ViewGroup 而不是 View,但还没有将任何 child 添加到 xml 中的 DraggableView 或代码。我在 DraggableView 中没有看到任何 addChild 或任何 child 相关代码。您使用了 topFragmentbottomFragment 等,并且您使用 addFragmentToView()ViewGroup 内进行了片段交易。坏主意!

我知道这很难而且很费时间。但是你需要退一步思考这个视图的设计。我强烈建议您将这些 topFragment 等视为 DraggableView 的 children,并且在此视图中不要有 FragmentManager 并执行 addTransaction .

一旦您开始将片段作为 children 添加到您的 ViewGroup,许多复杂的事情就会消失。我是根据我过去的经验这么说的。 onLayout()用于将Child Views排列在ViewGroup里面。旋转设备后,将再次调用 onLayout() 并根据需要排列 child 视图。这将非常简单。一旦你开始这样概念化,拖动操作就会变得简单很多。

如果您认为 ViewGroup 中的 Framgents 作为 child 是荒谬的,请查看 ViewPager 代码 here。这使用片段作为输入,但仍将它们视为布局的 children。

  1. 尝试在 onLayout() 本身中处理与位置相关的所有内容。
  2. 不要在onSaveInstanceState()中保存位置数据。仅保存 textdrawable 数据。
  3. 始终建议不要覆盖 onCofigurationChanged()

你现在可能无法做到这一点,但在更长的时间里运行,你可能想要重构并以这种方式来做。

记住 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.

我想这是个好方法

@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
    }