How to prevent refreshing of webview in navigation drawer - android

From a while i had been using viewpager to display webview pages in activity, but now i have change from viewpager to navigation drawer with RecyclerView, to display webview, i have multiple webview loading in my activity.
Suppose activity starts on webview 1 and then i click on webview 2 if i go back to webview 1 it will be reloading(refresh) and i want to prevent that from happening.
Please help.
Here is my main activity.
public class MainActivity extends AppCompatActivity implements FragmentDrawer.FragmentDrawerListener {
private Toolbar toolbar;
private FragmentDrawer drawerFragment;
private static String TAG = MainActivity.class.getSimpleName();
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.tool_bar);
if (toolbar != null) {
toolbar.setTitle(R.string.app_name);
setSupportActionBar(toolbar);
}
drawerFragment = (FragmentDrawer)
getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), toolbar);
drawerFragment.setDrawerListener(this);
displayView(0);
}
#Override
public void onDrawerItemSelected(View view, int position) {
displayView(position);
}
private void displayView(int position) {
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (position) {
case 0:
fragment = new TopRatedFragment();
title = getString(R.string.title_home);
break;
case 1:
fragment = new GamesFragment();
title = getString(R.string.title_friends);
break;
case 2:
fragment = new MoviesFragment();
title = getString(R.string.title_messages);
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container_body, fragment);
fragmentTransaction.commit();
getSupportActionBar().setTitle(title);
}
}
}

Are you saying that you want to restore the same fragment? This is from developer.android.com
FragmentTransaction transaction = getSupportFragmentManager().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
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
This enables the back navigation button to restore the same fragment that already loaded the webview. You are creating a new one each time in the switch statement.
Once you get the back button working how you want it you can easily learn how to manually pull the specific fragment off of the stack.
Edit:
I do not think you are refreshing the webview. You are creating a new fragment each time you select an item by calling the empty constructor. You need to only create each fragment one time and then use the same instance of that fragment when you go back to that webview. Using the back stack will enable it to stay loaded while you leave the application and come back to it later.
Also try using the static method YourFragment.newInstance(params) which returns a new instance of YourFragment. Do this instead of the empty constructor, as that is considered incorrect practice.

Related

Separate Back Stack for each tab in BottomNavigationView Android using Fragments

I'm implementing BottomNavigationView for navigation in an Android app. I am using Fragments to set the content for each tab.
I know how to set up one fragment for each tab and then switch fragments when a tab is clicked. But how can I have a separate back stack for each tab?
Here is the code to set up one fragment:
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content, selectedFragment);
transaction.commit();
For an example, Fragment A and B would be under Tab 1 and Fragment C and D under Tab 2. When the app is started Fragment A is shown and Tab 1 is selected. Then Fragment A might be replaced with Fragment B. When Tab 2 is selected Fragment C should be displayed. If Tab 1 is then selected Fragment B should once again be displayed. At this point, it should be possible to use the back button to show Fragment A.
And Here is the code to set up next fragment in the same tab:
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.content, selectedFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
Finally, I found the solution, it was inspired by a previous answer on StackOverflow: Separate Back Stack for each tab in Android using Fragments
I only have replaced TabHost with BottomNavigationView and here is the code:
Main Activity
public class MainActivity extends AppCompatActivity {
private HashMap<String, Stack<Fragment>> mStacks;
public static final String TAB_HOME = "tab_home";
public static final String TAB_DASHBOARD = "tab_dashboard";
public static final String TAB_NOTIFICATIONS = "tab_notifications";
private String mCurrentTab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
mStacks = new HashMap<String, Stack<Fragment>>();
mStacks.put(TAB_HOME, new Stack<Fragment>());
mStacks.put(TAB_DASHBOARD, new Stack<Fragment>());
mStacks.put(TAB_NOTIFICATIONS, new Stack<Fragment>());
navigation.setSelectedItemId(R.id.navigation_home);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
selectedTab(TAB_HOME);
return true;
case R.id.navigation_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.navigation_notifications:
selectedTab(TAB_NOTIFICATIONS);
return true;
}
return false;
}
};
private void gotoFragment(Fragment selectedFragment)
{
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, selectedFragment);
fragmentTransaction.commit();
}
private void selectedTab(String tabId)
{
mCurrentTab = tabId;
if(mStacks.get(tabId).size() == 0){
/*
* First time this tab is selected. So add first fragment of that tab.
* Dont need animation, so that argument is false.
* We are adding a new fragment which is not present in stack. So add to stack is true.
*/
if(tabId.equals(TAB_HOME)){
pushFragments(tabId, new HomeFragment(),true);
}else if(tabId.equals(TAB_DASHBOARD)){
pushFragments(tabId, new DashboardFragment(),true);
}else if(tabId.equals(TAB_NOTIFICATIONS)){
pushFragments(tabId, new NotificationsFragment(),true);
}
}else {
/*
* We are switching tabs, and target tab is already has atleast one fragment.
* No need of animation, no need of stack pushing. Just show the target fragment
*/
pushFragments(tabId, mStacks.get(tabId).lastElement(),false);
}
}
public void pushFragments(String tag, Fragment fragment, boolean shouldAdd){
if(shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
public void popFragments(){
/*
* Select the second last fragment in current tab's stack..
* which will be shown after the fragment transaction given below
*/
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
/*pop current fragment from stack.. */
mStacks.get(mCurrentTab).pop();
/* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
#Override
public void onBackPressed() {
if(mStacks.get(mCurrentTab).size() == 1){
// We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
finish();
return;
}
/* Goto previous fragment in navigation stack of this tab */
popFragments();
}
}
Home fragment example
public class HomeFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
Button gotoNextFragment = (Button) view.findViewById(R.id.gotoHome2);
gotoNextFragment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((MainActivity)getActivity()).pushFragments(MainActivity.TAB_HOME, new Home2Fragment(),true);
}
});
return view;
}
}
This behavior is supported by the new Navigation Architecture Component (https://developer.android.com/topic/libraries/architecture/navigation/).
Essentially, one can use NavHostFragment, which is a fragment that controls its own back stack:
Each NavHostFragment has a NavController that defines valid navigation within the navigation host. This includes the navigation graph as well as navigation state such as current location and back stack that will be saved and restored along with the NavHostFragment itself.
https://developer.android.com/reference/androidx/navigation/fragment/NavHostFragment
Here is an example: https://github.com/deisold/navigation
Edit: Turns out Navigation Architecture Component doesn't support seperate back stacks anyway, as pointed out by the commenters. But as #r4jiv007 mentioned, they are working on it and has offered an "official hack" in the meantime: https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
It is worth noting that the behavior you describe goes against the Google guidelines. https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior
Navigation through the bottom navigation bar should reset the task state.
In other words, having Fragment A and Fragment B "inside" Tab 1 is fine, but if the user opens Fragment B, clicks Tab 2, and then clicks Tab 1 again, they should see Fragment A.
Suppose you have 5(A, B, C, D, E) BottomNavigationView menu item, then in Activity create 5 FrameLayout(frmlytA, frmlytB, frmlytC, frmlytD, frmlytE) in parallel overlapping manner as the container for each of these menu items. When BottomNavigation Menu item A is pressed then hide all the other FrameLayouts(Visibility = GONE) and just show(Visibility = VISIBLE) the FrameLayout 'frmlytA' which will host the FragmentA and over this container do the further transactions like (FragmentA -> FragmentX -> FragmentY). And then If user clicks BottomNavigation Menu item B then just hide this(frmlytA) container and show 'frmlytB'. Then if user again presses the menu item A then show 'frmlytA' it should retain the earlier state. So, like this you can switch between the container FrameLayouts and can maintain the back stack of each container.
Instead of using replace method use add fragment,
Instead of this method
ft.replace(R.id.content, selectedFragment);
Use this
ft.add(R.id.content, selectedFragment);
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.(R.id.content, selectedFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();

Why I need to press back button twice to dismiss fragment on first time?

I have Base Activity including NavigationView with 2 menu items. On start it loads Home fragment having background image inside it. Each loads specific fragment. When I select Terms & Conditions menu item, it loads T&C fragment & when I press back button it simply kills it.
However, when I select About Us menu item, it loads About Us fragment but I need to press BACK button twice to kill it. I need to know why does it happen?
Part of Code in AppBaseActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
HomeFragment homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.body_container, homeFragment, "");
fragmentTransaction.commit();
}
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
navigationView.getMenu().findItem(item.getItemId()).setChecked(true);
switch (item.getItemId()) {
case R.id.nav_terms :
fragmentTransaction = fragmentManager.beginTransaction();
TCFragment tcFragment = new TCFragment();
fragmentTransaction.replace(R.id.body_container, tcFragment, "");
fragmentTransaction.commit();
break;
case R.id.nav_about_us :
fragmentTransaction = fragmentManager.beginTransaction();
AboutUsFragment aboutUsFragment = new AboutUsFragment();
fragmentTransaction.replace(R.id.body_container, aboutUsFragment, "");
fragmentTransaction.commit();
break;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
All fragments simply have overridden onCreateView() by inflating respected xml only. No code is written in both fragments yet.
You can stop back hardware navigation if you want.
Simply using onBackPressed() without super.onBackPressed()
#Override
public void onBackPressed() {
}
#Override
public void onBackPressed() {
super.onBackPressed();
}

Android Navigation Drawer Fragment Not Showing

I have implemented a navigation drawer in my application, The application is a tabbed layout and I think this is where the problem is coming from.
AT the moment I have a listener on the items in the Nav drawer to launch a new fragment when pressed. The issue is that when I press one of the items the fragment doesnt seem to load and the current fragment just goes blank.
How do I set the layout of the fragment to launch once the item has been selected? As my current method doesnt seem to be working. The below shows my current attemp where R.id.viewpager is what I am trying to replace. Is there a way of just launching the fragments layout and completely replacing the layout with the new fragments layout?
onDrawerItemSelected
#Override
public void onDrawerItemSelected(View view, int position) {
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (position)
{
case 0:
{
Intent intent = new Intent(MainActivity.this, MainActivity.class);
startActivity(intent);
}
case 1:
fragment = new FavouritesFragment();
title = getString(R.string.title_favourites);
break;
case 2:
fragment = new HelpFragment();
title = getString(R.string.title_help);
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.viewpager, fragment);
fragmentTransaction.commit();
// set the toolbar title
getSupportActionBar().setTitle(title);
}
}
fragmentTransaction.replace(R.id.viewpager, fragment);
You don't replace the viewpager, you set current item for the viewPager. You should have a PagerAdapter that defines which fragment to show for the position.

ActionBar Title dynamically change with fragment

I have 1 Activity with 3 Fragments inside (Home-Login-RestorePass) Initially, HomeFragment shows and the other two are hidden. I want that the ActionBar Title change depending on which Fragment is showing.
I'm trying in my Activity with:
public void setActionBarTitle(String title){
getSupportActionBar().setTitle(title);
}
#Override
public void onResume() {
super.onResume();
// Set title
setActionBarTitle(getString(R.string.app_name));
}
and the fragments has the same:
#Override
public void onResume() {
super.onResume();
// Set title
((LoginActivity) getActivity()).setActionBarTitle(getString(R.string.fragment_login));
}
But it doesn't work. It always show R.string.fragment_login on the title.
I'm using FragmentTransaction for the fragment transition:
btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
HomeFragment homeFragment = (HomeFragment) getFragmentManager().findFragmentById(R.id.fragmentHome);
LoginFragment loginFragment = (LoginFragment) getFragmentManager().findFragmentById(R.id.fragmentLogin);
ft.hide(homeFragment).addToBackStack(null);
ft.show(loginFragment).addToBackStack(null).commit();
}
});
Additionally if i could make appear an Arrow Button (Back) on ActionBar depending of the fragment would be great.
Thanks for your time! Regards.
Keep in mind that if you are using the support Library, you have to specifically cast your Activity when you get it through getActivity(). And then you'll want to make sure you are retrieving a support ActionBar by using getSupportActionBar(). I was able to set the ActionBar title in my app by using the following code in my Fragment's onResume()...
#Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
ActionBar actionBar = activity.getSupportActionBar();
actionBar.setTitle(R.string.my_fragment_title);
}
Use this method in activity to change the Fragment and set the title programmatically:
private void displayFragment(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
String title = "";
switch (position) {
case 0:
fragment = new Home();
title = "Home";
break;
case 1:
fragment = new Login();
title = "Login";
break;
case 2:
fragment = new RestorePass();
title = "Restore Password";
break;
default:
break;
}
// update selected fragment and title
if (fragment != null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_container, fragment).commit();
getSupportActionBar().setTitle(title);
// change icon to arrow drawable
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_arrow);
}
}
For example, you want Fragment Home to be displayed:
displayFragment(0);
If you set the activity attribute android:label="" in the AndroidManifest you will be able to set the title post initialisation. I discovered this tracing through the Toolbar source code.

How to set a default fragment in Android Studio Navigation Drawer?

I'm looking for a way to se a default fragment in my navigation drawer which is not the item # 0. I've seen some solutions based on checking if savedInstanceState is null or not but I feel like I don't have access to this in my onNavigationDrawerItemSelected method. Here is my class:
public class Main extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the Main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
break;
case 1:
fragment = new HomeFragment();
break;
case 2:
fragment = new HomeFragment();
break;
default:
fragment = new DiscoverFragment();
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment).commit();
}
}
The answer is rather simple. Since the DrawerLayout leaves you to manage fragments on your own, you can just use the basic FragmentManager APIs that would be used in a normal FragmentActivity.
I use the following method to make it a little simpler, which will replace the current fragments even if there are none currently added to the container. Just call this method with the fragment you want to be default in your onCreate() method.
public void setFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit();
}
Note that it is technically more correct to use .add(fragment) for the first call rather than .replace since you aren't replacing anything, but it really doesn't matter because internally, it basically just removes any existing fragments and adds the new one using .add() for you
http://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int,android.app.Fragment,java.lang.String)

Categories

Resources