When i change BottomNavigation tab , the fragment will be re-creation.
How to cache the created fragment and not re-creation?
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
bottomNavigationView = view.findViewById(R.id.bottom_navigation);
NavController navController = Navigation.findNavController(getActivity(), R.id.bottom_nav_host_fragment);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
this.initBottomNavigation();
}
private void initBottomNavigation() {
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
NavController navController = Navigation.findNavController(getActivity(), R.id.bottom_nav_host_fragment);
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
Related
When I change fragment my FAB (FloatingActionButton) not change her image. I've got this method to change image:
private void fabImages(){
final NavHostFragment nhf = (NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main);
if(nhf != null){
Fragment fragmentActual = nhf.getChildFragmentManager().getFragments().get(0);
if(fragmentActual instanceof HomeFragment){
binding.appBarMain.fab.setImageResource(R.drawable.ic_add_auto_blanco);
}else if(fragmentActual instanceof GalleryFragment){
binding.appBarMain.fab.setImageResource(R.drawable.ic_save);
}
}
}
i'm using this activity to my app:
I apply this method in onCreate and onResume from my MainActivity but the image not change, why?
UPDATE:
MainActivity:
public class MainActivity extends AppCompatActivity{
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
private String horaString, fechaString;
private NombreTallerPreferencia nombreTallerPreferencia;
private PermissionHelper permissionHelper;
private static final int PICK_IMAGE = 1;
public static final String TAG = "logcat";
public static final String BARRA = "/";
private View view_imagen_perfil;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
fabImages(); //method to change image
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
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_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
#Override
protected void onResume() {
super.onResume();
fabImages(); //method to change image
}
The problem is not in how to set a drawable to the FAB, because you already do that right. But the problem in the place where you do that which is in onResume().
Here is the scenario:
The app is launched, onResume() get triggered, and the fabImages() get called, so, it's HomeFragment, so set R.drawable.ic_add_auto_blanco, and this works with you.
You go to GalleryFragment, so HomeFragment is hidden now and the GalleryFragment is shown, but here the onResume() of the activity won't get called, because the activity is not paused() i.e. onPause() doesn't get called;
While you're at GalleryFragment, If you change the configuration by rotating the device, then onResume() will get called, and so as fabImages(), and fab changes its drawable to R.drawable.ic_save.
So, you need to call fabImages() somewhere else other than the onResume of the activity.
To fix this you need to register a listener in the activity that tracks whenever the navigation changes its current destination, and that through the OnDestinationChangedListener interface:
So, in your activity's onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
fabImages(); //method to change image
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
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_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
/////////////////////////
// Here is the new code:
/////////////////////////
NavController.OnDestinationChangedListener destinationChangedListener = new NavController.OnDestinationChangedListener() {
#Override
public void onDestinationChanged(#NonNull NavController controller, #NonNull NavDestination destination, #Nullable Bundle arguments) {
if (destination.getId() == R.id.nav_home) {
binding.appBarMain.fab.setImageResource(R.drawable.ic_add_auto_blanco);
} else if (destination.getId() == R.id.nav_gallery) {
binding.appBarMain.fab.setImageResource(R.drawable.ic_save);
}
}
};
navController.addOnDestinationChangedListener(destinationChangedListener);
}
It seems that your application does not have fallback conditional like else just incase your values are null.
You can read more about changing the icon on FAB here on
https://stackoverflow.com/a/32082108/12028348 or from the official android studio documentation
https://developer.android.com/reference/android/support/design/widget/FloatingActionButton.html
HomeActivity
public class HomeActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
public static Toolbar toolbar;
public static BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
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,R.id.cartFragment,R.id.wishFragment,R.id.dashboardFragment)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
bottomNavigationView = findViewById(R.id.bottomNavigationView);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}
#Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
Cart Fragment in bottom navigation
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_cart, container, false);
HomeActivity main = (HomeActivity) getActivity();
//You can access all public variable and methods of MainActivity.
//simply call
main.setSupportActionBar(toolbar);
main.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
toolbar.setNavigationIcon(R.drawable.back_arrow);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getActivity().onBackPressed();
bottomNavigationView.setVisibility(View.VISIBLE);
}
});
bottomNavigationView.setVisibility(View.INVISIBLE);
return view;
}
}
In Cat fragment first botttom navigation invisible
when backpress from the cart fragment at that time use bottom navigation visible. because of, without visible bottom navigation backpress in MainActivity bottom navigation didn't show.
***When back from the card fragment and click navigation drawer app crush
I was trying to add conditional navigation following this. But I ran into a problem.
So the steps I followed are:
Created navigation drawer in the MainActivity with HomeFragment as a start destination.
Conditionally navigated to FromBlackFragment from BlankFragment.
Want to go to HomeFragment from FromBlackFragment by clicking up/back button.
The problem is with step 3 above. I am going back to HomeFragment by clicking back button whereas I am navigated to BlankFragment on up button click. The code for step 3 is: (inside FromBlackFragment.java file)
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(),
new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
Navigation.findNavController(view).popBackStack(R.id.homeFragment, false);
}
});
The MainActivity.java file:
public class MainActivity extends AppCompatActivity {
private NavController navController;
private DrawerLayout drawerLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.my_toolbar);
drawerLayout = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
navController = findNavController(this, R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph())
.setOpenableLayout(drawerLayout).build();
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
}
BlankFragment.java file:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedPreferences sp = requireActivity().getSharedPreferences("Testing",0);
if(!sp.getBoolean("b",false)){
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean("b",true);
editor.apply();
Navigation.findNavController(view).navigate(R.id.fromBlackFragment);
}
}
FromBlackFragment.java file:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_from_black, container, false);
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(),
new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
Navigation.findNavController(view).popBackStack(R.id.homeFragment, false);
}
});
}
Note: Shared preferences used in the code is not part of the problem.
Please help. Thanks.
Edit: (More specifically the question is) How to sync upNavigation and back button?
Step1: In MainActivity's onCreate, you need to call setSupportActionBar(toolbar);
Step2: Call setupActionBarWithNavController() from NavigationUI, passing in the proper arguments, this will:
Set up the ActionBar returned by
AppCompatActivity.getSupportActionBar() for use with a NavController.
Step3: By calling setupActionBarWithNavController(), you should now override onSupportNavigateUp(), putting in the call to navigate:
#Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
If NavigationUI.navigateUp won't get you to home fragment destination, you maybe want call a Navigation method that explicitly get you there.
It can build and run successfully but in virtual device has stopped.
this is the code of my main layout in fragment:
public class BlankFragment14 extends Fragment {
#Override
public View onCreateView( LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState ) {
return inflater.inflate(R.layout.fragment_blank_fragment14, container, false);
}
#Override
public void onCreate( #Nullable Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
BottomNavigationView bottomNav = getView( ).findViewById(R.id.bottom_navigation);
BottomNavigationView.OnNavigationItemSelectedListener navListener = null;
bottomNav.setOnNavigationItemSelectedListener(navListener);
getFragmentManager( ).beginTransaction( ).replace(R.id.fragment_container1, new ProfileFragment( )).commit( );
navListener = new BottomNavigationView.OnNavigationItemSelectedListener( ) {
#Override
public boolean onNavigationItemSelected( #NonNull MenuItem menuItem ) {
Fragment selectedFragment = null;
switch (menuItem.getItemId( )) {
case R.id.nav_profile1:
selectedFragment = new ProfileFragment( );
break;
case R.id.nav_appointment1:
selectedFragment = new AppointmentFragment( );
break;
case R.id.nav_patientdata1:
selectedFragment = new PatientdataFragment( );
break;
}
getFragmentManager( ).beginTransaction( ).replace(R.id.fragment_container1, selectedFragment).commit( );
return true;
}
};
}
}
I don't know that It can be possible to work ? or I must use activity to be a main layout.
Use Navigation component to implement BottomNavigationView. Your MainActivity must have NavHostFragment, that can display other fragment destinations. In MainActivity's onCreate
setup NavController as shown below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar myToolbar = (Toolbar) findViewById(R.id.toolbar1);
setSupportActionBar(myToolbar);
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
//NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
}
I'm trying to use a shared element animation between 2 fragments, BlankFragment and BlankFragment2. BlankFragment has a recycler view and BlankFragment2 is a details screen. They share an image and I'm using the new navigation component.
In BlankFragment I'm building FragmentNavigator.Extras and passing the extras to my call to navigate with the transition name of the shared image (as its a recycler view and these need to be unique),
In BlankFragment2 I'm receiving this name setting it to my image and setting the setSharedElementEnterTransition
The result is that the enter animation works fine but the exit/return doesn't, I've tried setting them and not setting them (because I believe the navigation component should handle this for me) can anyone help?
MainActivity Navigation Setup
private void setNavigation() {
navController = Navigation.findNavController(this, R.id.main_fragment);
NavigationUI.setupActionBarWithNavController(this, navController);
}
Handling back button
#Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.main_fragment).navigateUp()
|| super.onSupportNavigateUp();
}
BlankFragment OnClick
#Override
public void onClick(View view, int position) {
NavController navController = Navigation.findNavController(recyclerView);
FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder().addSharedElement(view, view.getTransitionName()).build();
BlankFragmentDirections.ActionBlankFragmentToBlankFragment2 directions = BlankFragmentDirections.actionBlankFragmentToBlankFragment2(view.getTransitionName());
navController.navigate(directions,extras);
}
BlankFragment2 onCreate with return/exit transition
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.no_transition));
setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
getFragmentArguments();
}
Get Arguments Method
private void getFragmentArguments(){
if (getArguments() != null){
transitionName = BlankFragment2Args.fromBundle(getArguments()).getTransitionName();
Log.d(TAG, "transition name " + transitionName);
}
}
Set image transition name
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
image = view.findViewById(R.id.image);
image.setTransitionName(transitionName);
text = view.findViewById(R.id.text);
}
Java
To Fix on return transition, use viewTreeObserver.addOnPreDrawListener
In BlankFragment (fragment with recycle view)
We need to call postponeEnterTransition(); so the transition
will be pospone
Setup addOnPreDrawListener on recycleView as following
RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
//setup for recycle view adapter
ViewTreeObserver viewTreeObserver = recyclerView.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
startPostponedEnterTransition();
return true;
}
});
That's it.
Follow this link to get more info on ViewTreeObserver follow this link
FIN !!