Fragment使用过程中一些需要注意的点
2017-06-27 20:09
239 查看
对Fragment相关知识的一点复习和整理。
fragment默认是可见的,下面两个方法可以改变fragment的可见状态:
当fragment被add或replace到容器中时,会创建视图(onCreate -> onCreateView -> onStart -> onResume被调用),当fragment被从容器中remove掉或replace掉时,会销毁视图(onPause -> onStop -> onDestroyView -> onDestroy被调用)。
而当fragment被hide或show时,只是其可见性的改变,不会有视图的创建和销毁,因此上面8个生命周期方法中任何一个都不会被调用。
当Fragment对象被从容器中移除时(被remove掉或被另一个Fragment对象 replace掉),无论该Fragment对象是否被引用,它的视图都会被销毁(onPause -> onStop -> onDestroyView -> onDestroy都会被调用),例如在下面的代码中,当点击btn2时,容器中原有的Fragment1对象的视图就会被销毁。
尽管Fragment1对象的视图被销毁了,但是因为它还被成员变量fragment1引用,因此Fragment1对象本身并不会被销毁(表现为Fragment1对象的onPause -> onStop -> onDestroyView -> onDestroy被调用之后,成员变量fragment1并不为null)。
在activity的生命周期方法(如onCreate)中创建fragmentA并add到容器中,如果acitivity因旋转屏幕、内存不足等原因被销毁了,那么activity被重建后,你会发现容器中出现了重叠的多个fragmentA。
原因:
当activity可能被销毁时,系统会在onSaveInstanceState中保存fragmentA的各种状态。当activity重建时,系统会在onCreate中检测参数savedInstanceState,如果发现其中保存有fragmentA的信息,则会通过反射调用FragmentA类的空参数构造方法来重新创建fragmentA,并将savedInstanceState中保存的fragmentA的信息恢复到重新创建的fragmentA,然后再将此fragmentA添加到容器中。除此之外,我们在onCreate方法中创建fragmentA并add到容器中的代码也会被执行——这样一来,容器中就有了两个fragmentA,因此便出现了重叠现象。
注意:如果使用replace来显示fragment则不会有上述问题,因为replace在添加之前会先移除容器中的全部fragment。
解决办法:
上面的例子“使用add-hide-show来显示和切换fragment”中,就会存在fragment重叠的问题,下面以此为例来说明解决办法:
1.在activity的onCreate方法中检测参数savedInstanceState是否为null,如果非null则说明是销毁后重建,此时就不再将fragmentA add到容器中。
上面的Fragment来自
建议使用最新版本的
2.不保存fragment的状态:在Activity中重写onSaveInstanceState方法,将
我们习惯通过构造方法来创建对象并向其传递数据,然而如果我们通过Fragment的构造方法来向Fragment传递数据的话,可能会出现问题。
我们知道,当activity被销毁、重建时,其中的fragment也会被销毁并重建。然而,重建fragment是通过反射调用其无参数的构造方法来实现的,也就是说此fragment带有传递数据功能的构造方法并没有被调用,因而数据并没有被传递给重建的fragment。
如下例所示:
旋转屏幕之前,tv中显示的是”ZhangSan”,而旋转屏幕之后,tv中显示的是”nothing”。
解决办法:
在activity中使用setArguments来向fragment传递数据,在fragment中使用getArguments来获取数据。
当activity销毁和重建时,系统会保存和恢复传递给fragment的arguments,因此在重建的fragment中仍然可以通过getArguments来获取数据。
1 使用add-hide-show来显示和切换fragment
add(int containerViewId, Fragment fragment):将fragment添加到容器中,并不会remove掉容器中已有的fragment。
fragment默认是可见的,下面两个方法可以改变fragment的可见状态:
hide(Fragment fragment):隐藏fragment
show(Fragment fragment):显示fragment
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="cn.szx.myapplication.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="show Fragment1" /> <Button android:id="@+id/btn2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="show Fragment2" /> </LinearLayout> <FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener { Fragment fragment1, fragment2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn1).setOnClickListener(this); findViewById(R.id.btn2).setOnClickListener(this); fragment1 = new Fragment1(); fragment2 = new Fragment2(); getFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment1) .add(R.id.fragment_container, fragment2) .hide(fragment2) .commit(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn1: getFragmentManager().beginTransaction() .hide(fragment2) .show(fragment1) .commit(); break; case R.id.btn2: getFragmentManager().beginTransaction() .hide(fragment1) .show(fragment2) .commit(); break; } } }
当fragment被add或replace到容器中时,会创建视图(onCreate -> onCreateView -> onStart -> onResume被调用),当fragment被从容器中remove掉或replace掉时,会销毁视图(onPause -> onStop -> onDestroyView -> onDestroy被调用)。
而当fragment被hide或show时,只是其可见性的改变,不会有视图的创建和销毁,因此上面8个生命周期方法中任何一个都不会被调用。
2 使用replace来显示和切换fragment
replace(int containerViewId, Fragment fragment):将fragment添加到容器中,在添加之前,会先remove掉容器中已有的全部fragment。
当Fragment对象被从容器中移除时(被remove掉或被另一个Fragment对象 replace掉),无论该Fragment对象是否被引用,它的视图都会被销毁(onPause -> onStop -> onDestroyView -> onDestroy都会被调用),例如在下面的代码中,当点击btn2时,容器中原有的Fragment1对象的视图就会被销毁。
尽管Fragment1对象的视图被销毁了,但是因为它还被成员变量fragment1引用,因此Fragment1对象本身并不会被销毁(表现为Fragment1对象的onPause -> onStop -> onDestroyView -> onDestroy被调用之后,成员变量fragment1并不为null)。
public class MainActivity extends AppCompatActivity implements View.OnClickListener { Fragment fragment1, fragment2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn1).setOnClickListener(this); findViewById(R.id.btn2).setOnClickListener(this); fragment1 = new Fragment1(); fragment2 = new Fragment2(); getFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment1).commit(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn1: getFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment1).commit(); break; case R.id.btn2: getFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment2).commit(); break; } } }
3 activity销毁重建后fragment重叠的问题
情景:在activity的生命周期方法(如onCreate)中创建fragmentA并add到容器中,如果acitivity因旋转屏幕、内存不足等原因被销毁了,那么activity被重建后,你会发现容器中出现了重叠的多个fragmentA。
原因:
当activity可能被销毁时,系统会在onSaveInstanceState中保存fragmentA的各种状态。当activity重建时,系统会在onCreate中检测参数savedInstanceState,如果发现其中保存有fragmentA的信息,则会通过反射调用FragmentA类的空参数构造方法来重新创建fragmentA,并将savedInstanceState中保存的fragmentA的信息恢复到重新创建的fragmentA,然后再将此fragmentA添加到容器中。除此之外,我们在onCreate方法中创建fragmentA并add到容器中的代码也会被执行——这样一来,容器中就有了两个fragmentA,因此便出现了重叠现象。
注意:如果使用replace来显示fragment则不会有上述问题,因为replace在添加之前会先移除容器中的全部fragment。
解决办法:
上面的例子“使用add-hide-show来显示和切换fragment”中,就会存在fragment重叠的问题,下面以此为例来说明解决办法:
1.在activity的onCreate方法中检测参数savedInstanceState是否为null,如果非null则说明是销毁后重建,此时就不再将fragmentA add到容器中。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn1).setOnClickListener(this); findViewById(R.id.btn2).setOnClickListener(this); fragment1 = new Fragment1(); fragment2 = new Fragment2(); if (savedInstanceState == null) {//非销毁后重建 getFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment1) .add(R.id.fragment_container, fragment2) .hide(fragment2) .commit(); } }
上面的Fragment来自
android.app,这里其实还有一个系统库的bug,就是在保存恢复fragment的状态时,并不会保存和恢复fragment的显示隐藏状态,也就是说,假设在activity销毁之前,两个fragment一个显示一个隐藏,那么当activity重建之后,两个fragment都会变成显示状态,因此仍然会出现重叠现象。
建议使用最新版本的
android.support.v4.app包中的Fragment,这个Fragment已经解决了这个bug(fragment的显示和隐藏状态也会被保存和恢复)。
2.不保存fragment的状态:在Activity中重写onSaveInstanceState方法,将
super.onSaveInstanceState(outState);注释掉,让其不再保存fragment的状态。
@Override public void onSaveInstanceState(Bundle outState) { //super.onSaveInstanceState(outState); }
4 使用setArguments方法向Fragment传递数据
问题:我们习惯通过构造方法来创建对象并向其传递数据,然而如果我们通过Fragment的构造方法来向Fragment传递数据的话,可能会出现问题。
我们知道,当activity被销毁、重建时,其中的fragment也会被销毁并重建。然而,重建fragment是通过反射调用其无参数的构造方法来实现的,也就是说此fragment带有传递数据功能的构造方法并没有被调用,因而数据并没有被传递给重建的fragment。
如下例所示:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) {//非重建 getFragmentManager().beginTransaction() .add(R.id.fragment_container, new Fragment1("ZhangSan")) .commit(); } } }
public class Fragment1 extends Fragment { String name = "nothing"; Fragment1() { } Fragment1(String name) { this.name = name; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment1, null); ((TextView) view.findViewById(R.id.tv)).setText(name); return view; } }
旋转屏幕之前,tv中显示的是”ZhangSan”,而旋转屏幕之后,tv中显示的是”nothing”。
解决办法:
在activity中使用setArguments来向fragment传递数据,在fragment中使用getArguments来获取数据。
当activity销毁和重建时,系统会保存和恢复传递给fragment的arguments,因此在重建的fragment中仍然可以通过getArguments来获取数据。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) {//非重建 Fragment1 fragment1 = new Fragment1(); Bundle bundle = new Bundle(); bundle.putCharSequence("name", "Zhangsan"); fragment1.setArguments(bundle); getFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment1) .commit(); } } }
public class Fragment1 extends Fragment { String name = "nothing"; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { name = (String) getArguments().getCharSequence("name"); View view = inflater.inflate(R.layout.fragment1, null); ((TextView) view.findViewById(R.id.tv)).setText(this.name); return view; } }
相关文章推荐
- Cygwin使用过程中需要注意的一些问题
- JavaScript使用过程中需要注意的地方和一些基本语法
- JavaScript使用过程中需要注意的地方和一些基本语法
- java实际开发中泛型使用需要注意的一些问题
- 3DMAX导出插件开发过程中需要注意的一些地方
- 『ExtJS』使用中需要注意的一些事(持续更新)
- 使用IOCP需要注意的一些问题~~(不断补充)
- [转]使用IOCP需要注意的一些问题~~(不断补充)
- 使用异常时需要注意的一些问题(转)
- 在使用 CCRenderTexture、shader 绘制几何图元时需要注意的一些细节问题
- 使用Linux raw socket时需要注意的一些问题
- 使用SVPullToRefresh 报错 需要注意 以及一些刷新方法
- JavaScript使用过程中的注意点和一些基本语法
- 电路系统设计制作过程和需要注意的一些问题
- 在使用 CCRenderTexture、shader 绘制几何图元时需要注意的一些细节问题
- 在java中使用常量变量的一些需要注意的地方
- Java transient关键字使用小结及一些需要注意的细节
- 关于SelectOjbect使用需要注意的一些问题
- 使用VS2005开发64位驱动程序需要注意的一些问题
- java实际开发中泛型使用需要注意的一些问题