I am using BottomNavigationBar and switch fragments with this. However, animations start to work only after the second showing of the same fragment. If you do not understand what, watch this video: https://drive.google.com/file/d/1AEAO_-wG2k17sMvpbrHu3UjlA4iAi6DQ/view?usp=sharing
onCreate():
fm = getSupportFragmentManager().beginTransaction();
fm.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fm.add(R.id.gen_fr, settings).hide(settings);
fm.add(R.id.gen_fr, events).hide(events);
fm.add(R.id.gen_fr, refs).hide(refs);
fm.add(R.id.gen_fr, earn);
fm.commit();
When switching fragments:
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
fm = getSupportFragmentManager().beginTransaction();
fm.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
switch (item.getItemId()) {
case R.id.main_room:
if (active != earn) {
fm.hide(active).show(earn);
active = earn;
}
break;
case R.id.refs_room:
if (active != refs) {
fm.hide(active).show(refs);
active = refs;
}
break;
case R.id.events_room:
if (active != events) {
fm.hide(active).show(events);
active = events;
}
break;
case R.id.settings_room:
if (active != settings) {
fm.hide(active).show(settings);
active = settings;
}
break;
}
fm.commit();
return true;
Related
When my user lands on the main activity for the first time I want the tiles(imageviews) to be greyed out and unclickable. Only the first tile should be in color and clickable. After the user accesses the first tile and returns to the main activity the next tile will become colored and clickable and so on.
The idea is to guide the user through the sequence on ther first time using the app. Im guessing this information will need to be stored in shared preferences as I only want this to happen the first time. Subsequent times all tiles should be in color and clickable.
Could someone help me achieve this? Currently I have a switch statement, a boolean and color/grey versions of the image tiles.
#Override
public void onClick(View v) {
SharedPreferences.Editor introEditor = introPref.edit();
switch (v.getId()) {
case R.id.cbt_button_grey:
Intent mainIntent = new Intent(getActivity(), IntroActivity2.class);
startActivity(mainIntent);
hasSelected = true;
introEditor.putBoolean("hasSelected", hasSelected);
introEditor.commit();
break;
case R.id.twisted_thinking_button_grey:
hasSelected = true;
Intent distortedIntent = new Intent(getActivity(), TwelveTypesDistortedThinkingSliderActivity.class);
startActivity(distortedIntent);
break;
case R.id.workout_button_grey:
hasSelected = true;
Fragment workoutFragment = new WorkoutFragment();
FragmentTransaction workoutTransaction = getActivity().getSupportFragmentManager().beginTransaction();
workoutTransaction.replace(R.id.fragment_container, workoutFragment);
workoutTransaction.commit();
break;
case R.id.workout_log_button_grey:
hasSelected = true;
Fragment workoutLogFragment = new WorkoutLogFragment();
FragmentTransaction workoutLogTransaction = getActivity().getSupportFragmentManager().beginTransaction();
workoutLogTransaction.replace(R.id.fragment_container, workoutLogFragment);
workoutLogTransaction.commit();
break;
case R.id.mood_log_button:
hasSelected = true;
Fragment moodLogFragment = new MoodLogFragment();
FragmentTransaction moodLogTransaction = getActivity().getSupportFragmentManager().beginTransaction();
moodLogTransaction.replace(R.id.fragment_container, moodLogFragment);
moodLogTransaction.commit();
break;
case R.id.activities_button_grey:
hasSelected = true;
Fragment activitiesFragment = new ActivitiesFragment();
FragmentTransaction activitiesTransaction = getActivity().getSupportFragmentManager().beginTransaction();
activitiesTransaction.replace(R.id.fragment_container, activitiesFragment);
activitiesTransaction.commit();
break;
}
}
How about using startActivityForResult() in MainActivity class. You can override onActivityResult() to make the Images enabled/disabled. It is just a suggestion.
My MainActivity has a Drawer and instantiates a new Fragment, depending on the clicked MenuItem (based on this tutorial).
I monitored the memory, which is slightly increasing on every Fragment change and I worry, that fragmentClass.newInstance() is not the right way.
// MainActivity
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
Fragment fragment = null;
Class fragmentClass = null;
if (id == R.id.nav_camera) {
fragmentClass = CameraFragment.class;
} else if (id == R.id.nav_gallery) {
fragmentClass = GalleryFragment.class;
} else if (id == R.id.nav_slideshow) {
fragmentClass = SlideshowFragment.class;
} else if (id == R.id.nav_manage) {
fragmentClass = ManageFragment.class;
} else if (id == R.id.nav_share) {
fragmentClass = ShareFragment.class;
} else if (id == R.id.nav_send) {
fragmentClass = SendFragment.class;
}
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.layout_main, fragment).commit();
}
I would expect something like:
// Pseudocode
if (fragmentManager.instanceExists(fragmentClass)) {
// load instantiated fragment
} else {
// newInstance()
}
How could I avoid memory leaks or is this even related?
Thank you!
Practically, there is no problems in that code. Things you can optimize are:
Reworking Fragment#instantiate() method (check docs)
Caching instances in something like Map in order not to create the new one every time you navigate through the drawer
Also, you can check if fragment exists in FragmentManager using FragmentManager#findFragmentById() or FragmentManager#findFragmentByTag()
i have an activity with a navigation drawer built using the library https://github.com/mikepenz/MaterialDrawer
I want to load 4 different pages from selecting 4 different items from the navigation drawer.
I decided to use fragments for each item in the navigation drawer, so when any item is pressed, a new fragment for that option is created and shown to the user.
What i want is that, regardless of which fragment the user is currently in, or which fragment he came from, whenever he presses the back button, he is returned to the original activity.
This is the code i have so far, but apparently the Fragment Backstack is not being updated.
result = new DrawerBuilder().withSelectedItem(-1)
.withActivity(this)
.withToolbar(toolbar)
.withAccountHeader(headerResult)
.addDrawerItems(
item1,item2,item3,item4
)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
#Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
result.setSelection(position,false);
clearStack();
switch (position) {
case 1:
fragment = new admin_fragment_data_entry();
break;
case 2:
fragment = new admin_fragment_data_edit();
break;
case 3:
break;
case 4:
break;
}
initiateFragment();
return false;
}
}
)
.build();
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (result != null && result.isDrawerOpen()) {
result.closeDrawer();
}
else if (count == 0) {
super.onBackPressed();
//additional code
}
else {
getFragmentManager().popBackStack();
}
}
private void initiateFragment(){
if (fragment != null) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.container_body, fragment);
ft.commit();
}
}
private void clearStack(){
int count = getFragmentManager().getBackStackEntryCount();
while(count > 0){
getFragmentManager().popBackStack();
count--;
}
}
Any help would be highly appreciated.
Also, should i have used fragments here? Or i should have just started 4 different activities for 4 different options from navigation drawer?
Are you using the correct fragment manager to get your backstack count?
I notice in other parts of your class you're using getSupportFragmentManager instead.
I have four tabs in navigation tab, and each tab has a fragment:
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position){
case 0:
return new PostJob();
case 1:{
return new PostedJobHistory();
}
case 2:{
return new SearchCandidates();
}
case 3:{
return new EmployerSettings();
}
default:
break;
}
return PlaceholderFragment.newInstance(position + 1);
}
I enter job details in first tab - PostJob fragment. and want to get it updated in the second tab when I go to Posted Job history fragment.
How can I achieve this.
In tab 1 I enter details in database
and in tab 2 i read database to show posted job history, but posted job history is not updated.
use this
List<Fragment> fragmentsList = getSupportFragmentManager().getFragments();
if (fragmentsList == null || fragmentsList.size() == 0) {
return;
}
for (Fragment fragment : fragmentsList) {
if (fragment instanceof PostedJobHistory) {
((PostedJobHistory) fragment).yourUpdateMethod();
}
}
In my main layout I have a central container which contains a fragment.
I started with FragmentTransaction.replace(...) to replace the fragment with other fragments but this recreate each fragment on each replacement, which makes the UI laggy (some fragments have complexe layout etc).
Instead of replace(), some suggest using .show() and .hide() to "cache" rendered fragments, and which leads to the code below:
private HashMap<String, Fragment> mCachedFragments = new HashMap<String, Fragment>();
private Fragment mCurrentFragment = null;
#Override
public void onNavigationDrawerItemSelected(int position) {
if (position == mCurrentSectionID)
return;
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
if (mCurrentFragment != null) {
Log.d("TRANSACTION", "putting fragment "+mCurrentFragment.getClass().getName());
mCachedFragments.put(mCurrentFragment.getClass().getName(), mCurrentFragment);
transaction.hide(mCurrentFragment);
}
Log.d("Nav", "selected: " + position);
Fragment dest = null;
switch (position) {
case BitsListFragment.FRAGMENT_ID:
dest = mCachedFragments.get(BitsListFragment.class.getName());
if (dest == null) {
dest = BitsListFragment.newInstance();
transaction
.add(R.id.container, dest);
} else {
Log.d("TRANSACTION", "bitlist retrieved");
transaction.show(dest);
}
onSectionAttached(BitsListFragment.FRAGMENT_ID);
break;
case AchievementsFragment.FRAGMENT_ID:
dest = mCachedFragments.get(AchievementsFragment.class.getName());
if (dest == null) {
dest = AchievementsFragment.newInstance();
transaction
.add(R.id.container, dest);
} else {
Log.d("TRANSACTION", "achievements retrieved");
transaction.show(dest);
}
onSectionAttached(AchievementsFragment.FRAGMENT_ID);
break;
case StatsFragment.FRAGMENT_ID:
dest = mCachedFragments.get(StatsFragment.class.getName());
if (dest == null) {
dest = StatsFragment.newInstance();
transaction
.add(R.id.container, dest);
} else {
Log.d("TRANSACTION", "stats retrieved");
transaction.show(dest);
}
onSectionAttached(StatsFragment.FRAGMENT_ID);
break;
}
mCurrentFragment = dest;
transaction.commit();
}
The code here works perfectly as long as the Activity does not gets recreated. The fragments gets retrieved and shown, and currentFragment is hidden when transition takes place.
As you can see, I'm basically keeping a reference to the CurrentFragment and a hashmap of all the cached fragments. The problem is that when I rotation the screen, the MainActivity gets recreated and these references are reset to null or emptied. Which ends up with BitsListFragment getting created multiple times and shown on screen overlapped.
Is there a better way to cache fragments/replace fragments without recreating them?
Thanks!
Actually found another way to do this: instead of keeping an reference on my own, we can add the fragment to the backstack and then retrieve it using corresponding tag. This let's fragmentManager manages the caching by itself. And the second time you access a fragment, it doesn't gets recreated.
The only hack that you should watch out is to override onBackPressed() so that you don't return to last fragment if you don't need to.
#Override
public void onNavigationDrawerItemSelected(int position) {
if (position == mCurrentSectionID)
return;
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
Fragment dest;
switch (position) {
case BitsListFragment.FRAGMENT_ID:
dest = fragmentManager.findFragmentByTag(BitsListFragment.class.getName());
if (dest == null) {
Log.d("TRANSACTION", "instanciating new fragment");
dest = BitsListFragment.newInstance();
}
transaction.replace(R.id.container, dest, BitsListFragment.class.getName());
transaction.addToBackStack(BitsListFragment.class.getName());
break;
case AchievementsFragment.FRAGMENT_ID:
dest = fragmentManager.findFragmentByTag(AchievementsFragment.class.getName());
if (dest == null) {
Log.d("TRANSACTION", "instanciating new fragment");
dest = AchievementsFragment.newInstance();
}
transaction.replace(R.id.container, dest, AchievementsFragment.class.getName());
transaction.addToBackStack(AchievementsFragment.class.getName());
break;
case StatsFragment.FRAGMENT_ID:
dest = fragmentManager.findFragmentByTag(StatsFragment.class.getName());
if (dest == null) {
Log.d("TRANSACTION", "instanciating new fragment");
dest = StatsFragment.newInstance();
}
transaction.replace(R.id.container, dest, StatsFragment.class.getName());
transaction.addToBackStack(StatsFragment.class.getName());
break;
}
transaction.commit();
onSectionAttached(position);
}