当数据发生变化时刷新Fragment内容的方法(例如重新调用onCreateView)

16

我有一个布局中带有碎片容器的Activity

可以在其中显示3个不同的Fragment

这些碎片包含一个使用我自己制作的自定义适配器显示数据的Listview

因此,在每个列表元素创建期间,我都会在onCreateView中查询数据库以获取数据。

但有时我的数据库中可能会更改一些数据,因此我想重新绘制/重建Listview

  • 最好的方法是什么(我是指,资源需求最少)来刷新我的碎片视图?
  • 是否有一种方法可以手动调用onCreateView

3
请使用adapter.notifyDataSetChanged()方法。它正是你所提到的确切目的。 - priyankvex
这就是你想要的! - Sucho
1
@priyankvex:不行,因为我还需要重新启动一个查询。实际上,我有一个名为“Query”的对象,我需要重新启动这个查询……然后是的,我可以使用adapter.notifyDataSetChanged()。 - Heretyk
在Fragment中实现一个回调(接口),当数据发生变化时调用它。 - Zar E Ahmer
或尝试https://dev59.com/iWUp5IYBdhLWcg3wdXaa#41888950 - Zar E Ahmer
12个回答

15

分离并附加它

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);

2
我做不到这点 - 我想从我的自定义适配器或甚至是MainActivity中调用片段实例外部的“刷新”。因此,这要求我调用静态方法。而且在静态方法中不能使用“getFragmentManager”。 - Heretyk
1
我在我的主活动中使用了您的代码,通过Handler处理,它使我能够处理“静态”考虑。谢谢。 - Heretyk
请帮忙更新一个从SQLite填充的GridView的片段:http://stackoverflow.com/questions/40095826/how-to-update-a-fragment-that-has-a-gridview-populated-from-sqlite - Si8
嗯,@Heretyk,我认为你可以在那种情况下使用EventBus。 - Wasi Sadman

5

将两个答案合并并删除 if (isVisibleToUser),因为它会以不可预测的异步顺序调用 setUserVisibleHint,从而导致片段可能被刷新或不被刷新。我发现在您的 Fragment 中,这段代码是稳定的:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

  // Refresh tab data:

  if (getFragmentManager() != null) {

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

这对我来说是最好的解决方案。 - Whiteq
真的!这个方法已经被弃用了!你永远不知道用户手机上的更新会因为这个方法而导致应用崩溃。 - androidStud

4

有一种非常有用的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(). 
    }
}

2
如果您遇到上述方法中的某些问题(就像我在升级后遇到的那样...),我建议在片段中制作某种公共刷新方法,然后简单地调用它,这甚至更少的代码,更好,更快,因为片段不需要重新初始化...
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();
}

1
如果您按照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();
    }
}

0

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

MainList片段调用FilterDialog片段。 在程序和用户完成Filter对话框的工作后,按下返回按钮返回到MainList片段并根据请求刷新它。如果您有Viewmodel并且能够共享数据,则很容易实现。

我先给你结论,然后是导致它的编码链。

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

这将弹出后退栈并正确刷新返回的片段,以反映对过滤器的更改。

    >>> 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 )}

这对我来说非常完美,因为我已经有了视图模型和导航。


0
现在,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
}

如果您想要在事件中传递数据,可以使用Kotlin密封类来实现AppEvent,而不是枚举。
假设与您的片段无关的某个其他组件正在更新数据,一旦完成,它就可以发出一个事件:
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 架构。

确保你的事件总线实例是单例实例,以便所有消费者都发布和订阅同一个总线。你可以使用依赖注入框架来实现这一点,并按照该框架创建单例的指示进行操作。

你可以在我写的 关于此主题的博客文章 中详细了解事件总线模式。


嗨@Tina,你能帮我解决这个问题吗? - Kotlin Learner

0

这个方法对我有效:

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

0

以下代码在适配器中刷新一个片段

DownloadsAdapter 代码:

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();

...

}

0

设置你想要刷新的片段

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接