android 直接添加一个Fragment到activity,不需要额外setContentView

getSupportFragmentManager().beginTransaction().replace(android.R.id.content,new ArticleListFragment()).commit();

转载于:https://www.cnblogs.com/agilezhu/p/7053619.html

<think>我们面临的问题:在Activity跳转返回时,避免Fragment重新加载(保持Fragment状态)从引用中我们可以提取关键信息:引用[1]:展示了从StoreActivity跳回MainActivity时通过Intent传递一个额外参数(fragmentToLoad)来指定要加载的Fragment。引用[2]:展示了ActivityFragment传输数据的过程,包括Fragment添加步骤(获取FragmentManager,开启事务,添加Fragment,提交事务)。引用[3]:展示了在Activity添加Fragment的方法(使用getSupportFragmentManager().beginTransaction().add(...).commit())。但是,我们的目标是如何传递数据或者添加Fragment,而是如何避免FragmentActivity重新创建时(例如配置变化或者从后台返回)被重新加载,从而丢失状态。解决方案:1.使用Fragment的setRetainInstance(true)方法(只适用于依附于ActivityFragment,且只对configurationchange有效,但在Activity被系统回收时无效,并且该方法在API29已被废弃)。2.利用ViewModel结合onSaveInstanceState保存状态(推荐)。3.利用Fragment的arguments保存数据(适用于少量数据)。4.将Fragment的创建和添加放在条件语句中,避免重复添加。然而,更常见和推荐的做法是使用ViewModel来保存数据,因为ViewModel在配置更改(如屏幕旋转)时仍然存在,当Activity被销毁重建时,ViewModel可以保留数据。此外,如果我们希望在Activity被销毁重建(例如在后台被系统回收)时恢复Fragment的状态,我们可以通过保存和恢复Fragment的状态来实现。但这里,我们特别关注的是:在同一个Activity中,当跳转到另一个Activity再返回时,原来的Fragment会被重新创建吗?实际上,如果Activity被销毁(例如由于内存足而被系统回收),则其中的Fragment也会被销毁。但如果是正常的跳转返回(没有发生Activity销毁),则Fragment会重新创建,除非我们在跳转时调用了finish()销毁了当前Activity(如引用[1]中的代码)。在引用[1]中,跳转后调用了finish(),所以StoreActivity被销毁。而回到MainActivity时,MainActivity会重新创建,因此其内部的Fragment也会重新创建。因此,我们的目标是:在MainActivity重新创建时,能够恢复之前Fragment的状态。具体步骤:方法一:使用onSaveInstanceState保存状态1.在MainActivity的onSaveInstanceState方法中保存必要的数据(例如当前显示的Fragment的tag,以及需要保存的Fragment状态)。2.在onCreate中,检查savedInstanceState是否为空,如果为空,则恢复Fragment的状态。方法二:利用ViewModel1.为Fragment创建一个ViewModel,将需要保存的数据放在ViewModel中(即使Activity重建,ViewModel也会被保留)。2.在Fragment中获取这个ViewModel,并使用它来保存和恢复数据。方法三:避免销毁Activity如果我们希望MainActivity被销毁,我们可以调用finish(),而是使用Activity的返回栈,这样当从StoreActivity返回时,MainActivity并没有被销毁,因此Fragment的状态自然得以保留。但是,在引用[1]中使用了finish(),所以我们需要权衡是否要销毁。针对引用[1]的场景,我们想要返回时加载指定的Fragment(OrderingFragment),同时又希望保留之前的状态,我们可以:1.在StoreActivity中调用finish(),这样MainActivity还在后台,会被销毁。但是这样可能会导致多个Activity实例。2.在跳转时传递需要加载的Fragment信息,并在MainActivity中处理,同时保存和恢复Fragment的状态。综合考虑,我们采用在ViewModel中保存数据,同时配合onSaveInstanceState来保存必要的状态信息(比如Fragment的tag)的方法。具体实现:步骤1:在MainActivity中,使用ViewModel保存各个Fragment的状态(如果需要保存多个Fragment,可以考虑使用一个Map来保存)。步骤2:在MainActivity的onCreate中,检查savedInstanceState,判断是否为首次创建,如果是,则恢复Fragment的状态。但是,更直接的做法是:让Fragment自身处理状态保存和恢复。Fragment自身也有onSaveInstanceState方法,我们可以重写它来保存数据,并在onViewCreated或onActivityCreated中恢复。然而,在Activity重建时,FragmentManager会自动保存Fragment的状态(包括Fragment的视图状态,比如EditText中的文字),因此如果我们没有额外要保存的数据,其实默认行为已经可以恢复视图状态。但是,如果我们有一些自定义的数据需要在重建时恢复,则需要在Fragment中重写onSaveInstanceState保存,并在onCreate、onCreateView或onActivityCreated中恢复。另外,为了防止Fragment重复添加,我们可以在Activity的onCreate中添加Fragment时先检查是否已经存在:if(savedInstanceState==null){//只有是第一次创建Activity时才添加FragmentgetSupportFragmentManager().beginTransaction().add(R.id.container,newMyFragment()).commit();}但是,如果我们想要根据Intent的extra来显示同的Fragment,并且可能每次跳转回来都要求改变显示的Fragment,那么我们就需要在onCreate中(即使savedInstanceState为空)也处理,这时可能会重复添加。所以,更好的做法是:1.在Activity中,我们每次只显示一个Fragment,所以在跳转回来时,如果已经存在Fragment,我们就替换它(而添加新的)。2.在替换之前,我们可以检查当前的Fragment是否已经是目标Fragment,如果是,则需要替换。考虑到需要保存Fragment状态,我们使用FragmentTransaction的replace方法,同时利用FragmentManager自动管理的BackStack。然而,用户的需求是避免重新加载(即保留数据)。如果我们要替换Fragment,那么原来的Fragment会被销毁,这样就会丢失数据。所以,我们更希望的是:如果已经存在目标Fragment,我们就直接显示它;如果存在,则创建并显示。具体方案:在MainActivity中:@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//检查是否有传递过来的fragmentToLoadStringfragmentToLoad=getIntent().getStringExtra("fragmentToLoad");//如果savedInstanceState为空,则Activity是重建的,FragmentManager会自动恢复之前添加Fragment//所以,我们只需要根据fragmentToLoad来显示对应的Fragment(如果当前显示的Fragment是目标Fragment的话)if(fragmentToLoad!=null){//检查这个Fragment是否已经存在Fragmentfragment=getSupportFragmentManager().findFragmentByTag(fragmentToLoad);if(fragment==null){//创建新的Fragment实例if(fragmentToLoad.equals("OrderingFragment")){fragment=newOrderingFragment();}elseif(fragmentToLoad.equals("OtherFragment")){fragment=newOtherFragment();}//设置tag,以便后续查找}//使用replace,先移除当前显示的Fragment,再添加新的Fragment//这里可能破坏之前的Fragment状态,所以如果我们希望保留之前的Fragment状态(即销毁),我们可以考虑使用hide和show//因此,我们采用addToBackStack(null)和hide/show的方式}else{//默认显示哪个Fragmentfragment=...;}//然而,为了保留之前的Fragment状态,我们应该每次都用新的Fragment替换旧的,而是应该隐藏旧的,显示新的,并且旧的Fragment会被销毁//因此,我们需要://1.如果目标Fragment已经存在,就显示它,并隐藏其他Fragment//2.如果存在,则创建它,并添加,同时隐藏其他Fragment//由于需要管理多个Fragment,我们可以在Activity中维护一个Fragment列表,或者通过FragmentManager的getFragments()来获取所有的Fragment//通常的做法是:使用多个Fragment时,在添加的时候设置tag,然后根据tag来查找。//下面我们实现一种方式://假设我们的容器是R.id.fragment_container//查找当前显示的FragmentFragmentcurrentFragment=getSupportFragmentManager().findFragmentById(R.id.fragment_container);//查找目标Fragment(通过tag)FragmenttargetFragment=getSupportFragmentManager().findFragmentByTag(fragmentToLoad);FragmentTransactiontransaction=getSupportFragmentManager().beginTransaction();if(targetFragment!=null){//如果目标Fragment已经存在,就显示它if(currentFragment!=null){transaction.hide(currentFragment);}transaction.show(targetFragment);transaction.addToBackStack(null);}else{//如果目标Fragment存在,创建并添加targetFragment=createFragmentByTag(fragmentToLoad);//这里需要自己实现根据tag创建Fragment的方法if(currentFragment!=null){transaction.hide(currentFragment);}transaction.add(R.id.fragment_container,targetFragment,fragmentToLoad);transaction.addToBackStack(null);}transaction.commit();//注意:这样我们每次切换Fragment时,都会销毁之前的Fragment,它们只是被隐藏,所以状态得以保留。}注意:我们需要在返回栈中管理这些Fragment,这样当用户按返回键时,可以回到上一个Fragment。但是,这种方式会增加内存占用,因为所有添加过的Fragment都会被保留在内存中(直到Activity销毁)。另外,在Activity被销毁重建后,FragmentManager会自动恢复Fragment的状态(包括隐藏/显示状态),所以需要我们额外处理。总结:为了避免FragmentActivity中重新加载(保持数据),我们可以:1.避免使用replace,而是使用show/hide来切换Fragment,这样Fragment会被销毁。2.使用Fragment的tag来标识每个Fragment,以便查找。3.在需要显示某个Fragment时,先通过tag查找是否已经存在,如果存在则显示它(并隐藏其他Fragment),如果存在则创建并添加(同时隐藏其他Fragment)。这种方法适合需要保留多个Fragment实例的场景。但是如果Fragment过多,可能会导致内存占用过大,因此需要根据实际情况选择。此外,对于需要保存的数据,我们也可以配合ViewModel,将数据放在ViewModel中,这样即使Fragment被销毁(比如内存足时被回收),数据也能得以保留(ViewModel的作用范围是Activity,所以只要Activity没有被销毁,ViewModel就会被保留。但Activity被销毁(比如用户退出)则ViewModel也会被清除)。具体代码示例:在MainActivity中,我们处理跳转回来的Intent:@OverrideprotectedvoidonCreate(BundlesavedInstanceState){//...handleIntent(getIntent());}@OverrideprotectedvoidonNewIntent(Intentintent){super.onNewIntent(intent);setIntent(intent);handleIntent(intent);}privatevoidhandleIntent(Intentintent){StringfragmentTag=intent.getStringExtra("fragmentToLoad");if(fragmentTag!=null){switchFragment(fragmentTag);}}privatevoidswitchFragment(Stringtag){//查找当前显示的Fragment(假设我们的容器只有一个Fragment,即当前显示的那个)FragmentcurrentFragment=getSupportFragmentManager().findFragmentById(R.id.fragment_container);FragmenttargetFragment=getSupportFragmentManager().findFragmentByTag(tag);if(targetFragment==null){targetFragment=createFragmentByTag(tag);}if(targetFragment!=null){FragmentTransactiontransaction=getSupportFragmentManager().beginTransaction();if(currentFragment!=null){transaction.hide(currentFragment);}if(targetFragment.isAdded()){transaction.show(targetFragment);}else{transaction.add(R.id.fragment_container,targetFragment,tag);}transaction.commit();}}这里,我们假设createFragmentByTag方法能够根据tag创建对应的Fragment实例。注意:我们在onNewIntent中也要处理,因为从StoreActivity返回时,可能会重用MainActivity(如果MainActivity没有被销毁,而是被重新带到前台,则会触发onNewIntent)。最后,在StoreActivity中跳转到MainActivity时,我们要调用finish()(这样StoreActivity会被销毁,而是进入后台,而MainActivity被带到前台)。但是,我们需要在MainActivity设置启动模式(例如singleTop),以避免创建多个MainActivity实例。在MainActivity的Manifest中设置:<activityandroid:name=".MainActivity"android:launchMode="singleTop"/>这样,当在StoreActivity中调用:Intentintent=newIntent(StoreActivity.this,MainActivity.class);intent.putExtra("fragmentToLoad","OrderingFragment");startActivity(intent);//要调用finish()这样,MainActivity会被重新创建(如果已经存在),而是会调用onNewIntent,并且StoreActivity被压在栈下,当我们返回时,回到StoreActivity,再退出StoreActivity,就回到了MainActivity。但是,用户可能希望直接按返回键就从StoreActivity回到MainActivity,然后显示指定的Fragment,而无需再按返回键退出MainActivity。如果我们调用finish(),则按下返回键只是回到MainActivity会再次打开StoreActivity)。因为StoreActivity还在栈中。所以,我们是否调用finish()取决于我们想要怎样的导航流程。如果我们希望从StoreActivity直接返回到MainActivity,并且StoreActivity销毁,并且MainActivity重新创建(或者已经存在)并显示指定Fragment,那么:1.在StoreActivity中启动MainActivity时,调用startActivity(intent),然后调用finish()销毁当前Activity。这样MainActivity会被带到前台(如果已经存在,则直接显示;如果存在则创建)。但是,如果MainActivity已经存在,则会被重新创建(除非被系统销毁重建),所以会调用onCreate,而是调用onNewIntent。2.我们在MainActivity中处理onNewIntent,然后按照上面的方式切换Fragment。因此,结合上面的代码,我们可以在MainActivity中处理即可。最后,为了防止多个Fragment重叠的问题,我们可以使用上面的show/hide策略。总结:要避免Fragment重新加载,主要在于使用show/hide来切换Fragment,而是replace(replace会销毁Fragment)。同时,在Activity重建时,由系统自动恢复Fragment的状态,这样我们就能保持Fragment的数据。注意:如果Activity被销毁重建,系统会自动恢复Fragment的状态(包括其视图状态),所以只要我们主动销毁FragmentFragment就会自动恢复。而我们使用show/hide,在重建时,系统也会恢复隐藏/显示状态。因此,我们采用这种方式,既能保证显示指定的Fragment,又能保留之前Fragment的状态。</think> 在Android开发中,避免FragmentActivity中重新加载并保持数据状态,可通过以下方案实现: ### 解决方案 #### 1. **使用`ViewModel`保存数据**(推荐) ```kotlin // 在Fragment中 class MyFragment : Fragment() { private val viewModel: MyViewModel by viewModels() override fun onCreateView(...): View? { // 从ViewModel获取数据 viewModel.data.observe(viewLifecycleOwner) { data -> // 更新UI } } } // 共享ViewModel(Activity作用域) class SharedViewModel : ViewModel() { val liveData = MutableLiveData<String>() } ``` #### 2. **利用`setRetainInstance`(API 29以下)** ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true // 防止配置更改时重建 } ``` #### 3. **使用Fragment的`arguments`** ```java // 保存数据 val fragment = MyFragment().apply { arguments = Bundle().apply { putString("KEY", "重要数据") } } // 读取数据 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val data = arguments?.getString("KEY") } ``` #### 4. **避免重复添加Fragment** 在Activity的`onCreate()`中添加检查: ```java if (savedInstanceState == null) { // 仅首次创建时添加 supportFragmentManager.beginTransaction() .add(R.id.container, MyFragment.newInstance()) .commit() } ``` ### 关键逻辑说明 1. **`ViewModel`优势**: - 独立于Activity生命周期,屏幕旋转时数据丢失 - 通过`LiveData`实现数据观察,自动更新UI - 作用域可控(Activity/Fragment级别)[^1] 2. **`setRetainInstance`限制**: - API 29已废弃,仅适合临时方案 - 无法解决Activity被系统销毁的情况(需配合`onSaveInstanceState`) 3. **Fragment状态保存流程**: ```mermaid graph LR A[Activity重建] --> B{检查savedInstanceState} B -->|非空| C[FragmentManager自动恢复Fragment] B -->|空| D[新建Fragment] ``` ### 注意事项 1. **大型数据**:避免在`onSaveInstanceState()`保存超过100KB的数据 2. **Fragment事务**:使用`commitNow()`避免异步添加导致的状态一致 3. **导航组件**:考虑使用`Navigation Component`自动处理Fragment状态[^3] > 提示:对于复杂场景,推荐组合方案:`ViewModel` + `onSaveInstanceState()` + 避免重复实例化Fragment[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值