my app contains a fragment with a webview and a edit text.i have a navigation drawer with 3 choices and use a switch condition to handle selection,on 3 selections i update the same webview with different html files using the foolowing code ..but my app crashes..
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
Fragment fragment;
switch (position) {
case 0:
WebView wv = (WebView) findViewById(R.id.webView);
wv.loadUrl("file:///android_asset/src/web.html"); //app crashes
fragment = getFragmentManager().findFragmentByTag(WebFragment.TAG);
if (fragment == null) {
fragment = new WebFragment();
}
getFragmentManager().beginTransaction().replace(R.id.container, fragment, WebFragment.TAG).commit();
break;
case 1: fragment = getFragmentManager().findFragmentByTag(WebFragment.TAG);
if (fragment == null) {
fragment = new WebFragment();
}
getFragmentManager().beginTransaction().replace(R.id.container, fragment, WebFragment.TAG).commit();
break;
case 2: fragment = getFragmentManager().findFragmentByTag(WebFragment.TAG);
if (fragment == null) {
fragment = new WebFragment();
}
getFragmentManager().beginTransaction().replace(R.id.container, fragment, WebFragment.TAG).commit();
break;
}
}
my logcat logs http://pastebin.com/snbzASTq
please provide a workaround for this
Related
With Fragment:setRetainInstance(true); the fragment is not re-instantiated on a phones orientation change.
And of course i want my fragments to be kept alive while switching from one fragment to another.
But the Android Studio 4 provides a wizard-template with only
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
From hours of debugging and searching the net if think it would need to inherent from the class FragmentNavigator so i can overwrite FragmentNavigator:naviagte where a new fragment gets created via final Fragment frag = instantiateFragment(.. and then is added with ft.replace(mContainerId, frag);
So i could find my old fragment and use ftNew.show and ftOld.hide instead.
Of course this is a stupid idea, because this navigate method is full of other internal stuff.
And i have no idea where that FrameNavigator is created.
I can retrieve it in the MainActivity:OnCreate with
NavigatorProvider navProvider = navController.getNavigatorProvider ();
Navigator<NavDestination> navigator = navProvider.getNavigator("fragment");
But at that time i could only replace it with my derived version. And there is no replaceNavigtor method but only a addNavigator method, which is called where ?
And anyways this all will be far to complicated and therefore error prone.
Why is there no simple option to keep my fragments alive :-(
In older Wizard-Templates there was the possibility of
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment;
switch (position) {
case 1:
fragment = fragment1;
break;
case 2:
fragment = fragment2;
break;
case 3:
fragment = fragment3;
break;
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(mCurrentFragment == null) {
ft.add(R.id.container, fragment).commit();
mCurrentFragment = fragment;
} else if(fragment.isAdded()) {
ft.hide(mCurrentFragment).show(fragment).commit();
} else {
ft.hide(mCurrentFragment).add(R.id.container, fragment).commit();
}
mCurrentFragment = fragment;
}
but i have no idea how to do this with the Android 4.0 template where my MainActivity is only derived as:
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
Ideas welcome :'(
Hi there & sorry for my late answer! I had a similar problem with navigation drawers and navigation component. I tried around a little and found a working solution, which might be helpful for others too.
The key is the usage of a custom FragmentFactory in the FragmentManager of the MainActivity. See the code for this below:
public class StaticFragmentFactory extends FragmentFactory {
private myNavHostFragment1 tripNavHostFragment;
private myNavHostFragment2 settingsNavHostFragment;
#NonNull
#Override
public Fragment instantiate(#NonNull ClassLoader classLoader, #NonNull String className) {
if (MyNavHostFragment1.class.getName().equals(className)) {
if (this.myNavHostFragment1 == null) {
this.myNavHostFragment1 = new MyNavHostFragment1();
}
return this.myNavHostFragment1 ;
} else if (MyNavHostFragment2.class.getName().equals(className)) {
if (this.myNavHostFragment2 == null) {
this.myNavHostFragment2 = new MyNavHostFragment2();
}
return this.myNavHostFragment2;
}
return super.instantiate(classLoader, className);
}
}
The FragmentFactory survives the navigation between different fragments using the NavigationComponent of AndroidX. To keep the fragments alive, the FragmentFactory stores an instance of the fragments which should survive and returns this instance if this is not null. You can find a similar pattern when using a singleton pattern in classes.
You have to register the FragmentFactory in the corresponding activity by calling
this.getSupportFragmentManager().setFragmentFactory(new StaticFragmentFactory())
Please note also that I'm using nesten fragments here, so one toplevel fragment (called NavHostFragmen here) contains multiple child fragments. All fragments are using the same FragmentFactory of their parent fragments. The custom FragmentFactory above returns the result of the super class method, when the fragment to be instantiated is not known to keep alive.
I am displaying DialogFragment from a manager. DialogFragment display multiple times.
I want to know is there a way to check from transaction whether this fragment already displaying. So don't display it.
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag(tag);
if (prev == null) {
ft.add(this, tag);
///ft.addToBackStack(tag);
ft.commitAllowingStateLoss();
}
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
I am calling my Fragment like
CustomerFeedbackDialog feedbackDialog = CustomerFeedbackDialog.newInstance(genaric.getData(), type);
feedbackDialog.show(getSupportFragmentManager(), "feedbackDialog");
I have call findFragmentByTag but it is always null. I don't want to show already displayed Fragment. otherwise it duplicate . Mulitple dialogFragment opens
I know I can do it using a flag in sharedprefs
EDIT Solution found
Thanks for your help. Problem solved and I posted answer below
If your problem is the same DialogFragment over another you can try adding a variable to your Manager:
#Nullable
private DialogFragment mCurrentDialogFrag;
And then whenever you add a new DialogFragment you set mCurrentDialogFrag to the new DialogFragment and then check before adding if current DialogFragment is the same as the new one.
I finally able to handle it by overriding show method
and addToBackStack(null) and executePendingTransactions
Firstly to put tag in findFragmentByTag addToBackStack is must otherwise tag is not added in fragmentTransaction.
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag(tag);
if (prev == null) {
ft.add(this, tag);
ft.addToBackStack(null);
ft.commitAllowingStateLoss();
manager.executePendingTransactions();
}
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
Now if fragment is already in transaction. It will not display again..
If I look into DialogFragment's public void show(FragmentManager manager, String tag) method implementation, I can see this:
public void show(FragmentManager manager, String tag) {
this.mDismissed = false;
this.mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
In order to know my dialog is already part of FragmentManager transactions, I'm simply doing this:
if (getFragmentManager().findFragmentByTag("...the tag") == null) {
fragment.show(getFragmentManager(), "...the tag");
}
I have to mention the above is strictly appcompat experience, and it works. Obviously you can put this check code inside override of show as well.
so i managed to get a stock navigation drawer to work and the action bar title to change with the fragments selected. I've also managed to get the backstack working easy peasy.
What i can't figure out how to do is get the action bar title to change back with the back click. google documentations says to add a onBackStackChangedListener:
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
but i'm at a lost where to place it? they say when i .commit to changes so i assumed it was placed after
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, spatialAwarenessFragment.getTag())
.commit();
but that didn't work, this is what i tried and all i get is red squigglies
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, spatialAwarenessFragment.getTag())
.commit();
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
setTitle("Spatial Awareness");
}
});
please help me noob
so i tried this
if (id == R.id.nav_spatial_awareness) {
setTitle("Spatial Awareness");
final SpatialAwareness spatialAwarenessFragment = new SpatialAwareness();
android.support.v4.app.FragmentManager spatialAwarenessManager = getSupportFragmentManager();
spatialAwarenessManager.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_content_layout, spatialAwarenessFragment, "spatialAwarenessFragmentTag")
.commit();
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
android.app.Fragment currentBackStackFragment = getFragmentManager().findFragmentByTag("spatialAwarenessFragmentTag");
if(currentBackStackFragment instanceof SpatialAwareness){
setTitle("Spatial");
}
}
});
i gave my fragment a tag and then tried matching the instance and then changing the title, still no good :(
Yes, the solution as mentioned here is to add a FragmentManager.OnBackStackChangedListener to your activity's FragmentManager.
Here is an example from a project I worked on:
(I have a navigation drawer with 7 Fragments and the OverviewFragment is the initial one that opens when the MainActivity opens)
My MainActivity:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, FragmentManager.OnBackStackChangedListener {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if (savedInstanceState == null) {
// open the default fragment
OverviewFragment fragment = new OverviewFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment).commit();
setTitle(R.string.title_fragment_overview);
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
}
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.nav_overview:
fragment = new OverviewFragment();
break;
case R.id.nav_schedule:
fragment = new ScheduleFragment();
break;
case R.id.nav_all_tasks:
fragment = new AllTasksFragment();
break;
case R.id.nav_announcements:
fragment = new AnnouncementFragment();
break;
case R.id.nav_my_courses:
fragment = new MyCoursesFragment();
break;
case R.id.nav_map:
fragment = new MapFragment();
break;
case R.id.nav_settings:
fragment = new SettingsFragment();
break;
default:
fragment = new OverviewFragment();
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackStackChanged() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment currentFragment =
fragmentManager.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof OverviewFragment) {
setTitle(R.string.title_fragment_overview);
navigationView.getMenu().findItem(R.id.nav_overview).setChecked(true);
}
else if (currentFragment instanceof ScheduleFragment) {
setTitle(R.string.title_fragment_schedule);
navigationView.getMenu().findItem(R.id.nav_schedule).setChecked(true);
}
else if (currentFragment instanceof AllTasksFragment) {
setTitle(R.string.title_fragment_all_tasks);
navigationView.getMenu().findItem(R.id.nav_all_tasks).setChecked(true);
}
else if (currentFragment instanceof MyCoursesFragment) {
setTitle(R.string.title_fragment_my_courses);
navigationView.getMenu().findItem(R.id.nav_my_courses).setChecked(true);
}
else if (currentFragment instanceof AnnouncementFragment) {
setTitle(R.string.title_fragment_announcements);
navigationView.getMenu().findItem(R.id.nav_announcements).setChecked(true);
}
else if (currentFragment instanceof MapFragment) {
setTitle(R.string.title_fragment_map);
navigationView.getMenu().findItem(R.id.nav_map).setChecked(true);
}
else if (currentFragment instanceof SettingsFragment) {
setTitle(R.string.title_fragment_settings);
navigationView.getMenu().findItem(R.id.nav_settings).setChecked(true);
}
}
}
Here's the entire project: https://github.com/FCI-E-campus/fci-e-campus-android
I am using an open source project(https://github.com/chrisbanes/cheesesquare) to develop my own application.
what I want is:
1.In the Home Fragment, there is a tablayout below to the toobar,and when the recycelview scroll, the Toolbar can hide, but the FloatingActionButton always stay;
2. In the Message Frgment, the is no Tab and on FloatingActionButton, only a simple blank Fragment with a Toolbar.
I try to do this in MainActivity:
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItem.setChecked(true);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment fragment = null;
int id = menuItem.getItemId();
if (id == R.id.nav_home) {
fragment = new HomeFragment();
} else if (id == R.id.nav_message) {
fragment = new MessageFragment();
} else if (id == R.id.nav_friends) {
fragment = new FriendsFragment();
} else if (id == R.id.nav_discussion) {
fragment = new DiscussionFragment();
}
ft.replace(R.id.viewpager, fragment);
ft.commit();
mDrawerLayout.closeDrawer(GravityCompat.START);
return true;
}
});
}
but this not work, because the tablayout stay both in HomeFragment and MessageFragment.
When I try to do change the tablayout to the layout xml of HomeFragment, I also meet some problem because the below code should write in MainActivity
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
I feel puzzled, what should I do to achieve my goals?
I try to solve the problem like this, implement the basic functions.
I am new to android application development. I have a fragment (layout of which contains one vertical linear layout). I am adding some views dynamically in the linear layout. When i change orientation to landscape from portrait the layout of fragment is recreated and all dynamically added views disappear. How can i prevent the fragment's layout from recreating?
EDIT: the code below is inside fragment. clicking on a button(say add button) dynamically adds a view in vertical linear layout but changing orientation these views disappear.
buttonAdd.setOnClickListener(new OnClickListener() {
#SuppressWarnings("unchecked")
#Override
public void onClick(View arg0) {
methodForCheckEmail(textIn);
if(textIn.getText().toString().length() == 0) {
Toast.makeText(getActivity(), "please enter valid email", Toast.LENGTH_LONG).show();
} else if (textIn.getText().toString().length() > 0 && checkEmail == false) {
Toast.makeText(getActivity(), "please enter valid email", Toast.LENGTH_LONG).show();
} else {
LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View addView = layoutInflater.inflate(R.layout.row2, null);
final TextView textOut = (TextView) addView.findViewById(R.id.textout);
textOut.setText(textIn.getText().toString());
Button buttonRemove = (Button) addView.findViewById(R.id.remove);
arrayList.add(textIn.getText().toString());
buttonRemove.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((LinearLayout) addView.getParent()).removeView(addView);
// textIn.setText("");
arrayList.remove(textOut.getText().toString());
}
});
textIn.setText("");
container.addView(addView);
}
//textIn.setText("");
}
});
If you change oriantation the activity will be recreated so you have to add the fragment again.
Please add code if you ask a question. You will get more help.
Use the activity lifecycle methods:
http://developer.android.com/training/basics/activity-lifecycle/index.html
Read this post:
Activity restart on rotation Android