Method to refresh Fragment content when data changed ( like recall onCreateView)
Asked Answered
C

12

16

I have an Activity with a fragment container in layout. 3 different Fragment can be displayed in it. These fragments contains a Listview which displays data with custom Adapter i made.

So each list elements is created during onCreateView, after i queried a database to have data.

But sometimes some data may changes in my database, so i would like to redraw/recreate it the Listview.

  • What would be the best way (i mean, the less ressources demanding) to refresh my fragment view ?
  • Is there a method to recall onCreateView manually ?
Clown answered 2/9, 2015 at 17:38 Comment(5)
Use adapter.notifyDataSetChanged(). It is meant for the exact purpose you are mentioning.Mundt
This is what you want! https://mcmap.net/q/166351/-refresh-fragment-at-reloadHalla
@Mundt : No it doesn't work because i need to restart a query too. In fact, i have an object 'Query' and i need to relaunch this query ...and then yes, i can use adapter.notifyDataSetChanged()Clown
implement a callback(interface) in fragment & when data is changed call it.Bega
or try https://mcmap.net/q/582489/-refresh-or-force-redraw-the-fragmentBega
A
15

Detach and attach it with

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

or search fragment with

Fragment currentFragment = getActivity().getSupportFragmentManager().findFragmentById(R.id.container);
Alkmaar answered 2/9, 2015 at 17:42 Comment(4)
I can't do this - I would like to call the "refresh" from oustide the fragment's instance...from my custom adapter or even from MainActivity So it requires me to call a static method. And 'getFragmentManager' cannot be used in static method.Clown
I used your code through a Handler on my main activity, it allowed me to handle the "static" considerations. ThanksClown
#40096326 Please helpRiff
Well @Heretyk, I think you can use EventBus in that case.Quinlan
B
5

Combined two answers and removed if (isVisibleToUser), because it makes the setUserVisibleHint be called in an unpredicted asynchroneous order and fragment can either be refreshed or not. I found this piece of code stable (in your Fragment):

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

  // Refresh tab data:

  if (getFragmentManager() != null) {

    getFragmentManager()
      .beginTransaction()
      .detach(this)
      .attach(this)
      .commit();
  }
}
Brawner answered 9/6, 2018 at 17:32 Comment(2)
this is the best solution for meRoentgenology
Really!! It's deprecated guyz! You never know when an update on your user's mobile can cause crash on your app because of this method.Illuminance
C
4

There is one very useful method of Fragment, which can be used for refreshing 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(). 
    }
}
Corporal answered 28/2, 2018 at 6:40 Comment(0)
S
2

If you have problems with some of the methods listed above (as I had after uprgrading...), I recommend to make some kind of public refresh method in fragment and then simply call it, it is even less code, nicer and faster because fragment doesn't need to be reinitialized...

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

If you added fragment via code and used a tag string when you added your fragment, use findFragmentByTag instead:

Fragment fragment = fm.findFragmentByTag("yourTag");
if(fragment instanceof YourFragmentClass)
{
    ((YourFragmentClass)fragment).showPrayer();
}
Strainer answered 12/9, 2019 at 13:54 Comment(0)
W
1

If you followed the Material design tutorial, you can just navigate to the same page which will automatically reload it. Don't forget to add "false" by backstack because you don't have to return to the previous fragment because it was the same fragment.

((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();
    }
}
Warehouseman answered 9/11, 2021 at 14:7 Comment(0)
C
0

According to FragmentTransaction#replace it's the same as calling remove and then add. So you can use .replace with the fragment manager when you're starting the fragment or when you want to reload it. So use the same function from the onCreate as well as the place where you'd want to reload it...

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

This way there's only one function that loads the content and passes data. This assumes you have an entry in your layout with the ID content_frag_container. I used a FrameLayout.

Calie answered 7/2, 2019 at 20:51 Comment(0)
B
0

This method works for me:

MyCameraFragment f2 = new MyCameraFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, f2);
transaction.addToBackStack(null);
transaction.commit();
Bocage answered 19/4, 2019 at 6:19 Comment(0)
B
0

Following code refreshes A fragment from withIn an Adapter

DownloadsAdapter code:

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

...

}
Brittain answered 1/5, 2019 at 14:29 Comment(0)
H
0

set in Fragment that you want to refresh

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    super.setUserVisibleHint(isVisibleToUser)
    if(isVisibleToUser){
        if (getFragmentManager() != null) {
            getFragmentManager()
                ?.beginTransaction()
                ?.detach(this)
                ?.attach(this)
                ?.commit();
        }
    }
}
Heigl answered 29/5, 2020 at 14:3 Comment(0)
M
0

Nowadays, Android apps tend to implement the Android Navigation component (which simplifies fragment management and navigation), in which case it is recommended to take another approach than the other suggested answers to avoid directly manipulating fragments and interacting with the fragment manager. Performing a "refresh" to keep your fragment state up to date unnecessarily destroys and recreates the view when all you need to do is update your adapter data.

You could instead use an event-driven approach and implement a pattern like an Event Bus, to which consumers can publish and subscribe. Your component that is updating the data would be responsible for publishing an update-related event, while any components that care about the update can subscribe to these events.

Here is a simple EventBus class in Kotlin that leverages Kotlin language features like coroutines and flows to implement this described pattern:

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

In this case, AppEvent is an enum, for example:

enum class AppEvent {
  TODO_LIST_UPDATED
}

If you'd like to pass the data alongside the event, you could implement Kotlin sealed classes for your AppEvent instead of an enum.

Assuming some other component, unrelated to your fragment is updating the data, once it's complete it can emit an event:

eventBus.emitEvent(AppEvent.TODO_LIST_UPDATED)

Assuming you are using MVVM architecture, your fragment's view model can subscribe to the event bus when initialized:

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

Your fragment would observe the LiveData and set the data on the adapter once the new data is retrieved.

If you do not have a view model, you can do this directly in your fragment, using lifecycleScope though nowadays, having a view model and implementing MVVM architecture is recommended.

One important thing to make sure of is that your event bus instance is a singleton instance so that all consumers are publishing and subscribing to the same bus. You can achieve this by using a dependency injection framework and following the directions on creating singletons for that framework.

You can read more about the event bus pattern in detail on a blog post I wrote on this topic.

Mong answered 24/8, 2021 at 23:25 Comment(1)
Hey @Mong can you please help me on this issueFrenzied
P
0

#My project is constructed with fragments using Android Navigation#

MainList fragment calls up FilterDialog Fragment. After the program and the user finish there work in the Filter dialog, a return button is pushed returns to the MainList Fragment and refreshes it as requested. If you have a Viewmodel and are able to share date this is easy to do.

I give you the end first and then the chain of coding that leads up to it.

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

this pops the backstack and refreshes the return Fragment properly to reflect changes to the filter.

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

this works like a charm for me as I already had the viewmodel and navigation in place.

Pyrrhuloxia answered 24/11, 2021 at 0:52 Comment(0)
C
-1

When the data in a fragment changes, its always a good idea to detach and reattach the fragment into the framelayout. In my case, I have a listview which shows the favorite items of my users. Once the user unlike a product, I have to remove it from the list and reload the Favorites fragment.

So I did something like this to reload the fragment into the frame layout:

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

Here, frm is the frame layout in the MainActivity that hold the fragment and FavoritesFragment() is the fragment that needs to be reloaded.

The above code should be executed everytime the user press unlike button

Charioteer answered 14/10, 2018 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.