I have a fragment activity that contains a Fragment , the Fragment starts an Asynctask that downloads some data, I have implemented a callback method in my Fragment that updates some values in an adapter and a listview. The problem I have is the following: This is my onCreateView method(important part):
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
list=(PullToRefreshListView)v1.findViewById(R.id.listapull);
adapterList=new ListViewAdapter(secciones, mContext);
}
when I rotate the device while the AsyncTask is running, the doInBackground() method keeps running, then on post execute it triggers the listener and starts the callback method in my fragment, this method has the old references of my adapter and my list view:
The fragment and its content are recreated when an orientation change happens and that is correct, but does anyone knows why the call back method is keeping the reference to the adapter and listview that where created before the orientation change?
EDIT:
I have a button that executes the asynctask like this:
asyncRefresh = new PullRefreshTask(taskContext, mContext, secciones);
asyncRefresh.setUpdatePull2RefreshListener(this);
asyncRefresh.execute();
If the user press the button the asyncTask will set old Fragment as the listener and when an orientation change occurs while the asynctask is running, I thought the activated callback method was the one from the newly created fragment method but I'm not sure anymore.
EDIT 2:
I have resolved my problem, as i said in my first edit the call back method was being called for the old fragment . so what I did was to save my asynctask in a variable in another class named "Info" and in on create view i did this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
list=(PullToRefreshListView)v1.findViewById(R.id.listapull);
adapterList=new ListViewAdapter(secciones, mContext);
PullRefreshTasktask task = Info.getAsyncTask();
asyncRefresh = task;
asyncRefresh.setUpdatePull2RefreshListener(this);
}
This way I set the new reference of my fragment in my
setUpdatePull2RefreshListener()
method of the running asynctask
...does anyone knows why the call back method is keeping the reference
to the adapter and listview that where created before the orientation
change?
(This answer comes without knowing your callback implementation or how your AsyncTask looks)
Why the callback shouldn't keep the reference to the old fragment? You set in that Button's listener the current Fragment instance and then do the device rotation while the task runs. You don't have any code in your Fragment that, as it is reconstructed after the configuration changes, will update the callback instance in your AsyncTask to point to the new fragment. Depending on how you use that Fragment you could use the Fragment.setRetainInstance() method which will prevent the Fragment to be destroyed so your task will have the same callback instance even after the rotation. Also have a look at an answer from one of the Android engineers regarding this particular problem.
Related
Imagine that you have 2 fragments connected to one (or more) viewModel(s) and inside of activity you'll switch between them. Once you open fragment, viewModel works as expected, so I start listening for changes from onCreate method, code example:
viewModel = new ViewModelProvider(requireActivity(), new InventoryTasksFactory()).get(InventoryTasksViewModel.class);
viewModel.inventoryTasksResponse().observe(this, new Observer<Response<List<InventoryTask>>>() {
#Override
public void onChanged(Response<List<InventoryTask>> listResponse) {
handleResponse(listResponse);
}
});
But when you switching to another fragment and going back, fragment becomes blank. I understand that fragment listening changes inside of viewModel, and you should manually getting value from viewModel and I get value from viewModel inside of onCreateView method, code example:
Response<List<InventoryTask>> inventory = viewModel.inventoryTasksResponse().getValue();
if (inventory!=null){
handleResponse(inventory);
}
Problem is that Response has 3 states: Running, Success, Error, and depends on those states view is updating. So, in first fragment opening, view updating twice and it leads to skipping frames and display blinking.
I was thinking about keeping data inside of fragment, but I want to avoid data duplicating. Besides of that, in case of sharedViewModel, you'll get issues about updating data inside of fragment!
Please, help me!
Observing your data from onViewCreated(View view, Bundle savedInstanceState) might work out.
I have this code:
public class CrimeListFragment extends Fragment {
private RecyclerView mCrimeRecyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mCrimeRecyclerView = (RecyclerView) view
.findViewById(R.id.crime_recycler_view);
mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return view;
}
}
I know that when creating fragment, you separately implement onCreate() and onCreateView(). However, onCreate() is obviously missing here.
Why is that?
you separately call onCreate() and onCreateView()
No, you don't call either. The Fragment lifecycle calls them.
onCreate is not needed to be implemented on a simple Fragment class, only Activity classes
As for the title of your question - it should be called if you add that Fragment to an Activity.
Both onCreate() and onCreateView() can be overridden by you.
onCreate() is more optional and you can use to instantiate some variables (but you hardly need to override it).
onCreateView() is mandatory since you must inflate the view that you want and return it (like return view;).
In the docs:
onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
onCreateView()
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
You can also check the Fragment Life Cycle.
As you can see in picture below, in case of returning from back stack, only onCreateView() is called again... So, you can use onCreate() to run some code that can be executed only once (when Fragment is created... like configure some array or something like that)...
Then, you leave onCreateView() just to refresh/inflate the Views before displaying it to the user.
But again: onCreate() is not usually overriden and there's no problem with that... Is always up to you...
:
Just need to know the proper syntax to accessing an array that was created in a different class.
public class item_fragment extends Fragment {
ArrayList<MyItems> mylist;
#Override
public View onCreateView( LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
mylist = ((MyApplication) getActivity().getApplication()).getItemsArrayList();
return inflater.inflate(R.layout.course_work_item_fragment,container, false);
}//ends onCreate View
}
I'd suggest having a look at Pass ArrayList from fragment to another fragment(extends ListFragment) using bundle, seListAdapter runtime error
This includes
One thing if you read your code carefully, you have declared ArrayAdapter in Monday_fragment, so this list initialize every time when you replace this fragment with other. So just create a ArrayAdapter in MainActivity and getter, setter for the same and change your methode ArrayList toStringList(Collection entryLogs) in the Monday_fragment like below
That will work fine. Except as a caveat. This might well be called before your activity is created. If you look at the fragment lifecycle you'll see.
The core series of lifecycle methods that are called to bring a fragment up to resumed state (interacting with the user) are:
onAttach(Activity) called once the fragment is associated with its activity.
onCreate(Bundle) called to do initial creation of the fragment.
onCreateView(LayoutInflater, ViewGroup, Bundle) creates and returns the view hierarchy associated with the fragment.
onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
onViewStateRestored(Bundle) tells the fragment that all of the saved state of its view hierarchy has been restored.
onStart() makes the fragment visible to the user (based on its containing activity being started).
onResume() makes the fragment begin interacting with the user (based on its containing activity being resumed).
You will note that onViewCreated can easily predate the activity created. As such if you say re-initialize that array, your fragment would have an empty array that never got anything added to it. Or if you checked the size before the onActivityCreated() is called you would still have 0 array size assuming you give the array values after it's done initializing.
Here is my problem area:
I have a Fragment A. Once it is attached, in its onCreateView, I load a webservice to fetch the data from the server and after that I set that data on the list view using a Base Adapter. Now on the Item Clicks of the list view I replace the Fragment A with Fragment B using replace Methods of the Fragment Transactions and addtoBackstack("FragmentA").
FragmentManager fm =getActivity().getFragmentManager();
fm.beginTransaction().replace(R.id.content_frame, Fragment B).commit();
Now here when I press back button on Fragment B, it takes me to Fragment A but the webservice again starts loading.
My Problem: I just want that when it returns to Fragment A, it should show its previous state and should not call the webservices again.
Thanks
OnCreateView for a fragment runs on the creation of the view every time it needs to be drawn. By going back you are causing the view to be recreated and hence the webservices are loading again.
I believe that if you only want the web services to load once then you could move the code to the "onCreate" method instead, but its probably a better idea to move this code to "onResume" instead and include some logic that checks whether you need to load your webservices again or not.
This way everytime the fragment is paused and then loaded again you could ensure that the fragment still has everything it needs.
(source: xamarin.com)
EDIT:
So for example you could have
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
if (data == null) { //Or list is empty?
getWebData()
}
}
I have an activity, its layout contains a FrameLayout. I use the framelayout as a fragment container. I replace the fragments in the FrameLayout using FragmentManager transactions.
In one of the fragment's onCreate method I register the fragment with the EventBus using.
#Override
public void onCreate(){
EventBus.getDefault().register(this);
// other initialization code
}
The fragment has a GridView in its layout. Whenever an item in the gridView is clicked I post an event to the EventBus
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.fragment_category, container, false);
gridView = (GridView) rootView.findViewById(R.id.categry_grid_view);
gridAdapter = new CustomGridAdapter(getActivity());
gridView.setAdapter(gridAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Category clickedCategory = gridAdapter.getItem(position);
EventBus.getDefault().post(new MyEvent());
}
});
The event handler methods for this event are in the same fragment i.e. The fragment has the following method
public void onEvent(MyEvent e){
//some code;
}
This works fine until the app loses focus and becomes inactive (due to pressing the home button or screen lock). When I make the app active again, the event handlers for the events are not called. I can see the following statements in the LogCat
com.example.app D/Event﹕ No subscribers registered for event class com.example.app.MyEvent
com.example.app D/Event﹕ No subscribers registered for event class de.greenrobot.event.NoSubscriberEvent
Can someone tell me what am I doing wrong here?
Edit 1:
When the application becomes inactive due to screen lock or the home button being pressed, the fragment's onStop method is called. The code to unregister the fragment from the EventBus is in the onStop method. When application becomes active again, the fragment's onStart and onResume methods are called. So I moved my code to register the fragment with the EventBus in its onStart method.
#Override
public void onStart(){
super.onStart();
EventBus.getDefault().register(this);
}
I put some log statements to check if the onStart method is actually called when the application become active. It is being called. Still things are not working when the application becomes inactive and then active again.
Edit 2
I forgot to mention that the activity which contains this fragment also subscribes to EventBus. The code to register the activity with the EventBus is in its onCreate method and the code to unregister the activity is in its onStop method.
The activity which contained this fragment also subscribed to EventBus. The code to register the activity with the EventBus was in its onCreate method and the code to unregister the activity was in its onStop method.
When the application was becoming inactive (due to screen lock or pressing the Home Button) the containing activity's onStop method was being called and it was getting unregisterd from the EventBus. For some reason its containing fragments were also getting unregistered (I am not sure why). Re-registering the fragments with the EventBus did not work.
I solved this problem by moving the code to unregister the containing activity to its onDestroy method.
I am still not sure why doing this works, but atleast it solves my current problem. If someone has an explanation or better insight please comment or post an answer.
For me the problem was something else, I tried to do a post to a fragment that was not yet committed. You will receive the same 2 errors as stated in the question.
So I did a getSupportFragmentManager().executePendingTransactions(); before calling the post and that fixed the problem for me.