I'm having an issue with adding a child fragment to another fragment in my android app.
In the parent fragment I have something like this:
#Override
public View onCreateView(blah) {
Thread initializeStateThread = new Thead() {
public void run() {
// Make some requests to get some data that determines
// which child fragment to display
state = // Some state that corresponds to a child frag
// Method that set's the child fragment based on |state|
setChildFragment(state);
}
}
initializeStateThread.start()
// Do a bunch of other stuff
return view;
}
setChildFragment(State state) {
Fragment fragment = null;
switch (mState) {
case CASE_1:
fragment = new DriverFindSpotFragment();
break;
case CASE_2:
fragment = new DriverNavigationFragment();
break;
case CASE_3:
fragment = new DriverArriveSpotFragment();
break;
}
getActivity().runOnUiThread(new Runnable() {
#Ovverride
public void run() {
getChildFragmentManager().beginTransaction().
replace(R.id.driver_fragment_container, fragment).commit();
};
}
However, this only works about 50% of the time. The other 50% of the time the child fragment just doesn't show up (there isn't an error or anything). I think the problem is related to the fact that I'm attempting to add a child fragment after the parent's view is returned form onCreateView.
I've noticed that if I force the main thread to sleep for a second or two, the issue goes away (I assume because the child fragment gets set before the view is returned in that case).
Has anyone ever run into a similar issue? Or can anyone confirm my theory as to why this issue is happening?
Try by adding your Fragment transaction in Main thread.
Like this :
runOnUiThread(new Runnable() {
#Override
public void run() {
getChildFragmentManager().beginTransaction().replace(R.id.driver_fragment_container, fragment).commit();
}
});
You can try as following method define it in fragment and replace with position/state
public void displayView(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FormFragment();
break;
case 1:
fragment = new HomeFragment();
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction transaction = fragmentManager
.beginTransaction();
if (position != 0)
// transaction.addToBackStack("back");
transaction.replace(R.id.main_ll_container, fragment).commit();
} else {
Log.e("MainActivity", "Error in creating fragment");
}
}
After reading the comments and other answers, I am not sure what and where is the actual problem.
Instead of:
getActivity().runOnUiThread(new Runnable() {
#Ovverride
public void run() {
}
});
Use:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Ovverride
public void run() {
}
});
This is just a wild guess, most probably it will work.
Related
I've already searched about this and found nothing, which helps me to solve my problem. In my Code I have a SpaceNavigationView (like BottomNavigationView) with five Fragments.
So in Fragment A, I've put a Recyclerview. If an item of the Recyclerview gets clicked, it will replace the current fragment with a new child fragment B.
In Fragment B I've set a Chronometer, which should count the time, when it gets pressed.
Now if I switch from Fragment B to Fragment C and go back to Fragment B, the Chronometer starts by zero, because the fragment was replaced.
I've tried to used onSaveInstanceState, so that it can be called when Fragment is recreated, but this doesn't work for me.
Here's a piece of the HomeActivity, which includes all the Fragments.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
init();
setFragment(fragmentHome);
navigationView.initWithSaveInstanceState(savedInstanceState);
navigationView.addSpaceItem(new SpaceItem("", R.drawable.bottom_baby));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.bottom_advise));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.ic_favorite_black_24dp));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.ic_settings));
navigationView.setSpaceOnClickListener(new SpaceOnClickListener() {
#Override
public void onCentreButtonClick() {
setFragment(fragmentPlayground);
navigationView.setCentreButtonSelectable(true);
}
#Override
public void onItemClick(int itemIndex, String itemName) {
switch (itemIndex) {
case 0:
setFragment(fragmentHome);
return;
case 1:
setFragment(fragmentAdvising);
return;
case 2:
setFragment(fragmentMemories);
return;
case 3:
setFragment(fragmentSettings);
return;
default:
setFragment(fragmentHome);
return;
}
}
#Override
public void onItemReselected(int itemIndex, String itemName) {
Toast.makeText(HomeActivity.this, itemIndex + " " + itemName, Toast.LENGTH_SHORT).show();
}
});
}
private void setFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.container, fragment);
fragmentTransaction.commit();
}
So if i navigate now to FragmentHome and use the OnClickListener for Reycleritems, I will switch to Fragment_Chronograph
#Override
public void onItemClick(int position) {
switch (position) {
case 0:
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment_chronograph).commit();
}
So now I'm in Fragment_Chronograph and want to save the base for Chronograph. I will save the variable in onSavedInstanceState, which gets called when Activity is Paused.
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
startTime = SystemClock.elapsedRealtime() - chronometerLeft.getBase();
outState.putLong(CHRONOLEFT_TIME_SAVE_ID,startTime);
super.onSaveInstanceState(outState);
#Override
public void onPause() {
super.onPause();
onSaveInstanceState(new Bundle());
}
At the end i've put this code for restore in the OnCreate Method:
if (savedInstanceState != null) {
startTime = savedInstanceState.getLong(CHRONOLEFT_TIME_SAVE_ID,0);
chronometerLeft.setBase(startTime - SystemClock.elapsedRealtime());
chronometerLeft.start();
The OnSaveInstanceState gets called, but in the OnCreate Method it won't be called. I would be very thankful if someone could help me with this problem. I'm searching for days and didnt get a solution.
in set fragment method , first find fragment with id and then check if it's not null replace that
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
if (fragment == null) {
fragment = new PlaceholderFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, TAG_PLACEHOLDER)
.commit();
Note : you must add tag to your fragments
#Arvin
FragmentHome ist initiated in the init() method;
private void init() {
navigationView = findViewById(R.id.space);
fragmentHome = new FragmentHome();
fragmentAdvising = new FragmentAdvising();
fragmentMemories = new FragmentMemories();
fragmentSettings = new FragmentSettings();
fragmentPlayground = new FragmentPlayground();
============= UPDATE =================
Problem was solved. I didn't have to use OnSavedInstanceState. I used a Helperclass to store the variable of the Chronometerbase. In OnCreate method i check if the variable is not null, then restore the before saved base.
I want to save fragment state so I use fragment .add() and fragment .show() methods.
the part I use to add fragments in my mainActivity is
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.main_content, fragment, CURRENT_TAG);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
On back pressed in the fragment and getting back to it the get fragment by tag returns null
if (getSupportFragmentManager().findFragmentByTag(CURRENT_TAG) != null) {
drawerLayout.closeDrawers();
Runnable mPendingRunnable = new Runnable() {
#Override
public void run() {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
hideTransactions(CURRENT_TAG);
fragmentTransaction.show(getSupportFragmentManager().findFragmentByTag(CURRENT_TAG)).commit();
}
};
although onBackPressed the state of the fragment is right(TAG_PREV returns not null) in the on back pressed method
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawers();
return;
}
if (CURRENT_TAG == TAG_HOME) {
return;
}
navItemIndex = 0;
TAG_PREV = CURRENT_TAG;
CURRENT_TAG = TAG_HOME;
loadHomeFragment();
super.onBackPressed();
}
addToBackStack saves the transaction, in this case: fragmentTransaction.add(R.id.main_content, fragment, CURRENT_TAG); and when you press back(onBackPressed) you are reversing that transaction. So that will imply you are removing the previously added fragment.
It is not possible to maintain entire fragment in back stack, There is no provision given by android teams.
You have to use onSaveInstanceState for saving current state/data when you are leaving fragment.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, " onSaveInstanceState.");
savedInstanceState.putString("greeting", "Hello");
}
After that when again coming on that fragment you have to use.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String greeting = (savedInstanceState != null) ? savedInstanceState.getString("greeting") : "null";
Log.i(TAG, " onCreate: " + greeting);
}
Please let me if it dosen't work for you.
Here I've nested TabLayouts inside a Fragment which is working so fine for me but not I just want to know how I can switch Tabs of TabLayouts when MainTabFragment is not in front of app. I searched for a long time and I found it's possible with findFragmentByTag but I don't know how I should use it in my case where I have to switch Tabs too. For more I'm writing my code too.
In MainActivity I'm switching Tabs when MainTabFragment is in front and calling that Fragment when it's not in front like:
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.frame_container);
if (currentFragment instanceof MainTabFragment) {
//Main Fragment
MainTabFragment.pager.setCurrentItem(1);
// Child Fragment of Main Fragment
TopAdvisoryPagerFragment.pager.setCurrentItem(2);
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
fragment = new MainTabFragment();
changeFragments();
}
}, 150);
}
mDrawerLayout.closeDrawer(mDrawerList);
public void changeFragments() {
if (fragment != null) {
mPendingRunnable = new Runnable() {
#Override
public void run() {
FragmentManager fragmentManager = getSupportFragmentManager();
final Bundle bundle = new Bundle();
bundle.putInt("pos_next", second_position);
bundle.putInt("pos_end", third_position);
fragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment, "TOP").addToBackStack(null).commit();
}
};
if (mDrawerLayout.isDrawerOpen(mDrawerList)) {
mDrawerLayout.closeDrawer(mDrawerList);
}
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
If I'm not wrong,
fragmentManager.beginTransaction().replace(com.cws.advisorymandi.R.id.frame_container, fragment, "TOP");
Tag refer to "TOP", if you have different fragment using the same tag, it would be difficult to switch. You could possibly change the code to
fragmentManager.beginTransaction().replace(com.cws.advisorymandi.R.id.frame_container, fragment, fragment.getClass().getSimpleName());
In this scenario, the tag will be your fragment class name.
How to return a value from Asynctask (Different class) to activity back from which you called the Asynctask , here i have followed the intsruction given in following link How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class? #HelmiB's
I have done everything and its returning the result to activity's method processFinish() also , the problem is lost the activity control or focus, i could not do further actions using the Asynctask's result, as because all my activity's members becomes null.
How to proceed ?
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (pDialog != null)
{
pDialog.cancel();
}
if (StringUtil.hasValue(responseXmlString))
{
if (Integer.parseInt(AppUtil.getXpathValue("Result/ErrorNo",AppUtil.buildDocument(responseXmlString))) == 0)
{
asyncResponse.processFinish(responseXmlString);
}
}
#Override
public void processFinish(Object output)
{
Log.d("Response From Asynchronous task:", (String) output);
displayView(position,(String) output);
}
private void displayView(int position,String responseXml)
{
// update the main content by replacing fragments
boolean isFragment = true;
Fragment fragment = null;
/*//For Handling back press
if (getIntent().getBooleanExtra("FromPassBook", false) )
{
getIntent().putExtra("FromPassBook", false);
position = 7;
}
if (getIntent().getBooleanExtra("toCustomerAcocunts", false) )
{
getIntent().putExtra("toCustomerAcocunts", false);
position = 1;
}*/
switch (position)
{
case 0:
fragment = new ChartFragment();
setTitle(getResources().getString(R.string.wealth));
break;
default:
break;
}
if (fragment != null && isFragment)
{
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft =fragmentManager.beginTransaction();
fragmentStack.push(fragment);
//passing data to fragment
if (StringUtil.hasValue(responseXml))
{
Bundle bundle = new Bundle();
bundle.putString("responseXml", responseXml);
fragment.setArguments(bundle);
}
ft.replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
listView.setItemChecked(position, true);
listView.setSelection(position);
mDrawerLayout.closeDrawer(listView);
}
else
{
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
private void callService(int position)
{
String input = "";
AsyncCallWS asyncCallWS;
switch (position)
{
case 0:
input = "<Parm><ProcessID>101</ProcessID><MobileNo>" + mobileNumber + "</MobileNo></Parm>";
asyncCallWS = new AsyncCallWS(MainActivity.this, input, position,new MainActivity());
asyncCallWS.execute();
break;
}
Asynctask class constructor
`AsyncResponse asyncResponse;
public AsyncCallWS(Context context,String input,int position,AsyncResponse response )
{
this.context = context;
this.inputToservice = input;
this.position = position;
asyncResponse = response;
}`
Well, your problem is in this line of code:
asyncCallWS = new AsyncCallWS(MainActivity.this, input, position,new MainActivity());
Or, particularly, in the statement new MainActivity(). You create a new instance of MainActivity class here and then you use it as a callback. Obviously, it will have all the fields non-initialized. Use MainActivity.this instead of new MainActivity(). And please remember that Android manages all of your activities. You never want to create one yourself.
I'm using the native Android drawer for my menu, here in MainActivity :
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
//Home
fragment = new EntryFragment();
break;
case 1:
//Journal
fragment = new JournalFragment();
break;
case 2:
// Medications
fragment = new RxFragment();
break;
case 3:
// Reminders
fragment = new ReminderFragment();
break;
case 4:
// Counselor Locator
fragment = new LocatorFragment();
break;
case 5:
// Library
fragment = new LibraryFragment();
break;
case 6:
// Settings
break;
case 7:
// About
break;
default:
break;
}
if (fragment != null) {
fm.beginTransaction()
.replace(R.id.container, fragment).addToBackStack(String.valueOf(position)).commit();
String fragmentTitles[] = getResources().getStringArray(R.array.menu_array);
if(mActionBar != null) {
mActionBar.setTitle(fragmentTitles[position]);
}
} else {
Log.e("MainActivity", "Error in creating fragment");
}
But I'm repurposing EntryFragment for another activity (EntryDetailActivity), so in EntryFragment I defined a couple of methods to modify the view based on which activity is hosting it:
// Runs on the home page, shows/hides appropriate views
public void prepareForHomeView() {
mTxtDate.setVisibility(View.GONE);
mBtnDiffDay.setVisibility(View.GONE);
mEdtNotes.setVisibility(View.GONE);
mBtnSave.setVisibility(View.GONE);
mBtnDelete.setVisibility(View.GONE);
}
// Runs on the detail page, shows/hides appropriate views
public void prepareForDetailView() {
mBtnLog.setVisibility(View.GONE);
}
In EntryDetailActivity, I can easily call prepareForDetailView() in onResumeFragments(), but I'm having an issue of where to call prepareForHome() in MainActivity, since I am swapping fragments in and out based on the menu. Any suggestions?
/** Update **/
I assigned the fragments a tag that is the position of the menu through addToBackstack(). I also added the following method to my MainActivity:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
FragmentManager fm = getSupportFragmentManager();
EntryFragment entryFrag = (EntryFragment)fm.findFragmentByTag("0");
if(entryFrag != null) {
Log.v("rx", "Home frag is not null");
} else {
Log.v("rx", "Home frag is null");
}
}
Activity (or FragmentActivity) has onAttachFragment(Fragment fragment) - you could do an instanceof on fragment to determine if it is the correct type of fragment (or use the id, tag, etc) and call the appropriate method.
Alternatively, you could have the Fragment check the getActivity() (in onCreateView / onViewCreated).