listView is not updated immediately - android

I have an Activity, which contains 2 dynamic Fragments (Exactly one of them is displayed at a time).
one of the Fragments (HasExploitsFragment) contains a listView.
When the activity resumes (the resume function has been called), (after an object added to the list)
the list (at the HasExploitsFragment fragment) is not updated..
The resume function is:
#Override
protected void onResume() {
super.onResume();
if (exploitTitleAndDescription.get(HasGroupsFragment.groupChosen)
.size() == 0) {
// Hide the fragment
FragmentManager fm = getFragmentManager();
fm.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in,
android.R.animator.fade_out)
.hide(hasExploitsFragment).commit();
addDynamicNoExploitsFragment();
} else {
//We enter this section, but despite that, the update is not shown immediately :(
FragmentManager fm = getFragmentManager();
fm.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in,
android.R.animator.fade_out)
.hide(noExploitsFragment).commit();
addDynamicHasExploitsFragment();
}
}
and the addDynamicHasExploitsFragment() is the following:
private void addDynamicHasExploitsFragment() {
hasExploitsFragment = new HasExploitsFragment();
getFragmentManager().beginTransaction()
.add(R.id.exploits_main_menu, hasExploitsFragment).commit();
}
When I exit the Activity and enters again, the list is updated.
How can I perform the update immediately (At the resume function details above)?

You need to call notifyDataSetChanged() to update listview.

Related

How to determine when a fragment is visible to the user

my app has one activity called MainActivity.
MainActivity loads 3 fragments, all added to back stack. The first one is automatically opened and is ListFragment. If I click on an item in ListFragment the DetailFragment is created. Inside DetailF I can click the edit button and EditFragment is opened. So the backstack is:
ListFragment
DetailFragment
EditFragment
Inside EditF one can change informations about the object that is being displayed in DetailFragment. Inside the onPause of EditF I save the chanegs
#Override
public void onPause() {
super.onPause();
mTask.setTitle(mTitleEditText.getText().toString());
mTask.setSummary(mSummaryEditText.getText().toString());
}
So when I hit the back button the chanegs are successfully saved (I see that from the logs). So DetailF appears but the changes are not shown: that's becuase DetailF has been previously loaded with the beggining data. Since the onresume doesn't get called when DetailF is visible I can't tell the fragment to update the data.
If I hit the back button another time I go back to the ListF and the updated data is also not shown.
This is how I create the fragemnts:
in MainActivity.java
private void launchListFragment(){
getSupportFragmentManager().popBackStack ("detail", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment newDetail = ListFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, newDetail, "LIST_F_TAG")
.commit();
}
in ListFragment:
Task taskToSend = mViewAdapter.getList().get(adapterPosition);
if(getActivity() != null){
Fragment newDetail = DetailFragment.newInstance(taskToSend);
getActivity().getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, newDetail)
.addToBackStack("detail")
.commit();
}
in DetailFragment:
private void launchEditFragment(Task task){
if(getActivity() != null){
Fragment newDetail = EditFragment.newInstance(task);
getActivity().getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, newDetail)
.addToBackStack("edit")
.commit();
}
}
I want that once I update the mTask data in EditFragment all other fragments display the updated data. Thanks.

Removing a fragment and then re-adding it

I've written a method, setLoading in my activity that is responsible for setting the "loading" status of my app. This method is responsible for instantiating a LoadingFragment, removing any existing instances of it (Using FragmentManager) and then depending on it's first parameter loading, adding it to one of two possible containers (Depending on the top parameter).
protected LoadingFragment loadingFragment;
public void setLoading(boolean loading, boolean top) {
FragmentManager fragmentManager = getFragmentManager();
// Create LoadingFragment instance if it has not already been created
if (loadingFragment == null || !(loadingFragment instanceof LoadingFragment)) {
loadingFragment = new LoadingFragment();
}
// Remove the fragment first if it is present
fragmentManager
.beginTransaction()
.remove(loadingFragment)
.commit();
// Only if loading is true should we display the fragment
if (loading) {
// Decide which container we're going to put the fragment in
int id = top ? R.id.topContainer : R.id.container;
// Place the fragment in the right position
fragmentManager
.beginTransaction()
.add(id, loadingFragment)
.commit();
}
}
public void setLoading(boolean loading) {
setLoading(loading, true);
}
I am triggering setLoading(true) from elsewhere in my activity and I have commented out it's corresponding setLoading(false) while testing.
What I want to happen is for my LoadingFragment to appear every time setLoading(true) is called. The first call shouldn't remove anything since it at that point it doesn't exist. All subsequent calls should remove the existing LoadingFragment and add it again.
What happens is that the first call to setLoading(true) does indeed create the LoadingFragment and put it in the correct container. However, subsequent calls to setLoading(true) remove the fragment, but it never seems to be re-added. I have checked to see that the fragment does indeed exist and is of type LoadingFragment at the point it is added and I have also checked to ensure that it's onCreateView method is being called.
Am I doing something wrong?
Edit
Using the answer given below by H Raval as a base I have now come up with the following:
public void setLoading(boolean loading, boolean top) {
FragmentManager fragmentManager = getFragmentManager();
Fragment currentLoadingFragment = fragmentManager.findFragmentById(R.id.loadingFragment);
if (currentLoadingFragment != null) {
fragmentManager
.beginTransaction()
.remove(currentLoadingFragment)
.commit();
}
if (loading) {
int id = top ? R.id.topContainer : R.id.container;
fragmentManager
.beginTransaction()
.add(id, new LoadingFragment())
.commit();
}
}
This seems to work as expected. It seems that the primary difference is that this code is creating a new LoadingFragment instance each time (When loading = true) whereas originally I was trying to use the same instance and just adding/removing it using the FragmentManager.
Out of interest, is there a reason I need to create a new instance after using remove? Is this the correct way to do it? Or should it still work when using the same instance? Additionally, if it's recommended to create a new instance each time, is there anything I should do in terms of clean-up, freeing up resources etc. (Perhaps there's a way of gracefully destroying the obsolete instances)?
well i have made some changes in your code and works perfect for me..let me know if you face any difficulty
public void loadFragment(boolean loading, boolean top){
FragmentManager fragmentManager = getSupportFragmentManager();
loadingFragment = new LoadingFragment();
// Only if loading is true should we display the fragment
if (loading) {
// Decide which container we're going to put the fragment in
int id = top ? R.id.topContainer : R.id.container;
if(top){
if(fragmentManager.findFragmentByTag("loadingFragment")!=null)
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("loadingFragment")).commit();
fragmentManager
.beginTransaction()
.replace(R.id.topContainer, loadingFragment,"toploadingFragment")
.commit();
}else{
if(fragmentManager.findFragmentByTag("toploadingFragment")!=null)
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("toploadingFragment")).commit();
fragmentManager
.beginTransaction()
.replace(R.id.container, loadingFragment,"loadingFragment")
.commit();
}
}

Fragment blank after popBackStack

I have this Activity in which I replace the main fragment with a preference fragment. When I click back after looking at the preferences, I get a blank (white) area where my fragment should be. If I rotate the screen then it works just fine. Everything in my fragment appears to be ok except for it is blank. Here are my methods:
The onCreate method of the activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
.....
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(MAIN_CONTAINER, new MainFragment())
.commit();
}
}
The starting of the preferences fragment:
public void startPreferencesFragment() {
FragmentManager mFragmentManager = getFragmentManager();
FragmentTransaction mFragmentTransaction = mFragmentManager
.beginTransaction();
MyPreferencesFragment mPrefsFragment = new MyPreferencesFragment();
mFragmentTransaction.addToBackStack(null)
.replace(MAIN_CONTAINER, mPrefsFragment)
.commit();
}
The onBackPressed of my activity:
#Override
public void onBackPressed() {
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return;
}else {
super.onBackPressed();
}
}
So what am I doing wrong?
Thanks.
EDIT: If I open preferences and then rotate and then press back, it all works fine.
Also I should mention that removing the onBackPressed method does not fix the issue, it just exits the app.
EDIT: Turned out to not be a problem with the fragment back stack at all. Basically my fragment has a recyclerview and that is all it has. The instance of the adapter I was setting on the recyclerview was being kept while the recyclerview itself was new when the fragment was brought back from the back stack and I was checking whether the adapter was null when setting it.
You are already adding the transaction to the backstack, there is not need to override onBackPressed(); the framework will pop the Fragment out of the stack automatically when the back button is pressed. I am pretty sure that you are "double" popping the backstack.

Android - fragment onActivityCreated() not getting called after .replace

My main application has a blanck FrameLayout the reason for this is so that I can on the go, add and replace fragments to it...
I have had no issue with this until now. I have successfully added the first fragment and the onCreate gets called etc, then in my code using a button I replace the FrameLayout with my second fragment which seems to execute, however not showing? as onActivityCreated() does not get called.
My main application calls FrameLayout xml file known as send_activity
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
sendControlFragment sendControlFragment = new sendControlFragment();
peerItemFragment fragmentList = new peerItemFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.send_activity);
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
sendControlFragment controlFragment = new sendControlFragment();
// Add the fragment to the 'fragment_container' FrameLayout
fragmentTransaction.add(R.id.fragment_container, controlFragment).commit();
}
}
The above works perfectly...
The button which I call the second fragment, I got told to initiate a new FragmentTransaction which I have done, however this does not call the onActivityCreated() and skips to fragmentList.addPeers(mService.peerList);
This is great that it is calling the addPeers() method however without the onActivityCreated() then it crashes!
public void authenticateClick(View v) {
mService.peerSearch();
peerItemFragment peerFragment = new peerItemFragment();
FragmentTransaction fragmentTransactionauthen = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransactionauthen.replace(R.id.fragment_container, peerFragment);
fragmentTransactionauthen.addToBackStack(null);
// Commit the transaction
fragmentTransactionauthen.commit();
//pass peer list to fragment to display
fragmentList.addPeers(mService.peerList);
}
Can you please suggest a solution as .replace currently to my knowledge doesnt call the onActivityCreated() method...
Because i was using a listfragment I had to replace my listView id to the following, as this was causing fatal error!
android:id="#android:id/list"
it now works and replaces fragments.

Android: keep Fragment running

Is it possible to keep a Fragment running while I call replace from FragmentManager to open a new Fragment?
Basically I don't want to pause a Fragment when navigating (via replace method) to another Fragment.
Is it possible?
Or the correct approach is, always, instantiate a new Fragment every time I need to open it and restore its previous state?
Thanks!
FragmentManger replace method will destroy the previous fragment completely, So in each transaction onDestroyView(), onDestroy() and onDetach() will get called on previous fragment. If you want to keep your fragment running you can instead use FragmentManger hide() and show() methods! It hides and shows the fragments without destroying them.
so first add both fragments to fragment manager and also hide the second fragment.
fragmentManager.beginTransaction()
.add(R.id.new_card_container, FragmentA)
.add(R.id.new_card_container,FragmentB)
.hide(FragmentB)
.commit();
Note that you can only call show() on hidden fragment. So here you can't call show() on FragmentA but it's not a problem because by hiding and showing FragmentB you can get replacement effect you want.
And here is a method to go back and forth between your fragments.
public void showOtherFragment() {
if(FragmentB.isHidden()){
fragmentManager.beginTransaction()
.show(FragmentB).commit();
} else {
fragmentManager.beginTransaction()
.hide(FragmentB).commit();
}
}
Now if you put log message in fragment callback method you will see there is no destruction (except for screen orientation change!), even view will not get destroyed since onDistroyView doesn't get called.
There is only one problem and that is, first time when application starts onCreateView() method get called one time for each fragment (and it should be!) but when the orientation changes onCreateView() gets called twice for each fragment and that's because fragments once created as usual and once because of there attachment to FragmentManger (saved on bundle object) To avoid that you have two options 1) detach fragments in onSaveInstaneState() callback.
#Override
protected void onSaveInstanceState(Bundle outState) {
fragmentManager.beginTransaction()
.detach(FragmentA)
.detach(FragmentB)
.commit();
super.onSaveInstanceState(outState);
}
It's working but view state will not get updated automatically, for example if you have a EditText its text will erase each time orientation change happens. of course you can fix this simply by saving states in the fragment but you don't have to if you use the second option!
first i save a Boolean value in onSaveInstaneState() method to remember witch fragment is shown.
#Override
protected void onSaveInstanceState(Bundle outState) {
boolean isFragAVisible = true;
if(!FragmentB.isHidden())
isFragAVisible = false;
outState.putBoolean("isFragAVisible",isFragAVisible);
super.onSaveInstanceState(outState);
}
now in activity onCreate method i check to see if savedInstanceState == null. if yes do as usual if not activity is created for second time. so fragment manager already contains the fragments. So instead i'm getting a reference to my fragments from fragment manager. also i make sure correct fragment is shown since its not recovered automatically.
fragmentManager = getFragmentManager();
if(savedInstanceState == null){
FragmentA = new FragmentA();
FragmentB = new FragmentB();
fragmentManager.beginTransaction()
.add(R.id.new_card_container, FragmentA, "fragA")
.add(R.id.new_card_container, FragmentB, "fragB")
.hide(FragmentB)
.commit();
} else {
FragmentA = (FragmentA) fragmentManager.findFragmentByTag("fragA");
FragmentB = (FragmentB) fragmentManager.findFragmentByTag("fragB");
boolean isFragAVisible = savedInstanceState.getBoolean("isFragAVisible");
if(isFragAVisible)
fragmentManager.beginTransaction()
.hide(FragmentB)
.commit();
else
fragmentManager.beginTransaction()
.hide(FragmetA) //only if using transaction animation
.commit();
}
By now your fragment will work perfectly if are not using transaction animation. If you do, you also need to show and hide FragmentA. So when you want to show FragmentB first hide FragmentA then show FragmentB (in the same transaction) and when you want to hide FragmentB hide it first and also show FragmentA (again in the same transaction). Here is my code for card flip animation (downloaded from developer.goodle.com)
public void flipCard(String direction) {
int animationEnter, animationLeave;
if(direction == "left"){
animationEnter = R.animator.card_flip_right_in;
animationLeave = R.animator.card_flip_right_out;
} else {
animationEnter = R.animator.card_flip_left_in;
animationLeave = R.animator.card_flip_left_out;
}
if(cardBack.isHidden()){
fragmentManager.beginTransaction()
.setCustomAnimations(animationEnter, animationLeave)
.hide(cardFront)
.show(cardBack)
.commit();
} else {
fragmentManager.beginTransaction()
.setCustomAnimations(animationEnter,animationLeave)
.hide(cardBack)
.show(cardFront)
.commit();
}
}

Categories

Resources