Change BottomNavigationView Icons on Back Button clicked - java

At the bottom of my layout I have a BottomNavigationView with three fragments. If I click the back button the fragment is switching but not the bottom icons. How can I fix it?
addToBackStack() works. Maybe you have some advise to pretty the code. Is it good practise to have the fragment tags in the activity or fragment?
public class MainActivity extends AppCompatActivity {
private FragmentManager mFragmentManager;
private BottomNavigationView mBottomNavigationView;
private static final String HOME_FRAGMENT = "homeFragment";
private static final String SEARCH_FRAGMENT = "searchFragment";
private static final String SHARE_FRAGMENT = "shareFragment";
private boolean isFirstFragment;
private long mBackPressedTime;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
setBottomNavigationView();
}
private void init() {
mBottomNavigationView = findViewById(R.id.bottomNavigationView);
mFragmentManager = getSupportFragmentManager();
mBackPressedTime = 0;
}
private void setBottomNavigationView() {
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
isFirstFragment = true;
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.ic_home:
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
return true;
case R.id.ic_search:
setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
return true;
case R.id.ic_circle:
setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
return true;
default:
return false;
}
}
});
}
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
if (isFirstFragment) {
transaction.addToBackStack(tag);
}
transaction.commit();
}
#Override
public void onBackPressed() {
final long currentTimeMillis = System.currentTimeMillis();
if (mFragmentManager.getBackStackEntryCount() > 0) {
mFragmentManager.popBackStack();
} else if (currentTimeMillis - mBackPressedTime > 2000) {
mBackPressedTime = currentTimeMillis;
Toast.makeText(this, getString(R.string.reach_homescreen), Toast.LENGTH_SHORT).show();
} else {
super.onBackPressed();
}
}
}

#Hans Baum instead of adding your first fragment to back stack try this code,
#Override
public void onBackPressed() {
if(mBottomNavigationView.getSelectedItemId () != R.id.ic_home)
{
mBottomNavigationView.setSelectedItemId(R.id.ic_home);
}
else
{
super.onBackPressed();
}
}
This code will exit your activity if you are in Home Fragment else if you are in any other Fragment it will go to Home Fragment.
So no addToBackStack() needed. So your serFragment() method should be,
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
transaction.commit();
}
hope it helps.
Note that in your code you never assigned false to isFirstFragment so i assume all fragments are added to backstack which is very memory consuming.
UPDATE:
Since your requirement is different.As you want Instagram like tabs, i hope this implementation helps you .
Deque<Integer> mStack = new ArrayDeque<>();
boolean isBackPressed = false;
private void setBottomNavigationView() {
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.ic_home:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_home);
}
isBackPressed = false
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
return true;
case R.id.ic_search:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_search);
}
isBackPressed = false
setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
return true;
case R.id.ic_circle:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_circle);
}
isBackPressed = false
setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
return true;
default:
return false;
}
}
});
mBottomNavigationView.setOnNavigationItemReselectedListener(new
BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
}
});
mBottomNavigationView.setSelectedItemId(R.id.ic_home);
pushFragmentIntoStack(R.id.ic_home);
}
private void pushFragmentIntoStack(int id)
{
if(mStack.size() < 3)
{
mStack.push(id);
}
else
{
mStack.removeLast();
mStack.push(id);
}
}
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
transaction.commit();
}
#Override
public void onBackPressed() {
if(mStack.size() > 1)
{
isBackPressed = true;
mStack.pop();
mBottomNavigationView.setSelectedItemId(mStack.peek());
}
else
{
super.onBackPressed();
}
}
I have used deque to store the order in which the tabs are clicked Since there are 3 tabs deque size is 3 .On back press it will pop the stack and go to that tab. If no item in stack it will quit the activity.
IMPORTANT NOTE:
Do not add fragments to backstack in this scenario because as i switch the tabs the backstack count will keep increasing and may even lead to MemoryOutOfBound exception.
To Question 2:
Coming to your Fragment tag question , It is good to save tag in fragment . As the fragment is reusable in any activity instead adding tag in every activity it is used , it is good if we add it in fragment itself.

Try this:
#Override
public void onBackPressed() {
if(navigation_bottom.getSelectedItemId () != R.id.action_home)
{
FragmentTransaction transaction =
((HomeActivity)this).getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frameLayout_home, new HomeFragment());
transaction.commit();
navigation_bottom.setSelectedItemId(R.id.action_home);
}
else
{
HomeActivity.this.finishAffinity();
}
}

You can add the fragment name in the backstack with the fragment transaction
getFragmentManager()
.beginTransaction()
.replace(R.id.frame, YourCurrentFragment.newInstance())
.addToBackStack(YourFragment.class.getName())
.commit();
Then you can add onBackstackChangedListener to get the currently selected fragment
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
FragmentManager.BackStackEntry bse = fragmentManager.
getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1);
//bse will be the backstack entry for current fragment
if (bse.getName().equals(YourFragment.class.getName())) {
navigationView.getMenu().getItem(0).setChecked(true);
} else if (bse.getName().equals(YourSecondFragment.class.getName())) {
navigationView.getMenu().getItem(1).setChecked(true);
}
}
});

Related

ListView in fragment not refreshed when move between fragment

ListView in FragmentA refresh perfectly when move from Activity to Fragment. This is my very very successful code in fragmentA but before, this is the OUTPUT that i will show the code after this:
OUTPUT:
from first Query
EAT
DRINK
POOP
from second Query
DONALD TRUMP
BARRACK OBAMA
GEORGE BUSH
the success above output happen when i move from,
FragmentA --> Activity --> back to FragmentA
bu when i move like this,
FragmentA --> FragmentB --> back to FragmentA
ListView show unwanted ASCENDING result
// wrong
POOP
DRINK
EAT
// true
EAT
DRINK
EAT
and there is no second query at all.
I am very thankful for your response. Before i show the code after this,
this is what happen why go to Activity then having successful refresh
because i put Fragments in MainActivity, so:
MainActiviy --> Activity --> call again onCreate in MainActivity
FragmentA --> FragmentB --> not call onCreate in MainActivity
All codes below inside FragmentA.
Ok, First Query code:
// when scroll reach bottom
recyclerSeen.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// vertical scroll
Boolean reachBottom = !recyclerView.canScrollVertically(-1);
if (reachBottom) {
secondQuery();
}
}
});
Query firstQuery = firebaseFirestore
.collection("Posts")
.orderBy("timestamp", Query.Direction.DESCENDING)
.limit(3);
firstQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (isFirstPageFirstLoad) {
lastVisible = documentSnapshots.getDocuments().get(documentSnapshots.size() - 1);
}
for (DocumentChange doc : documentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
//String postId = doc.getDocument().getId();
ContentProfile contentSeen = doc.getDocument().toObject(ContentProfile.class);
/* how to store multi ArrayList */
if (isFirstPageFirstLoad) {
contentList.add(contentSeen);
} else {
contentList.add(0, contentSeen);
}
adapterSeen.notifyDataSetChanged();
}
}
isFirstPageFirstLoad = false;
}
});
second Query code:
public void loadMorePost() {
Query nextQuery = firebaseFirestore
.orderBy("timestamp", Query.Direction.DESCENDING)
.collection("Posts")
.startAfter(lastVisible)
.limit(3);
nextQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (!documentSnapshots.isEmpty()) {
lastVisible = documentSnapshots.getDocuments().get(documentSnapshots.size() - 1);
for (DocumentChange doc : documentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
//String postId = doc.getDocument().getId();
ContentProfile contentSeen = doc.getDocument().toObject(ContentProfile.class);
contentList.add(contentSeen);
adapterSeen.notifyDataSetChanged();
}
}
}
}
});
}
MainActivity.class
public class MainActivity extends AppCompatActivity {
private HomeFragment homeFragment;
private Search search;
private CenterFragment centerFragment;
private Notification notification;
private ProfileFragment profileFragment;
private BottomNavigationView bottomNavigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
homeFragment = new HomeFragment();
search = new Search();
centerFragment = new CenterFragment();
notification = new Notification();
profileFragment = new ProfileFragment();
bottomNavigation = findViewById(R.id.bottom_navigation);
BottomNavigationViewHelper.removeShiftAnimation(bottomNavigation);
//initializeFragment
replaceFragment(homeFragment);
}
#Override
public void onStart() {
super.onStart();
bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener(){
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item){
switch(item.getItemId()){
case R.id.action_home :
replaceFragment(homeFragment);
return true;
case R.id.action_search :
replaceFragment(search);
return true;
case R.id.action_center :
replaceFragment(centerFragment);
return true;
case R.id.action_notif :
replaceFragment(notification);
return true;
case R.id.action_profile :
replaceFragment(profileFragment);
return true;
default:
return false;
}
}
});
}
private void replaceFragment(Fragment fragment){
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_container, fragment);
fragmentTransaction.commit();
}
}
i have a solution for handling my above problem, forawhile i don't know how to to refresh it, or call MainActivity onCreate() manually , or remove() fragment or whatever clean code, what i do is, change replace become hide. And this code below work like a charm, i don't have to refresh my list.
public class MainActivity extends AppCompatActivity {
private HomeFragment homeFragment;
private Search search;
private CenterFragment centerFragment;
private Notification notification;
private ProfileFragment profileFragment;
private BottomNavigationView bottomNavigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
homeFragment = new HomeFragment();
search = new Search();
centerFragment = new CenterFragment();
notification = new Notification();
profileFragment = new ProfileFragment();
bottomNavigation = findViewById(R.id.bottom_navigation);
BottomNavigationViewHelper.removeShiftAnimation(bottomNavigation);
initializeFragment();
//replaceFragment(homeFragment);
Toast.makeText(MainActivity.this, "MainActivity : onCreate() ", Toast.LENGTH_SHORT).show();
}
#Override
public void onStart() {
super.onStart();
bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener(){
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item){
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
switch(item.getItemId()){
case R.id.action_home :
replaceFragment(homeFragment, currentFragment);
return true;
case R.id.action_search :
replaceFragment(search, currentFragment);
return true;
case R.id.action_center :
replaceFragment(centerFragment, currentFragment);
return true;
case R.id.action_notif :
replaceFragment(notification, currentFragment);
return true;
case R.id.action_profile :
replaceFragment(profileFragment, currentFragment);
return true;
default:
return false;
}
}
});
}
private void initializeFragment(){
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.main_container, homeFragment);
fragmentTransaction.add(R.id.main_container, search);
fragmentTransaction.add(R.id.main_container, centerFragment);
fragmentTransaction.add(R.id.main_container, notification);
fragmentTransaction.add(R.id.main_container, profileFragment);
fragmentTransaction.hide(search);
fragmentTransaction.hide(centerFragment);
fragmentTransaction.hide(notification);
fragmentTransaction.hide(profileFragment);
fragmentTransaction.commit();
}
// private void replaceFragment(Fragment fragment){
// FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
//
// fragmentTransaction.replace(R.id.main_container, fragment);
// fragmentTransaction.commit();
// }
private void replaceFragment(Fragment fragment, Fragment currentFragment){
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if(fragment == homeFragment){
fragmentTransaction.hide(search);
fragmentTransaction.hide(centerFragment);
fragmentTransaction.hide(notification);
fragmentTransaction.hide(profileFragment);
}
if(fragment == search){
fragmentTransaction.hide(homeFragment);
fragmentTransaction.hide(centerFragment);
fragmentTransaction.hide(notification);
fragmentTransaction.hide(profileFragment);
}
if(fragment == centerFragment){
fragmentTransaction.hide(search);
fragmentTransaction.hide(homeFragment);
fragmentTransaction.hide(notification);
fragmentTransaction.hide(profileFragment);
}
if(fragment == notification){
fragmentTransaction.hide(search);
fragmentTransaction.hide(centerFragment);
fragmentTransaction.hide(homeFragment);
fragmentTransaction.hide(profileFragment);
}
if(fragment == profileFragment){
fragmentTransaction.hide(search);
fragmentTransaction.hide(centerFragment);
fragmentTransaction.hide(notification);
fragmentTransaction.hide(homeFragment);
}
fragmentTransaction.show(fragment);
//fragmentTransaction.replace(R.id.main_container, fragment);
fragmentTransaction.commit();
}

BottomNavigationView - How to avoid recreation of Fragments and reuse them

I would like to make a bottom navigation bar in my project. Every view has it's own fragment. The problem is that every time i click on the button to change the view for example from recents to favorites it creates new fragment with completely new states(e.g. scroll position, text changed whatever my fragment contains). I know that in official Android documentation there was written that bottom navigation bar should reset the task states, but i think it is too uncomfortable for users.
I would like to have kind of similar functionality like instagram that you change from feed to explore and back to feed the scroll position the image caches everything remains saved. I tried almost every way to solve this problem the only thing that worked is by setting visibility GONE and setting visibility VISIBLE according to situation but i understand that it is not RIGHT way there should be better way of doing this and i am not talking about manually saving needed instances. I followed almost every tutorial about bottom nav fragments but the interesting thing is that no one is interested to make it use without calling new every time.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, FirstFragment.newInstance());
fragmentTransaction.commit();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
fragment = FirstFragment.newInstance();
break;
case R.id.menu_email:
fragment = SecondFragment.newInstance();
break;
case R.id.menu_map:
fragment = ThirdFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
return true;
}
});
I also tried the onAttach and Deattach solutions but again no success.
VIDEO LINK : new i tried Nino Handler version it only works when i tap on the same fragment button
Maybe it is connected that i am using canary version or something wrong in my gradle dependencies?
NEW UPDATES:
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private FragmentManager fragmentManager;
private Fragment currentFragment;
String TAG = "babken";
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
Fragment fragment = null;
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.navigation_dashboard:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = FragmentSecond.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
}
return true;
}
};
private void replaceFragment(#NonNull Fragment fragment, #NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.armen, fragment, tag)
.commit();
currentFragment = fragment;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
I had similar issue, but this code solved my problem.
public class MainActivity extends AppCompatActivity {
boolean doubleBackToExitPressedOnce = false;
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
BottomNavigationView navigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
setFragment(fragment1, "1", 0);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
setFragment(fragment1, "1", 0);
return true;
case R.id.navigation_dashboard:
setFragment(fragment2, "2", 1);
return true;
case R.id.navigation_notifications:
setFragment(fragment3, "3", 2);
return true;
}
return false;
}
};
public void setFragment(Fragment fragment, String tag, int position) {
if (fragment.isAdded()) {
fm.beginTransaction().hide(active).show(fragment).commit();
} else {
fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
}
navigation.getMenu().getItem(position).setChecked(true);
active = fragment;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (active == fragment1) {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
} else {
setFragment(fragment1, "1", 0);
}
}
}
I wouldn't keep the fragment instances globally.
Instead add a tag to the fragment when creating them
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, new PlaceholderFragment(), TAG_PLACEHOLDER)
.commit();
Then you can always retrieve it like this:
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
if (fragment == null) {
fragment = new PlaceholderFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, TAG_PLACEHOLDER)
.commit();
UPDATE: I updated my answer and to provide a complete solution:
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private static final String TAG_FRAGMENT_THREE = "fragment_three";
private FragmentManager fragmentManager;
private Fragment currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// instantiate the fragment manager
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
// I'm aware that this code can be optimized by a method which accepts a class definition and returns the proper fragment
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.menu_email:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = SecondFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
case R.id.menu_map:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_THREE);
if (fragment == null) {
fragment = ThirdFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_THREE);
break;
}
return true;
}
});
}
private void replaceFragment(#NonNull Fragment fragment, #NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.frameLayout, fragment, tag)
.commit();
currentFragment = fragment;
}
}
ADDITIONAL INFO: If you want to be sure that the fragment states don't change and if you also want to be able to swipe the fragments you should consider using a ViewPager with a FragmentStatePagerAdapter and change the current fragment in the adapter with every click event
I wrote a Kotlin Extension function for FragmentManager class
fun FragmentManager.switch(containerId: Int, newFrag: Fragment, tag: String) {
var current = findFragmentByTag(tag)
beginTransaction()
.apply {
//Hide the current fragment
primaryNavigationFragment?.let { hide(it) }
//Check if current fragment exists in fragmentManager
if (current == null) {
current = newFrag
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
}
This can be called by supportFragmentManager.swtich(R.id.container,newFrag,newFrag.TAG) from onNavigationItemSelected
All the previous answers are using fragmentTransaction.replace(...). This will replace the current fragment by destroying it (which is causing the problem). Therefore all those solutions won't actually work.
This is the closest thing I could get to as a solution for this problem:
private void selectContentFragment(Fragment fragmentToSelect)
{
FragmentTransaction fragmentTransaction = this.getSupportFragmentManager().beginTransaction();
if (this.getSupportFragmentManager().getFragments().contains(fragmentToSelect)) {
// Iterate through all cached fragments.
for (Fragment cachedFragment : this.getSupportFragmentManager().getFragments()) {
if (cachedFragment != fragmentToSelect) {
// Hide the fragments that are not the one being selected.
fragmentTransaction.hide(cachedFragment);
}
}
// Show the fragment that we want to be selected.
fragmentTransaction.show(fragmentToSelect);
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction.add(R.id.fragment_container, fragmentToSelect);
}
fragmentTransaction.commit();
}
To make this work, you should keep track of the fragments in an array (or in separate variables) in your Activity. I for reference pre-instantiated all fragments in a SparseArray.
You can use attach() and detach() methods:
private FirstFragment firstFragment = FirstFragment.newInstance();
private SecondFragment secondFragment= SecondFragment.newInstance();
private ThirdFragment thirdFragment = ThirdFragment.newInstance();
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_dialer:
changeFragment(firstFragment, "firstFragment");
return true;
case R.id.menu_email:
changeFragment(secondFragment, "secondFragment");
return true;
case R.id.menu_map:
changeFragment(thirdFragment, "thirdFragment");
return true;
}
return false;
}
});
public void changeFragment(Fragment fragment, String tagFragmentName) {
FragmentManager mFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
if (currentFragment != null) {
fragmentTransaction.detach(currentFragment);
}
Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
if (fragmentTemp == null) {
fragmentTemp = fragment;
fragmentTransaction.add(R.id.content, fragmentTemp, tagFragmentName);
} else {
fragmentTransaction.attach(fragmentTemp);
}
fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();
}
So I have been researching for this for a long time and figured out that there is no way to reuse fragment with AUTOMATICALLY saved states you have to save your needed states manually then retrieve them whenever a new fragment is created but what about scroll position it is too hard and even in some cases it is impossible to save scroll view position state(e.g. recycler view). So i used the concept called VISIBILITY when i click on the button that fragment becomes visible others hide automatically.
How about that.
You declare the fragments in the class.
Fragment firstFragment,secondFragment,thirdFragment;
then in the switch-case you can code as that:
switch (item.getItemId()) {
case R.id.menu_dialer:
if(firstFragment != null) {
fragment = firstFragment;
}else{
fragment = FirstFragment.newInstance();
}
break;
case R.id.menu_email:
// the same ...
break;
case R.id.menu_map:
//the same ...
break;
}
Thomas's answer nearly helped me but had an issue that whenever I open new fragments the very first time, they overlapped but doesn't get overlapped once I open them again by pressing the menu buttons.
So I modified his code and obtained the solution using the following code:
private fun selectContentFragment(fragmentToSelect: Fragment) {
val fragmentTransaction = fragmentManager?.beginTransaction()
if (fragmentManager?.fragments?.contains(fragmentToSelect)!!) {
// Show the fragment that we want to be selected.
fragmentTransaction?.show(fragmentToSelect)
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction?.add(R.id.container, fragmentToSelect)
}
// Iterate through all cached fragments.
for (cachedFragment in fragmentManager?.fragments!!) {
if (cachedFragment !== fragmentToSelect) {
// Hide the fragments that are not the one being selected.
// Uncomment following line and change the name of the fragment if your host isn't an activity and a fragment otherwise whole view will get hidden.
// if (!cachedFragment.toString().contains("HomeContainerFragment"))
fragmentTransaction?.hide(cachedFragment)
}
}
fragmentTransaction?.commit()
}
Make sure you're not passing the fragment's new instance every time.
This will work:
selectContentFragment(
when (item.itemId) {
R.id.home -> frag1
R.id.photoGallery -> frag2
else -> Home()
}
)
where frag1 and frag2 are global variables defined as:
val frag1 = Home()
val frag2 = PhotoGallery()
This will not work:
selectContentFragment(
when (item.itemId) {
R.id.home -> Home()
R.id.photoGallery -> PhotoGallery()
else -> Home()
}
)
It wasted my several hours. Hope it helps others!
**This code very helpful for me. Just like youtube. **
private Deque<Integer> fragmentIds = new ArrayDeque<>(3);
int itemId;
private HomeFragment homeFragment = new HomeFragment();
private FavouriteFragment favouriteFragment = new FavouriteFragment();
private NearmeFragment nearmeFragment = new NearmeFragment();
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentIds.push(R.id.action_home);
showTabWithoutAddingToBackStack(homeFragment);
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemClicked);
}
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
itemId= item.getItemId();
if(fragmentIds.contains(itemId)){
fragmentIds.remove(itemId);
}
fragmentIds.push(itemId);
showTabWithoutAddingToBackStack(getFragment(item.getItemId()));
return true;
}
};
private Fragment getFragment(int fragmentId) {
switch (fragmentId) {
case R.id.action_home:
return homeFragment;
case R.id.action_favorites:
return favouriteFragment;
case R.id.action_nearme:
return nearmeFragment;
}
return homeFragment;
}
private void showTabWithoutAddingToBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()).commit();
}
#Override
public void onBackPressed() {
if(fragmentIds.getLast() != R.id.action_home){
fragmentIds.addLast(R.id.action_home);
}
fragmentIds.pop();
bottomNavigationView.getMenu().getItem(fragmentIds.size()-1).setChecked(true);
if (!fragmentIds.isEmpty()) {
showTabWithoutAddingToBackStack(getFragment(fragmentIds.peek()));
} else {
finish();
}
}
For Kotlin user may help this code :
First create a FragmentManager extension class
fun FragmentManager.replace(containerId: Int, fragment: Fragment, tag: String) {
var current = findFragmentByTag(tag)
beginTransaction()
.apply {
//Hide the current fragment
primaryNavigationFragment?.let { hide(it) }
//Check if current fragment exists in fragmentManager
if (current == null) {
current = fragment
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
}
Now simply call it on your
onNavigationItemSelected
supportFragmentManager.replace(R.id.fragment_id,YourFragmentClass,yourFragment.TAG)
I had the same issue but I resolved it by creating multiple fragment hosts then I added fragments in fragmentHost. And when bottom nav itemSelected I just make the required host fragment visible and other host fragmentsHost to visibility gone.
This method may be wrong but it works perfectly for me.
And we don't have to manually handle the states of fragment only we need to handle backpress of activity.
But this method does not pause the fragments.
And I don't know how to handle the pauses and resumes of fragments so please reply to me.
Maybe This will help you.
This is my fragmentHosts Home Activity:
<androidx.fragment.app.FragmentContainerView
android:id="#+id/homeFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/libraryFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/myStuffFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/moreFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_NavBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/app_bottom_nav_menu"
tools:ignore="BottomAppBar" />
Home Activity onCreate:
supportFragmentManager.beginTransaction().replace(R.id.homeFragmentHost,HomeFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.libraryFragmentHost,LibraryFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.myStuffFragmentHost,MyStuffFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.moreFragmentHost,MoreFragment()).commitNow()
homeFragmentHost.visibility = View.VISIBLE
Bottom nav Item Selected listener
override fun onNavigationItemSelected(item: MenuItem): Boolean {
return when(item.itemId){
R.id.homeFragment -> loadFragmentHost(homeFragmentHost)
R.id.libraryFragment -> loadFragmentHost(libraryFragmentHost)
R.id.myStuffFragment -> loadFragmentHost(myStuffFragmentHost)
R.id.moreFragment -> loadFragmentHost(moreFragmentHost)
else -> false
}
loadFragmentHost function
private fun loadFragmentHost(view:FragmentContainerView): Boolean {
val list = arrayListOf(homeFragmentHost,libraryFragmentHost,myStuffFragmentHost,moreFragmentHost)
list.remove(view)
view.visibility = View.VISIBLE
list.forEach {
it.visibility = View.GONE
}
return true
}
public class MainActivity extends AppCompatActivity {
boolean doubleBackToExitPressedOnce = false;
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
BottomNavigationView navigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
setFragment(fragment1, "1", 0);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
setFragment(fragment1, "1", 0);
return true;
case R.id.navigation_dashboard:
setFragment(fragment2, "2", 1);
return true;
case R.id.navigation_notifications:
setFragment(fragment3, "3", 2);
return true;
}
return false;
}
};
public void setFragment(Fragment fragment, String tag, int position) {
if (fragment.isAdded()) {
fm.beginTransaction().hide(active).show(fragment).commit();
} else {
fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
}
navigation.getMenu().getItem(position).setChecked(true);
active = fragment;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (active == fragment1) {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
} else {
setFragment(fragment1, "1", 0);
}
}
}
Maintain Bottom sheet fragment Reusable
BackPress Maintain
double back press to exit
public class MainActivity extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
Toaster toaster;
private final Fragment androidFragment = new AndroidFragment();
private final Fragment settingsFragment = new SettingsFragment();
Fragment active;
String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toaster = new Toaster(this);
bottomNavigationView = findViewById(R.id.bottom_navigation);
renderFragment(androidFragment, "android");
active = androidFragment;
bottomNavigationView.setOnItemSelectedListener(
item -> {
Log.e(TAG, "onCreate: " + active );
if(item.getItemId() == R.id.action_android){
renderFragment(androidFragment, "android");
return true;
}
else if(item.getItemId() == R.id.action_settings){
renderFragment(settingsFragment, "settings");
return true;
}
return false;
}
);
}
private void renderFragment(Fragment fragment, String tag){
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragment.isAdded()){
fragmentManager.beginTransaction().hide(active).show(fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
else {
if(active != null){
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_view, fragment, tag)
.hide(active)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
else {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_view, fragment, tag)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
}
active = fragment;
}
}
This worked for me Guys.
Create the three fragments as members of the class and reuse them.
public class MainActivity extends AppCompatActivity {
private final Fragment mFirstFragment = new FirstFragment();
private final Fragment mSecondFragment = new SecondFragment();
private final Fragment mThirdFragment = new ThirdFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
......
......
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, mFirstFragment);
fragmentTransaction.commit();
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
if (bottomNavigationView.getSelectedItemId() != item.getItemId()) {
switch (item.getItemId()) {
R.id.menu_dialer:
replaceFragment(mFirstFragment);
break;
case R.id.menu_email:
replaceFragment(mSecondFragment);
break;
case R.id.menu_map:
replaceFragment(mThirdFragment);
break;
}
}
return true;
}
});
}
private void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
}

Back Stack in fragments not working

I have a home activity in that I am replacing fragments as required.
In home activity I have main fragment, then from main fragment I am replacing a Transport fragment, from Transport fragment I am replacing TransportList Fragment.
Now as I press back from TransportList fragment I see the main fragment instead of Transport fragment.
I have added the fragments to backstack still its working like this.
Home activity
public class HomeActivity extends AppCompatActivity{
private boolean mBackPressCancelled = false;
private static final long BACK_PRESS_DELAY = 10000;
private long mBackPressTimestamp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FragmentManager fragmentManager = HomeActivity.this.getFragmentManager();
MainFragment fragment = new MainFragment();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.mycontainer, fragment,"MAIN_FRAGMENT").commitAllowingStateLoss();
}
#Override
public void onBackPressed() {
// Do nothing if the back button is disabled.
if (!mBackPressCancelled) {
// Pop fragment if the back stack is not empty.
if (getFragmentManager().getBackStackEntryCount() > 0) {
mTxtTitle.setVisibility(View.GONE);
mLogo.setVisibility(View.VISIBLE);
super.onBackPressed();
}
else {
if (snackbar != null) {
snackbar.dismiss();
}
long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp < mBackPressTimestamp + BACK_PRESS_DELAY) {
super.onBackPressed();
} else {
mBackPressTimestamp = currentTimestamp;
Toast.makeText(this,"press again",Toast.LENGTH_LONG).show();
}
}
}
}
}
Transport fragment :
mBtnSearch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager();
TransportListFragment fragment1 = new TransportListFragment();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.mycontainer, fragment1).addToBackStack("G").commit();
}
});
Whats going wrong here please help.Thank you.
Just remove the following lines when you add new Fragment to the BackStack:
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

Android BaseFragment class with multiple different fragment types

I created a base fragment class that handles setting the toolbar title, registering for when the fragment is attached, setting the menu icons and a few other things. My issue is that I've decided to use the PreferencesFragmentCompat for my settings fragment and I cant extend both my BaseFragment and androids PreferencesFragmentCompat. Using an interface here wouldn't help because my BaseFragment has a lot of functionality, and I don't want to duplicate it into each of my fragment classes. Normally to extend two classes, you just do it in two seperate files but because both already extend off Androids Fragment class, I dont see how this is possible. Is there a better way of doing this?
BaseFragment:
public abstract class BaseFragment extends Fragment {
protected View rootView;
protected AppSettings settings;
protected LayoutInflater inflater;
public static void startFragment(Activity activity, BaseFragment newFragment) {
FragmentManager fragManager = ((AppCompatActivity) activity).getSupportFragmentManager();
BaseFragment currentFragment = (BaseFragment) fragManager.findFragmentById(R.id.fragment_container);
// Start the transactions
FragmentTransaction transaction = fragManager.beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
// If there is already a fragment then we want it on the backstack
if (currentFragment != null) {
transaction.addToBackStack(null);
}
// Show it
transaction.commit();
}
#TargetApi(21)
private void lockMode(boolean start) {
if (android.os.Build.VERSION.SDK_INT >= 16) {
if (start) {
getActivity().startLockTask();
} else {
getActivity().stopLockTask();
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get a reference to the app settings
settings = AppSettings.getInstance(getActivity());
// Don't want keyboard to stay open between fragments
hideKeyboard();
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
if (toolbarElevation()) {
actionBar.setElevation(4 * getActivity().getResources().getDisplayMetrics().density);
} else {
actionBar.setElevation(0);
}
}
setHasOptionsMenu(true);
}
#Override
public void onResume() {
super.onResume();
// Set the title up
Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
toolbar.setTitle(getTitle());
// Enable the home button in the action bar
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayShowHomeEnabled(true);
// Change the home button icon for menu or back
if (showUpNavigation()) {
toolbar.setNavigationIcon(R.drawable.ic_navigation_back_white);
} else {
toolbar.setNavigationIcon(R.drawable.ic_menu_white);
}
if (isAppInLockTaskMode() == true && pinnedMode() == false) {
lockMode(false);
}
setDrawerMenu();
}
public boolean getAuthRequired() {
return true;
}
public boolean isBackAllowed() {
return true;
}
public boolean toolbarElevation() {
return true;
}
public String getTitle() {
return "ISOPED";
}
public boolean pinnedMode() {
return false;
}
public boolean showUpNavigation() {
return false;
}
public void hideKeyboard() {
InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
// check if no view has focus:
View v = getActivity().getCurrentFocus();
if (v == null) {
return;
}
inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
public void setDrawerMenu() {
NavigationView navigationView = (NavigationView) getActivity().findViewById(R.id.drawer_navigation);
Integer menuID = null;
Integer currentMenuId = null;
if (settings.isType(AppSettings.TYPES.PERSONAL)) {
menuID = R.menu.drawer_personal;
} else if (settings.isType(AppSettings.TYPES.PROFESSIONAL)) {
if (getAuthRequired() == true) {
menuID = R.menu.drawer_professional_locked;
} else {
menuID = R.menu.drawer_professional_unlocked;
}
}
if (menuID != null) {
if (navigationView.getTag() != null) {
currentMenuId = (Integer) navigationView.getTag();
}
if (currentMenuId == null || navigationView.getMenu().size() == 0 || currentMenuId != menuID) {
navigationView.getMenu().clear();
navigationView.inflateMenu(menuID);
navigationView.setTag(Integer.valueOf(menuID));
}
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear();
if (settings.isType(AppSettings.TYPES.PROFESSIONAL) && pinnedMode() && false == isAppInLockTaskMode()) {
inflater.inflate(R.menu.pin_menu, menu);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
switch (item.getItemId()) {
case android.R.id.home:
if (showUpNavigation()) {
getActivity().onBackPressed();
} else {
drawer.openDrawer(GravityCompat.START);
}
return true;
case R.id.menu_pin:
if (isAppInLockTaskMode()) {
PinDialog dialog = new PinDialog((AppCompatActivity) getActivity(), new NavigationCallback((AppCompatActivity) getActivity()) {
#Override
public void run() {
lockMode(false);
}
});
dialog.show();
} else {
lockMode(true);
}
return true;
}
return super.onOptionsItemSelected(item);
}
private boolean isAppInLockTaskMode() {
ActivityManager activityManager;
activityManager = (ActivityManager)
getActivity().getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// For SDK version 23 and above.
return activityManager.getLockTaskModeState()
!= ActivityManager.LOCK_TASK_MODE_NONE;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// When SDK version >= 21. This API is deprecated in 23.
return activityManager.isInLockTaskMode();
}
return false;
}
}
This is a nice example, where you should apply Joshua Bloch's "Favor composition over inheritance" idiom.
You can delegate all the logic that you have applied to BaseFragment to some class FragmentHelper:
public class FragmentHelper {
private final Fragment fragment;
public FragmentHelper(Fragment fragment) {
this.fragment = fragment;
}
public void create(Bundle bundle) {
// `BaseFragment`'s code goes here
}
public void resume() {
// `BaseFragment`'s code goes here
}
...
}
Now in your BaseFragment:
public class BaseFragment {
private FragmentHelper fragmentHelper;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
fragmentHelper = new FragmentHelper(this);
fragmentHelper.create(savedInstanceState);
}
#Override
public void onResume() {
fragmentHelper.resume();
}
}
And the same this should be applied to the class that is extending PreferenceFragment.
Thus, you'd evade from code duplication.
Reference:
Joshua Bloch - Effective Java 2nd edition, Item 16.

handle DrawerLayout in fragments

I am trying to debug an app built with fragments. Since I don't know nearly anything about fragments, I have some problems with them.
I want the action button to be transformed in back button when I open a fragment from MainActivity or another fragment.
I kinda made that work, but when I press back from a fragment and it resumes the other the back button transforms back to home button.
I want to know how I can make that home button behave like the back button on Android when it transforms to the back arrow. It does nothing currently.
Here is some code I think it's relevant, I will provide more if you need:
MainActivity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_trash_all:
DataHolder.cart = null;
isTrashButtonPresent = false;
supportInvalidateOptionsMenu();
inflateFragment(CartFragment.newInstance(null, null));
resolveCircularText(DataHolder.getOverallCartQuantity());
return true;
case R.id.btnMyMenu:
if (drawer.getDrawerLockMode(GravityCompat.START)
==DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
onBackPressed();
} else {
drawerToggle();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void drawerToggle() {
if (drawer.isDrawerVisible(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
drawer.openDrawer(GravityCompat.START);
}
}
private void setupNavigationDrawer() {
mDrawerToggle = new ActionBarDrawerToggle(
this, drawer, toolbar,
R.string.navigation_drawer_open,R.string.navigation_drawer_close);
mDrawerToggle.setHomeAsUpIndicator(R.drawable.drawer_icon);
drawer.addDrawerListener(mDrawerToggle);
navigationView.setNavigationItemSelectedListener(this);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
setDrawerState(true);
}
public void setDrawerState(boolean isEnabled) {
if (isEnabled) {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED,
GravityCompat.START);
mDrawerToggle.syncState();
} else {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
GravityCompat.START);
//Drawable backButton = ContextCompat.getDrawable(this,
R.mipmap.ic_back_button);
getSupportActionBar().setHomeAsUpIndicator(null);
}
}
Fragments:
OnCreate:
if (getActivity() != null) {
((MainActivity) getActivity()).setDrawerState(false);
}
#Override
public void onDestroy() {
super.onDestroy();
if (getActivity() != null) {
((MainActivity) getActivity()).setDrawerState(true);
}
}
Thank you in advance!
Try using Interface class:
public interface DrawerViewInterface {
void lockDrawer();
void unlockDrawer();
void showBackIcon();
void showHamburgerIcon();
}
Now, Implement this Interface class in your MainActivity:
public class MainActivity extends AppCompactActivity implements DrawerViewInterface {
#Override
public void lockDrawer() {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
#Override
public void unlockDrawer() {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
#Override
public void showBackIcon() {
mActionBarDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
public void showHamburgerIcon() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
mActionBarDrawerToggle.setDrawerIndicatorEnabled(true);
}
}
Now, In your Fragment, call whatever you want by:
((DrawerViewInterface) getActivity()).showHamburgerIcon();
or
((DrawerViewInter) getActivity()).showBackIcon();

Categories

Resources