旋转后如何正确恢复视图状态
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
的布局,您的 View
的 Activity
不需要这个
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 相关代码。您使用了 topFragment
、bottomFragment
等,并且您使用 addFragmentToView()
在 ViewGroup
内进行了片段交易。坏主意!
我知道这很难而且很费时间。但是你需要退一步思考这个视图的设计。我强烈建议您将这些 topFragment
等视为 DraggableView
的 children,并且在此视图中不要有 FragmentManager
并执行 addTransaction
.
一旦您开始将片段作为 children 添加到您的 ViewGroup
,许多复杂的事情就会消失。我是根据我过去的经验这么说的。 onLayout()
用于将Child Views
排列在ViewGroup
里面。旋转设备后,将再次调用 onLayout()
并根据需要排列 child 视图。这将非常简单。一旦你开始这样概念化,拖动操作就会变得简单很多。
如果您认为 ViewGroup
中的 Framgents
作为 child 是荒谬的,请查看 ViewPager
代码 here。这使用片段作为输入,但仍将它们视为布局的 children。
- 尝试在
onLayout()
本身中处理与位置相关的所有内容。
- 不要在
onSaveInstanceState()
中保存位置数据。仅保存 text
或 drawable
数据。
- 始终建议不要覆盖
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
}
我正在尝试手动处理 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
的布局,您的 View
的 Activity
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 相关代码。您使用了 topFragment
、bottomFragment
等,并且您使用 addFragmentToView()
在 ViewGroup
内进行了片段交易。坏主意!
我知道这很难而且很费时间。但是你需要退一步思考这个视图的设计。我强烈建议您将这些 topFragment
等视为 DraggableView
的 children,并且在此视图中不要有 FragmentManager
并执行 addTransaction
.
一旦您开始将片段作为 children 添加到您的 ViewGroup
,许多复杂的事情就会消失。我是根据我过去的经验这么说的。 onLayout()
用于将Child Views
排列在ViewGroup
里面。旋转设备后,将再次调用 onLayout()
并根据需要排列 child 视图。这将非常简单。一旦你开始这样概念化,拖动操作就会变得简单很多。
如果您认为 ViewGroup
中的 Framgents
作为 child 是荒谬的,请查看 ViewPager
代码 here。这使用片段作为输入,但仍将它们视为布局的 children。
- 尝试在
onLayout()
本身中处理与位置相关的所有内容。 - 不要在
onSaveInstanceState()
中保存位置数据。仅保存text
或drawable
数据。 - 始终建议不要覆盖
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
}