The*_*Vee 465 android android-fragments
我在SO上发现了许多类似问题的实例,但遗憾的是没有答案符合我的要求.
我有纵向和横向的不同布局,我正在使用后台堆栈,这两个都阻止我setRetainState()使用配置更改例程使用和技巧.
我在TextViews中向用户显示某些信息,这些信息不会保存在默认处理程序中.仅使用活动编写我的应用程序时,以下工作正常:
TextView vstup;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever);
vstup = (TextView)findViewById(R.id.whatever);
/* (...) */
}
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putCharSequence(App.VSTUP, vstup.getText());
}
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
vstup.setText(state.getCharSequence(App.VSTUP));
}
Run Code Online (Sandbox Code Playgroud)
使用Fragments,这仅适用于非常特定的情况.具体来说,可怕的破坏是替换片段,将其放入后栈,然后在显示新片段时旋转屏幕.根据我的理解,旧片段onSaveInstanceState()在被替换时没有接收到调用但是以某种方式Activity与之相关联,并且此方法稍后在其View不再存在时调用,因此将我TextView的任何结果查找为a NullPointerException.
另外,我发现保留对我的引用对于s TextViews来说并不是一个好主意Fragment,即使它是正常Activity的.在这种情况下,onSaveInstanceState()实际上保存状态但是如果我在隐藏片段时旋转屏幕两次,则问题会重新出现,因为它onCreateView()不会在新实例中被调用.
我想把状态保存onDestroyView()到某个Bundle类型的类成员元素中(它实际上是更多的数据,而不仅仅是一个TextView)并保存它,onSaveInstanceState()但还有其他缺点.首先,如果该片段是当前显示,调用两个函数的顺序是相反的,所以我需要考虑两种不同的情况.必须有一个更清洁,更正确的解决方案!
Tha*_*hHH 516
要正确保存实例状态,Fragment请执行以下操作:
1.在片段中,通过覆盖onSaveInstanceState()和恢复来保存实例状态onActivityCreated():
class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
if (savedInstanceState != null) {
//Restore the fragment's state here
}
}
...
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's state here
}
}
Run Code Online (Sandbox Code Playgroud)
2.而且很重要的一点,在活动中,你必须保存片段的实例在onSaveInstanceState()和恢复onCreate().
class MyActivity extends Activity {
private MyFragment
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
//Restore the fragment's instance
mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
...
}
...
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
}
}
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.
The*_*Vee 82
这就是我现在使用的方式......它非常复杂但至少它可以处理所有可能的情况.如果有人有兴趣.
public final class MyFragment extends Fragment {
private TextView vstup;
private Bundle savedState = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.whatever, null);
vstup = (TextView)v.findViewById(R.id.whatever);
/* (...) */
/* If the Fragment was destroyed inbetween (screen rotation), we need to recover the savedState first */
/* However, if it was not, it stays in the instance from the last onDestroyView() and we don't want to overwrite it */
if(savedInstanceState != null && savedState == null) {
savedState = savedInstanceState.getBundle(App.STAV);
}
if(savedState != null) {
vstup.setText(savedState.getCharSequence(App.VSTUP));
}
savedState = null;
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
savedState = saveState(); /* vstup defined here for sure */
vstup = null;
}
private Bundle saveState() { /* called either from onDestroyView() or onSaveInstanceState() */
Bundle state = new Bundle();
state.putCharSequence(App.VSTUP, vstup.getText());
return state;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* If onDestroyView() is called first, we can use the previously savedState but we can't call saveState() anymore */
/* If onSaveInstanceState() is called first, we don't have savedState, so we need to call saveState() */
/* => (?:) operator inevitable! */
outState.putBundle(App.STAV, (savedState != null) ? savedState : saveState());
}
/* (...) */
}
Run Code Online (Sandbox Code Playgroud)
或者,始终可以将数据显示在被动Views中的变量中,并View仅使用s来显示它们,使两者保持同步.不过,我不认为最后一部分很干净.
Ric*_*rdo 56
在最新的支持库中,这里讨论的解决方案都不再是必需的.您可以Activity根据自己的喜好使用自己的片段FragmentTransaction.只需确保您的片段可以使用ID或标记进行标识.
只要您不尝试在每次调用时重新创建片段,片段都将自动恢复onCreate().相反,您应该检查是否savedInstanceState为null,并在这种情况下找到对创建的片段的旧引用.
这是一个例子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
myFragment = MyFragment.newInstance();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.my_container, myFragment, MY_FRAGMENT_TAG)
.commit();
} else {
myFragment = (MyFragment) getSupportFragmentManager()
.findFragmentByTag(MY_FRAGMENT_TAG);
}
...
}
Run Code Online (Sandbox Code Playgroud)
但请注意,恢复片段的隐藏状态时存在错误.如果要在活动中隐藏片段,则需要在这种情况下手动恢复此状态.
Dro*_*idT 16
我只想提供我想出的解决方案来处理本文中我从Vasek和devconsole派生的所有案例.此解决方案还可以处理特殊情况,当手机不能一次旋转而片段不可见时.
这是我存储捆绑以供以后使用,因为onCreate和onSaveInstanceState是片段不可见时唯一的调用
MyObject myObject;
private Bundle savedState = null;
private boolean createdStateInDestroyView;
private static final String SAVED_BUNDLE_TAG = "saved_bundle";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
savedState = savedInstanceState.getBundle(SAVED_BUNDLE_TAG);
}
}
Run Code Online (Sandbox Code Playgroud)
由于在特殊旋转情况下不调用destroyView,我们可以确定如果它创建状态我们应该使用它.
@Override
public void onDestroyView() {
super.onDestroyView();
savedState = saveState();
createdStateInDestroyView = true;
myObject = null;
}
Run Code Online (Sandbox Code Playgroud)
这部分是一样的.
private Bundle saveState() {
Bundle state = new Bundle();
state.putSerializable(SAVED_BUNDLE_TAG, myObject);
return state;
}
Run Code Online (Sandbox Code Playgroud)
现在这里是棘手的部分.在我的onActivityCreated方法中,我实例化了"myObject"变量,但是onActivity和onCreateView上的旋转都没有被调用.因此,当方向旋转多次时,myObject在这种情况下将为null.我通过重复使用onCreate中保存的相同包作为外包来解决这个问题.
@Override
public void onSaveInstanceState(Bundle outState) {
if (myObject == null) {
outState.putBundle(SAVED_BUNDLE_TAG, savedState);
} else {
outState.putBundle(SAVED_BUNDLE_TAG, createdStateInDestroyView ? savedState : saveState());
}
createdStateInDestroyView = false;
super.onSaveInstanceState(outState);
}
Run Code Online (Sandbox Code Playgroud)
现在,只要您想要恢复状态,只需使用savedState包
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
if(savedState != null) {
myObject = (MyObject) savedState.getSerializable(SAVED_BUNDLE_TAG);
}
...
}
Run Code Online (Sandbox Code Playgroud)
感谢DroidT,我做了这个:
我意识到如果 Fragment 不执行 onCreateView(),则不会实例化其视图。所以,如果返回堆栈上的片段没有创建它的视图,我保存最后存储的状态,否则我用我想要保存/恢复的数据构建我自己的包。
1)扩展这个类:
import android.os.Bundle;
import android.support.v4.app.Fragment;
public abstract class StatefulFragment extends Fragment {
private Bundle savedState;
private boolean saved;
private static final String _FRAGMENT_STATE = "FRAGMENT_STATE";
@Override
public void onSaveInstanceState(Bundle state) {
if (getView() == null) {
state.putBundle(_FRAGMENT_STATE, savedState);
} else {
Bundle bundle = saved ? savedState : getStateToSave();
state.putBundle(_FRAGMENT_STATE, bundle);
}
saved = false;
super.onSaveInstanceState(state);
}
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if (state != null) {
savedState = state.getBundle(_FRAGMENT_STATE);
}
}
@Override
public void onDestroyView() {
savedState = getStateToSave();
saved = true;
super.onDestroyView();
}
protected Bundle getSavedState() {
return savedState;
}
protected abstract boolean hasSavedState();
protected abstract Bundle getStateToSave();
}
Run Code Online (Sandbox Code Playgroud)
2)在你的片段中,你必须有这个:
@Override
protected boolean hasSavedState() {
Bundle state = getSavedState();
if (state == null) {
return false;
}
//restore your data here
return true;
}
Run Code Online (Sandbox Code Playgroud)
3)比如可以在onActivityCreated中调用hasSavedState:
@Override
public void onActivityCreated(Bundle state) {
super.onActivityCreated(state);
if (hasSavedState()) {
return;
}
//your code here
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
269670 次 |
| 最近记录: |