数据变化时刷新 Fragment 内容的方法(如调用 onCreateView)

Posted

技术标签:

【中文标题】数据变化时刷新 Fragment 内容的方法(如调用 onCreateView)【英文标题】:Method to refresh Fragment content when data changed ( like recall onCreateView) 【发布时间】:2015-11-28 08:23:36 【问题描述】:

我有一个Activity,布局中有一个片段容器。 可以在其中显示3个不同的Fragment。 这些片段包含一个Listview,它使用我制作的自定义适配器显示数据。

所以每个列表元素都是在onCreateView 期间创建的,在我查询数据库以获取数据之后。

但有时我的数据库中的某些数据可能会发生变化,所以我想重新绘制/重新创建它Listview

刷新我的片段视图的最佳方式是什么(我的意思是资源要求越低)? 有没有手动调用 onCreateView 的方法?

【问题讨论】:

使用 adapter.notifyDataSetChanged()。它是为了你提到的确切目的。 这就是你想要的! ***.com/questions/20702333/refresh-fragment-at-reload @priyankvex :不,它不起作用,因为我也需要重新启动查询。事实上,我有一个对象“查询”,我需要重新启动这个查询......然后是的,我可以使用 adapter.notifyDataSetChanged() 在片段中实现回调(接口)&当数据改变时调用它。 或试试***.com/a/41888950/3496570 【参考方案1】:

分离并附上

Fragment currentFragment = getFragmentManager().findFragmentByTag("YourFragmentTag");
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.detach(currentFragment);
fragmentTransaction.attach(currentFragment);
fragmentTransaction.commit();

或搜索片段

Fragment currentFragment = getActivity().getSupportFragmentManager().findFragmentById(R.id.container);

【讨论】:

我不能这样做 - 我想从片段的实例中调用“刷新”......从我的自定义适配器甚至从 MainActivity 所以它需要我调用一个静态方法。并且'getFragmentManager'不能在静态方法中使用。 我在我的主要活动中通过Handler 使用了您的代码,它使我能够处理“静态”注意事项。谢谢 ***.com/questions/40095826/… 请帮忙 好吧@Heretyk,我认为在这种情况下你可以使用EventBus。【参考方案2】:

Fragment有一个非常有用的方法,可以用来刷新fragment。

@Override
public void setUserVisibleHint(boolean isVisibleToUser) 
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) 
        //Write down your refresh code here, it will call every time user come to this fragment. 
       //If you are using listview with custom adapter, just call notifyDataSetChanged(). 
    

【讨论】:

【参考方案3】:

合并两个答案并删除if (isVisibleToUser),因为它使setUserVisibleHint 以不可预测的异步顺序被调用,并且片段可以刷新或不刷新。我发现这段代码稳定(在你的片段中):

@Override
public void setUserVisibleHint(boolean isVisibleToUser) 

super.setUserVisibleHint(isVisibleToUser);

  // Refresh tab data:

  if (getFragmentManager() != null) 

    getFragmentManager()
      .beginTransaction()
      .detach(this)
      .attach(this)
      .commit();
  

【讨论】:

这对我来说是最好的解决方案 真的!!已弃用的家伙!您永远不知道用户手机上的更新何时会因为这种方法而导致您的应用崩溃。【参考方案4】:

如果您对上面列出的某些方法有问题(就像我在升级后遇到的那样......),我建议在片段中创建某种公共刷新方法,然后简单地调用它,它的代码更少,更好更快,因为片段不需要重新初始化......

FragmentManager fm = getSupportFragmentManager();

//if you added fragment via layout xml
Fragment fragment = fm.findFragmentById(R.id.your_fragment_id);
if(fragment instanceof YourFragmentClass) // avoid crash if cast fail

    ((YourFragmentClass)fragment).showPrayer();

如果您通过代码添加片段并在添加片段时使用了标签字符串,请改用 findFragmentByTag:

Fragment fragment = fm.findFragmentByTag("yourTag");
if(fragment instanceof YourFragmentClass)

    ((YourFragmentClass)fragment).showPrayer();

【讨论】:

【参考方案5】:

如果您按照 Material design 教程进行操作,您可以导航到同一页面,该页面会自动重新加载。不要忘记通过 backstack 添加“false”,因为您不必返回上一个片段,因为它是同一个片段。

((NavigationHost) getActivity()).navigateTo(new MyNotesFragment(), false);
import androidx.fragment.app.Fragment;

public interface NavigationHost 
    void navigateTo(Fragment fragment, boolean addToBackstack);

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity implements NavigationHost 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) 
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.container, new LoginFragment())
                    .commit();
        
    

    @Override
    public void navigateTo(Fragment fragment, boolean addToBackstack) 
        FragmentTransaction transaction =
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.container, fragment);

        if (addToBackstack) 
            transaction.addToBackStack(null);
        

        transaction.commit();
    

【讨论】:

【参考方案6】:

根据FragmentTransaction#replace,这和调用remove然后add是一样的。因此,当您启动片段或想要重新加载片段时,您可以将.replace 与片段管理器一起使用。因此,请使用 onCreate 中的相同功能以及您想要重新加载它的位置...

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState == null) 
        loadContentFragment();
    


private void someFunctionThatChangesValue(String value) 
    mValue = value;
    loadContentFragment();


private void loadContentFragment() 
    ContentListFragment newFrag = ContentListFragment.newInstance();
    // passing value from activity
    Bundle args = new Bundle();
    args.putString(Constants.ARG_ACCOUNT_NAME, mValue);
    newFrag.setArguments(args);
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.content_frag_container,
                    newFrag,
                    Constants.CONTENT_FRAGMENT)
            .commitNow();

这样,只有一个函数可以加载内容并传递数据。这假设您的布局中有一个 ID 为 content_frag_container 的条目。我使用了框架布局。

【讨论】:

【参考方案7】:

这个方法对我有用:

MyCameraFragment f2 = new MyCameraFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, f2);
transaction.addToBackStack(null);
transaction.commit();

【讨论】:

【参考方案8】:

以下代码刷新来自适配器的片段

下载适配器代码:

public class DownloadsAdapter extends RecyclerView.Adapter<DownloadsAdapter.ViewHolder> 

private Context mCtx;

//getting the context and product list with constructor
public DownloadsAdapter(Context mCtx, List<DataModel> fileUrlLinkList) 
    this.mCtx = mCtx;
    this.fileUrlList = fileUrlLinkList;



**... and in onBindViewHolder**

 FragmentManager manager = ((AppCompatActivity) mCtx).getSupportFragmentManager();
 Fragment currentFragment = manager.findFragmentByTag("DOWNLOADS");
 FragmentTransaction fragmentTransaction = manager.beginTransaction();
 fragmentTransaction.detach(currentFragment);
 fragmentTransaction.attach(currentFragment);
 fragmentTransaction.commit();

...


【讨论】:

【参考方案9】:

在要刷新的Fragment中设置

    override fun setUserVisibleHint(isVisibleToUser: Boolean) 
    super.setUserVisibleHint(isVisibleToUser)
    if(isVisibleToUser)
        if (getFragmentManager() != null) 
            getFragmentManager()
                ?.beginTransaction()
                ?.detach(this)
                ?.attach(this)
                ?.commit();
        
    

【讨论】:

【参考方案10】:

如今,Android 应用程序倾向于实现 Android Navigation 组件(它简化了片段管理和导航),在这种情况下,建议采用不同于其他建议答案的方法,以避免直接操作片段并与片段管理器交互.当您需要做的只是更新适配器数据时,执行“刷新”以使您的片段状态保持最新会不必要地破坏并重新创建视图。

您可以改为使用事件驱动的方法并实现像事件总线这样的模式,消费者可以发布和订阅该模式。更新数据的组件将负责发布与更新相关的事件,而任何关心更新的组件都可以订阅这些事件。

这是 Kotlin 中的一个简单 EventBus 类,它利用协程和流等 Kotlin 语言特性来实现这种描述的模式:

import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

class EventBus 
    private val _events = MutableSharedFlow<AppEvent>()
    val events = _events.asSharedFlow()

    suspend fun emitEvent(event: AppEvent) 
        _events.emit(event)
    

在这种情况下,AppEvent 是一个枚举,例如:

enum class AppEvent 
  TODO_LIST_UPDATED

如果您想将数据与事件一起传递,您可以为您的AppEvent 实现Kotlin sealed classes 而不是枚举。

假设与您的片段无关的其他组件正在更新数据,一旦完成,它就会发出一个事件:

eventBus.emitEvent(AppEvent.TODO_LIST_UPDATED)

假设您使用的是 MVVM 架构,您的片段的视图模型在初始化时可以订阅事件总线:

init 
    viewModelScope.launch 
        eventBus.events
            .filter  it == AppEvent.TODO_LIST_UPDATED 
            .collectLatest  
                val todosResponse = todoRepository.getTodos()
                _todos.value = todosResponse.todos
            
        

一旦检索到新数据,您的片段将观察 LiveData 并在适配器上设置数据。

如果您没有视图模型,您可以直接在片段中执行此操作,使用 lifecycleScope,但现在建议使用视图模型并实现 MVVM 架构。

要确保的一件重要事情是,您的事件总线实例是一个单例实例,以便所有消费者都发布和订阅同一总线。您可以通过使用依赖注入框架并按照为该框架创建单例的说明来实现这一点。

您可以在blog post I wrote on this topic 上详细了解事件总线模式。

【讨论】:

嘿@Tina 你能帮我解决这个问题吗issue【参考方案11】:

#我的项目是用Android Navigation构建的片段#

MainList 片段调用 FilterDialog 片段。 在程序和用户完成过滤器对话框中的工作后,按下返回按钮返回到 MainList 片段并根据请求刷新它。如果您有 Viewmodel 并且能够共享日期,这很容易做到。

我先给你结束,然后是通向它的编码链。

*val navController = findNavController()
navController.run  popBackStack()
navigate(mViewModel. FilterDialogReturnTarget )`*

这会弹出 backstack 并正确刷新返回的 Fragment 以反映对过滤器的更改。

    >>> Viewmodel:
import androidx.lifecycle.ViewModel ……. 

class MainActivityViewModel : ViewModel()  …… 

var FilterDialogReturnTarget:Int=0……. 


   >>navigation Graph

 navigation/nglist.xml
 <navigation …….
 <fragment
 android:id="@+id/nav_main_list" …… 


    >>>MainList.kt
class MainList : androidx.fragment.app.Fragment()……

import App.name.name.viewmodel.MainActivityViewModel
private val mViewModel: MainActivityViewModel by activityViewModels()


private fun setUpListeners() ….

binding.vemlBtnFilterOn.setOnClickListener …….

mViewModel.FilterDialogReturnTarget = R.id.nav_main_list
findNavController().navigate(R.id.action_main_list_to_FilterDialog)


    >>>FilterDialog.kt
import androidx.fragment.app.DialogFragment
import app.name.name.MainActivityViewModel

class ElectionsFilterDialog : DialogFragment()…..
private val mViewModel: MainActivityViewModel by activityViewModels()


private fun setUpListeners() ….
binding.vemlBtnFilterOn.setOnClickListener …….
val navController = findNavController()
navController.run  popBackStack()
navigate(mViewModel.FilterDialogReturnTarget )

这对我来说就像一个魅力,因为我已经有了视图模型和导航。

【讨论】:

【参考方案12】:

当片段中的数据发生变化时,最好将片段分离并重新附加到framelayout。就我而言,我有一个listview,它显示了我的用户最喜欢的项目。一旦用户不喜欢某个产品,我必须将其从列表中删除并重新加载Favorites 片段。

所以我做了这样的事情来将片段重新加载到框架布局中:

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.frm,new FavoritesFragment()).addToBackStack(null).commit();

这里,frmMainActivity 中保存片段的框架布局,FavoritesFragment() 是需要重新加载的片段。

每次用户按下unlike按钮时都应该执行上面的代码

【讨论】:

以上是关于数据变化时刷新 Fragment 内容的方法(如调用 onCreateView)的主要内容,如果未能解决你的问题,请参考以下文章

Fragment-Fragment切换的数据刷新问题

解决ViewPager缓存导致不能实时刷新数据

activity 嵌套一级fragment,一级fragment嵌套二级fragment,在一级fragment中刷新二级fragment中的UI

片段不刷新其内容

vue改变数组内容页面不刷新问题

重新加载时刷新片段