I used Navigation Drawer to setup a Drawer.
In my MainActivity on the onCreate method, I have the following setup
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frame = new FrameLayout(this);
frame.setId(CONTENT_VIEW_ID);
setContentView(frame, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
if (savedInstanceState == null) {
setFragment(0);
}
}
And my setFragment Method, within the Main Activity is this:
public void setFragment(int id) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch(id){
case 0:
fragmentTransaction.add(CONTENT_VIEW_ID, FragOne.newInstance()).commit();
break;
case 1:
fragmentTransaction.replace(CONTENT_VIEW_ID,FragTwo.newInstance()).commit();
break;
}
}
note I've declared CONTENT_VIEW_ID as:
private static final int CONTENT_VIEW_ID = 666;
When the application starts, it loads the fragment one and it works fine.
When I swipe open the Navigation drawer and select the position 1, it replaces the current fragment with the FragTwo, but right after that the navigation drawer freezes, I can't access it anymore.
Can anyone help me resolve this issue, I been searching online for awhile now and couldn't find a solution to my problem, that why I have decided to ask here, thank you in advanced :)
Asuming you have all the other needed code t make a drawerLayout work fine, I think you should use .replace(...) in both cases. It works for me, even though it freezes a little while fragment is being load before it finishes closing.
Related
I have a Navigation Drawer in the main activity of my app. On the onCreate method of the activity I initialize one of the fragments like this :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
MenuItem menuItem = navigationView.getMenu().findItem(R.id.menu_history);
openFragment(menuItem);
}
public void openFragment(MenuItem menuItem){
Fragment newFragment = null;
switch (menuItem.getItemId()){
case R.id.menu_history :
newFragment = new HistoryFragment();
break;
//.....
}
if (newFragment != null){
//Replace content frame in activity_main.xml with newFragment
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, newFragment)
.commit();
menuItem.setChecked(true);
getSupportActionBar().setTitle(menuItem.getTitle());
}
drawerLayout.closeDrawers();
}
This all works well, the fragment appears on startup with the title on the toolbar being "History". But when the app goes into onPause and then onResume the toolbar title switches from "History" to the app name. I suspect this is an issue with onResume not opening the fragment / returning to its previous state correctly, because when I added the following lines to onResume the issue stopped:
#Override
protected void onResume() {
super.onResume();
MenuItem menuItem = navigationView.getMenu().findItem(R.id.menu_history);
openFragment(menuItem);
}
That solution seems to fix it but that means it has to reload the fragment with the animations every time the app is resumed, which isn't optimal. Any ideas on how to fix this?
If it helps, to recreate the issue I found useful to make the app split screen, because it always calls onResume. Thanks.
Inside your openFragment() method, you write:
getSupportActionBar().setTitle(menuItem.getTitle());
This is the only thing that changes your toolbar's title.
Note that this code has nothing to do with your Fragments, per se. When your activity is destroyed and recreated, the active Fragment will be successfully (automatically) destroyed and recreated by the FragmentManager... but your openFragment() method won't run again and so nothing will update your toolbar's title.
There are numerous ways you could solve this. Probably the right thing to do is update your toolbar's title from within one of your Fragment's lifecycle methods.
Edit: A reasonable place to update your toolbar's title would be in your fragment's onActivityCreated() method. This will run both when the fragment is first added and during recreation. Something like:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("hello world");
}
If you do not want to re-create the fragment each time, you should use saveInstanceState like mentioned here: https://stackoverflow.com/a/17135346/1505074
Ok so i'm building an android app that uses this library for a bottom navigation and i'm using a base Activity to hold it along with a Framelayout to manage my fragments for loading/replacing etc.
What works:
tapping on a bottom bar icon loads the fragment it corresponds to and that works perfectly.
My problem:
If i tap on the first tab and then the second tab and then the first tab AGAIN, the entire fragment reloads from scratch.
I don't want this behavior. Anyone have any good tips on how to retain the state of a fragment while ALSO using the bottom bar library.
I achieved something similar with a pagerview in a previous app (the previous app did not use a bottom bar for navigation) but I'm not sure how to use a pager view with ONE base activity that holds the Framelayout for replacing the fragments or if that is even the best solution.
I like the solution i have so far except that the fragments reload from scratch each time they replace the previous. If anyone has any help or suggestions that can help me out it would be greatly appreciated.
Alright i seemed to figure out a work around for the time being. It keeps the fragment state after switching tabs so I'm satisfied.
In the base activity class that hosts the fragment container i have the following
public class BaseActivity extends AppCompatActivity
{
AFragment AFragment = new AFragment();
BFragment BFragment = new BFragment();
Fragment currentFragment;
Boolean aIsActive = false;
Boolean bIsActive = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
BottomBar bottomBar = BottomBar.attach(this, savedInstanceState);
bottomBar.setItems(
new BottomBarTab(null,"A"),
new BottomBarTab(null,"B")
);
bottomBar.setDefaultTabPosition(0);
bottomBar.setOnTabClickListener(new OnTabClickListener()
{
#Override
public void onTabSelected(int position)
{
if (position == 0)
{
if(!aIsActive)
{
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer,AFragment).commit();
aIsActive = true;
}
else
{
getSupportFragmentManager().beginTransaction().hide(currentFragment).show(AFragment).commit();
}
currentFragment = AFragment;
}
else if(position == 1)
{
if(!bIsActive)
{
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer,BFragment).commit();
bIsActive = true;
}
else
{
getSupportFragmentManager().beginTransaction().hide(currentFragment).show(BFragment).commit();
}
currentFragment = BFragment;
}
}
#Override
public void onTabReSelected(int position) {
}
});
}
}
And loe and behold it works as expected without refreshing the fragments :)
any suggestions or feedback please let me know and feel free to comment.
Now im into my first question, after not finding an answer in the internet. Usually this should be a really easy thing, but it took me hours and hours so please excuse me if its a beginner question. Also i want to make sure im going the right way before spending more days just to see that in the end it just not works as i want, the way i implement it atm.
The problem:
My App has 3 Tabs with 3 fragments as content, one for every tab. I use Tabs because all users i asked want Tabs and not a Navigation Drawer and i listen to users, not to google (sorry). Also the 3 tabs have very different "roles" (Language Guide, Currency convertor, and own Notebook for own phrases etc) and it makes sense to split it this way (imo). If someone has other ideas, go ahead, im open :-)
The tab-navigation works as intended so far. But now i want to build the further navigation for the single Tabs an im not sure anymore wich way to go... Theres just so many way for a simple thing like this...
The first Tab has some imageButtons to further navigate into this section (See Screenshot 1), to choose a category in the language section. Of course this means the tabs should be stay in place to be able to easy switch to the Converter i.E, just the fragment from tab 1 should get switched to the new fragment (see Screenshot 2), and so on. The Buttons that you see in the below screenshot have clicklisteners that change current fragment to the new one (like "Daily")
For this i open another fragment and add it to the backstack. Maybe you already can guess what happens now. If i press the backbutton, i come back to the first fragment of course. Problems start when i switch tabs when im inside a fragment opened on tab 1. The added fragment is still in backstack what means if i press back button on Tab 2 or 3, the displayed content gets mixes up somehow with the other fragment ones. See Screenshot 3
Do i make an error somehere or can i simply tell the backstack to "reset" when switching tabs so this problem doesnt occur?
Thank you for info and sorry for language and spelling errors, english isnt my main language.
Tablistener Class to switch the tab fragments:
public class MyTabListener implements ActionBar.TabListener {
Fragment fragment;
public MyTabListener(Fragment fragment) {
this.fragment = fragment;
}
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment);
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// nothing done here
}
}
The Fragments class from first fragment look like this (not full code):
public static class FragmentTab1 extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
final View view = inflater.inflate(R.layout.tab, container, false);
ImageButton catBtn_Daily = (ImageButton) view.findViewById(R.id.catBtn_Daily);
ImageButton catBtn_OnTheRoad = (ImageButton) view.findViewById(R.id.catBtn_OnTheRoad);
ImageButton catBtn_Shopping = (ImageButton) view.findViewById(R.id.catBtn_Shopping);
ImageButton catBtn_Restaurant = (ImageButton) view.findViewById(R.id.catBtn_Restaurant);
ImageButton catBtn_Romance = (ImageButton) view.findViewById(R.id.catBtn_Romance);
ImageButton catBtn_Emergency = (ImageButton) view.findViewById(R.id.catBtn_Emergency);
// attach an OnClickListener
catBtn_Daily.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Create new fragment and transaction
Fragment newFragment = new FragmentTab4();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.setTransition(4097);
transaction.commit();
}
});
Mainactivity:
public class MainActivity extends Activity {
ActionBar.Tab tab1, tab2, tab3;
Fragment fragmentTab1 = new FragmentTab1();
Fragment fragmentTab2 = new FragmentTab2();
Fragment fragmentTab3 = new FragmentTab3();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_test);
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
tab1 = actionBar.newTab().setText("Home");
tab2 = actionBar.newTab().setText("Calculator");
tab3 = actionBar.newTab().setText("Notebook");
tab1.setTabListener(new MyTabListener(fragmentTab1));
tab2.setTabListener(new MyTabListener(fragmentTab2));
tab3.setTabListener(new MyTabListener(fragmentTab3));
actionBar.addTab(tab1);
actionBar.addTab(tab2);
actionBar.addTab(tab3);
Cannot post pics yet, rep too low....
Please see here (third pic is called Untitled-3.png)
Screen 1: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-1.png
Screen 2: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-2.png
Screen 3: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-3.png
you might be better off not using the fragment back stack at all. you can maintain your own stack and override onBackPressed() to provide the appropriate behavior.
#Override protected void onBackPressed()
{
if(myStack.isEmpty())
super.onBackPressed(); // default handling finishes the activity
else
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, myStack.pop());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
transaction.commit();
}
}
Actually it sounds like you might want to consider a completely different approach. If I am understanding correctly you might want to consider using a ViewPager with a FragmentPagerAdapter (see the google documentation code sample here: FragmentPagerAdapter).
Essentially the viewpager presents the fragments under your tabs, the adapter provides the list of fragments to show and you select the current "page" by calling setCurrentItem on the viewpager (when the user clicks a tab). Additionally users will be able to swipe between pages (you would update the selected tab by listening to the pager events see google documentation code sample here: ViewPager).
This approach avoids the whole backstack issue and sounds more like what you are after.
Hope it helps.
You can try following:
public void onTabSelected(YourTab tab) {
Fragment fragment = new YourTabFragment();
replaceRootFragment(fragment);
}
public void replaceRootFragment(Fragment fragment) {
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
int id = getSupportFragmentManager().getBackStackEntryAt(0).getId();
try {
getSupportFragmentManager().popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} catch (IllegalStateException e) {
return;
}
}
getSupportFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.container, fragment)
.commit();
}
Call onTabSelected when you click on the tab.
So I have this situation. I have one activity with content FrameLayout. Then I create two SupportMapFragments and add it into content frame. Of course is visible only one at time and when I want to see another one then I hide the first and show the second. But what hapens is that I can move only with the first map and not with another. Is that bug or is there something that I need to do differentely than only show/hide fragments?
Thanks a lot :)
EDIT 1:
Here is sample code with what I am basicaly doing:
SupportMapFragment mapFragment1, mapFragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mapFragment1 = new SupportMapFragment();
mapFragment2 = new SupportMapFragment();
FragmentTransa ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.mapFrame, mapFragment1);
ft.add(R.id.mapFrame, mapFragment2);
ft.hide(mapFragment2);
ft.commit();
}
private void switch() {
FragmentTransa ft = getSupportFragmentManager().beginTransaction();
if (mapFragment1.isVisible()) {
ft.hide(mapFragment1);
ft.show(mapFragment2);
} else {
ft.hide(mapFragment2);
ft.show(mapFragment1);
}
ft.commit();
}
I'm calling switch() on button click and I'm able to move only with that mam that is created the first.
I am building a one activity-multiple fragments application. I add to the backstack after every transaction. After a couple of hiding and showing fragments and then I rotate the phone, all the fragments added on the container were restored and every fragment is on top of the other.
What can be the problem? Why is my activity showing the fragments I have previously hidden?
I am thinking of hiding all the previously-hidden-now-shown fragments but is there a more 'graceful' way of doing this?
Use setRetainInstance(true) on each fragment and your problem will disappear.
Warning: setting this to true will change the Fragments life-cycle.
While setRetainInstance(true) resolves the issue, there may be cases where you don't want to use it.
To fix that, setup a boolean attribute on the Fragment and restore the visibility:
private boolean mVisible = true;
#Override
public void onCreate(Bundle _savedInstanceState) {
super.onCreate(_savedInstanceState);
if (_savedInstanceState!=null) {
mVisible = _savedInstanceState.getBoolean("mVisible");
}
if (!mVisible) {
getFragmentManager().beginTransaction().hide(this).commit();
}
// Hey! no setRetainInstance(true) used here.
}
#Override
public void onHiddenChanged(boolean _hidden) {
super.onHiddenChanged(_hidden);
mVisible = !_hidden;
}
#Override
public void onSaveInstanceState(Bundle _outState) {
super.onSaveInstanceState(_outState);
if (_outState!=null) {
_outState.putBoolean("mVisible", mVisible);
}
}
Once the configuration changes (e.g. screen orientation), the instance will be destroyed, but the Bundle will be stored and injected to the new Fragment instance.
I had the same problem. you should check source code in the function onCreateView() of your activity.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
if(savedInstanceState == null){//for the first time
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentExample fragment = new FragmentExample();
fragmentTransaction.add(R.id.layout_main, fragment);
fragmentTransaction.commit();
}else{//savedInstanceState != null
//for configuration change or Activity UI is destroyed by OS to get memory
//no need to add Fragment to container view R.id.layout_main again
//because FragmentManager supported add the existed Fragment to R.id.layout_main if R.id.layout_main is existed.
//here is one different between Fragment and View
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/layout_main">
You might want to try to use the replace() function rather than hide and show. I had the same problem when I started using Fragments and using the replace function really helped manage the Fragments better. Here is a quick example:
fragmentManager.replace(R.id.fragmentContainer, desiredFragment, DESIRED_FRAGMENT_TAG)
.addToBackStack(null)
.commit();