I follow SlidingMenu Example to create a sliding menu:
public class MyActivity extends FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
......
SlidingMenu menu = new SlidingMenu(this);
menu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW);
menu.setMenu(R.layout.activity_fragment);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, new MenuFragment())
.commit();
}
}
It works.
But this will execute new SlidingMenu(this) everytime when navigation to this activity.
I want to create SlidingMenu only once for this activtiy, so
The first modify try:
private static SlidingMenu sSlidingMenu;
......
if (sSlidingMenu == null) {
sSlidingMenu = new SlidingMenu(this);
}
sSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW);
sSlidingMenu.setMenu(R.layout.activity_fragment);
......
It works when executed first time, but it crash second time:
java.lang.IllegalStateException: This SlidingMenu appears to already be attached
I google this error tip, and found these similar questions:
Q1 StackoverFlow: This SlidingMenu appears to already be attached Androd
Q2 Github: Fix for changing content view after SlidingMenu is attached to the Activity. #324
then try
The Second modify try:
......
if (sSlidingMenu == null) {
sSlidingMenu = new SlidingMenu(this);
sSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW);
}
sSlidingMenu.setMenu(R.layout.activity_fragment);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, new MenuFragment())
.commit();
It also crash when execute second time:
java.lang.IllegalArgumentException: No view found for id 0x7f090035 (:id/fragmentContainer) for fragment MenuFragment
A similar question
I have no idea how to execute new SlidingMenu(this) only once to implement SlidingMenu. Please help, thanks.
Related
I am using BottomNavigationView with navigation according to android guidelines, the issue i am facing is new fragment is created everytime I go to another tab. I tried to explore and follow different solutions, but nothing works so far. Is there any work around which anyone has tried to make it work?
You can do this by creating fragment as singleton by using the below code snippet: -
class YourFragment extends Fragment{
private static YourFragment instance;
public static YourFragment getInstance(){
if (instance == null) {
instance = new YourFragment();
}
return instance;
}
}
Now create object of fragment using by YourFragment.getInstance();
It won't create a new object every time.
I used this method.
Fragment currentlyActiveFragment = null;
private void showActiveFragment(Fragment nextFragment){
currentlyActiveFragment = nextFragment;
if (currentlyActiveFragment!=null){
fragmentManager.beginTransaction()
.show(currentlyActiveFragment)
.commitAllowingStateLoss();
}
}
Then keep some references of your fragments:
private FragmentA fragmentA=null;
private FragmentB fragmentB=null;
// ... ...
So whenever you click on your button, you do the following check:
btnToOpenFragmentA.setOnClickListener(v -> {
if (fragmentA == null) {
// create this fragment
fragmentA = new FragmentA();
getSupportFragmentManager().beginTransaction()
.add(R.layout.baseFrameLayout, fragmentA, FragmentA.TAG)
.addToBackStack(null)
.commitAllowingStateLoss();
} else {
showActiveFragment(fragmentA);
}
});
So this basically checks: if the fragment already exists in the stack, don't create it, instead bring it to top, else create a new object.
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.
I am new to fragment concept. In my app I have to save user preferences. I have gone through this doc.
Prepared my preferences xml file and PreferenceFragment file. Everything is fine up to now.
My problem is, I have to add the following code in my onCreate() method of my MainActivity
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
It is showing on Main screen. But I want to launch this on a button click method
onSettingsClicked(){
// launch preferces screen
}
And I want to display it as a separate screen. How can I do that?
You need to implement the concept of fragmentTransaction.
You need to do the following things-
Create a new Fragment.xml to display the button.
Invoke the new fragment from the onCreate();
Get the Button on the Fragment view.
On Button onclick listener replace the the fragment with the SettingFragment().
Done!
Check out this FragmentTransaction Tutorial, it will guide you-
Do the following changes like -
// Code not accurate, may be some syntax error
#Override
public void onCreate()
{
// super and other stuff
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new NewFragment())
.commit();
Button btn = (Button)findViewById(R.id.button01);
btn.setOnClickListener(new OnClickListener(){
#override
public void onClick(View v)
{
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, new new SettingsFragment())
.commit();
}
});
}
Instead of using fragementTransaction you can use preferenceFragement :
How do you create Preference Activity and Preference Fragment on Android? by WannaGetHigh
I am developing an app in which i have to use left and right menu.I googled and found SlidingMenu library of #jfeinstein
Things are working fine i am able to open both left and right menu.But The only problem is that the secondry menu opens with unnecessary extra spacing.I need to remove that extra spacing.I have attached the screen shots also.
here spacing is fine
Please let me know the solution.
But here i need to make the content fit inside the menu.
and here is Activity for displaying menu
public class BaseSlidingMenuActivity extends SlidingFragmentActivity {
private int mTitleRes;
protected ListFragment mFrag;
protected SlideLeftMenuFragment mLeftFrag;
/**
* Title for Sliding Menu.
*
* #param titleRes
*/
public BaseSlidingMenuActivity(int titleRes) {
mTitleRes = titleRes;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(mTitleRes);
// set the Behind View
setBehindContentView(R.layout.menu_frame);
if (savedInstanceState == null) {
FragmentTransaction t = this.getSupportFragmentManager().beginTransaction();
mFrag = new SlideRightMenuFragment();
t.replace(R.id.menu_frame, mFrag);
t.commit();
} else {
mFrag = (ListFragment) this.getSupportFragmentManager().findFragmentById(R.id.menu_frame);
}
// customize the SlidingMenu
final SlidingMenu sm = getSlidingMenu();
sm.setMode(SlidingMenu.LEFT_RIGHT);
sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
sm.setShadowWidthRes(R.dimen.shadow_width);
sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);
sm.setShadowDrawable(R.drawable.shadow);
sm.setFadeDegree(0.35f);
sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
sm.setSecondaryMenu(R.layout.menu_frame_two);
sm.setSecondaryShadowDrawable(R.drawable.shadowright);
mLeftFrag = new SlideLeftMenuFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.menu_frame_two, mLeftFrag).commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// setSlidingActionBarEnabled(false);
}
Thanks in advance
I had forked a new branch to add this feature from jfeinstein's slidemenu.
https://github.com/buptcoder/SlidingMenu
in my branch, I added a new method for SlideMenu.
You can use
sm.setRightBehindOffset(200);
to adjust the right menu's width.
Any problems please let me know.
Here is my problem. I have an app where I am using ActionBar Sherlock with tabs, fragments with option menus. Every time I rotate the emulator, menus are added for all the fragments even those that are hidded/removed (I tried both).
This is the setting: One FragmentActivity, that has an ActionBar with
final ActionBar bar = getSupportActionBar();
bar.addTab(bar.newTab()
.setText("1")
.setTabListener(new MyTabListener(new FragmentList1())));
bar.addTab(bar.newTab()
.setText("2")
.setTabListener(new MyTabListener(new FragmentList2())));
bar.addTab(bar.newTab()
.setText("3")
.setTabListener(new MyTabListener(new FragmentList3())));
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayShowHomeEnabled(true);
bar.setDisplayShowTitleEnabled(true);
The tabs all use the same Listener:
private class MyTabListener implements ActionBar.TabListener {
private final FragmentListBase m_fragment;
public MyTabListener(FragmentListBase fragment) {
m_fragment = fragment;
}
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager();
FragmentTransaction transaction = fragmentMgr.beginTransaction();
transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG);
transaction.commit();
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager();
FragmentTransaction transaction = fragmentMgr.beginTransaction();
transaction.remove(m_fragment);
transaction.commit();
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
}
Each subclass of FragmentListBase has its own menu and therefore all 3 subclasses have :
setHasOptionsMenu(true);
and the appropriate
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Log.d(TAG, "OnCreateOptionsMenu");
inflater.inflate(R.menu.il_options_menu, menu);
}
When I run the app I can see that the onCreateOptionsMenu is being called multiple times, for all the different fragments.
I'm totally stumped.
I tried posting the most code as possible without being overwhelming, if you find that something is missing, please advise.
[Edit]
I added more logging, and it turns out that the fragment is being attached twice (or more) on rotation. One thing that I notice is that everything is being called multiple times except for the onCreate() method which is being called only once.
06.704:/WindowManager(72): Setting rotation to 0, animFlags=0
06.926:/ActivityManager(72): Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=35}
07.374:/FragmentList1(6880): onAttach
07.524:/FragmentList1(6880): onCreateView
07.564:/FragmentList1(6880): onAttach
07.564:/FragmentListBase(6880): onCreate
07.564:/FragmentList1(6880): OnCreateOptionsMenu
07.574:/FragmentList1(6880): OnCreateOptionsMenu
07.604:/FragmentList1(6880): onCreateView
[Edit 2]
Ok, I started tracing back into Android code and found this part here (that I edited to shorten this post).
/com_actionbarsherlock/src/android/support/v4/app/FragmentManager.java
public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (mActive != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null && !f.mHidden && f.mHasMenu) {
f.onCreateOptionsMenu(menu, inflater);
}
}
}
The problem is that mAdded does indeed have multiple instances of FragmentList1 in it, so the onCreateOptionsMenu() method is "correctly" being called 3 times, but for different instances of the the FragmentList1 class. What I don't understand is why that class is being added multiple times... But that is a hell of a good lead.
I seem to have found the problem(s). I say problem(s) because on top of the multitude of menus, there is now also an Exception.
1) the call to
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
which is after the calls to addTab() has a side effect of calling onTabSelected(). My TabListener would then add a FragmentList1 to the FragmentManager
2) rotating the device would destroy the Activity as expected, but would not destroy the Fragments. When the new Activity is created after rotation it would do two things :
create another set of Fragments that it would add to the FragmentManager. This is what was causing the multitude of Menus
call onTabSelected (via setNavigationMode()) which would perform the following code:
if (null != fragmentMgr.findFragmentByTag(m_fragment.LIST_TAG)) {
transaction.attach(m_fragment);
transaction.show(m_fragment);
}
else {
transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG);
}
Basically if the fragment is already in the FragmentManager there is no need to add it, just show it. But there lies the problem. It's not the same Fragment! It's the Fragment that was created by the earlier instance of the Activity. So it would try to attach and show this newly created Fragment which would cause an Exception
The Solution.
There were a few things to do in order to fix all of this.
1) I moved the setNavigationMode() above the addTab()s.
2) this is how I now create my tabs:
FragmentListBase fragment = (FragmentListBase)fragmentMgr.findFragmentByTag(FragmentList1.LIST_TAG_STATIC);
if (null == fragment) {
fragment = new FragmentList1();
}
bar.addTab(bar.newTab()
.setText("1")
.setTabListener(new MyTabListener(fragment)));
So upon Activity creation I have to check to see if the Fragments are already in the FragmentManager. If they are I use those instances, if not then I create new ones. This is done for all three tabs.
You may have noticed that there are two similar labels: m_fragment.LIST_TAG and FragmentList1.LIST_TAG_STATIC. Ah, this is lovely... ( <- sarcasm)
In ordrer to use my TagListener polymorphically I have declared the following non static variable in the base class:
public class FragmentListBase extends Fragment {
public String LIST_TAG = null;
}
It is assigned from inside the descendents and allows me to look in the FragmentManager for the different descendents of FragmentListBase .
But I also need to search for specific descendents BEFORE they are created (because I need to know if I must create them or not), so I also have to declare the following static variable.
public class FragmentList1 extends FragmentListBase {
public final static String LIST_TAG_STATIC = "TAG_LIST_1";
public FragmentList1() {
LIST_TAG = LIST_TAG_STATIC;
};
}
Suffice to say that I am disapointed that nobody came up with this simple and elegant solution ( <- more sarcasm)
Thanks a lot to Jake Wharton who took the time to look at this for me :)
public FragmentListBase() {
setRetainInstance(true);
setHasOptionsMenu(true);
}
This will save/restore the individual states of each of the fragments upon rotation.
Another simple change you might want to make is calling transaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG) in the tab selected callback and getting rid of the content in the unselected callback.
I had this very similar issues with "stackable" menus on rotation. I don't use tabs but I do use ViewPager with FragmentStatePagerAdapter so I can't really reuse my Fragments. After banging my head for 2 days I found very simple solution. Indeed the problem seems to be with onCreateOptionsMenu called multiple times. This little code snippet takes care (masks?) of all the problems:
/** to prevent multiple calls to inflate menu */
private boolean menuIsInflated;
#Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
if (!menuIsInflated) {
inflater.inflate(R.menu.job_details_fragment_menu, menu);
menuIsInflated = true;
}
}
What worked for me was moving the setHasMenuOptions(true) to the calling activity ie the activity in which the fragment was declared. I previously had it in the onCreate method of the fragment.
Here is the code snippet:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ForecastFragment forecastFragment = new ForecastFragment();
forecastFragment.setHasOptionsMenu(true);
fragmentTransaction.add(R.id.fragment, forecastFragment);
fragmentTransaction.commit();
}
Just a quite note on your polymorphic tag frustrations.
Declare your base class like so:
public abstract class ListFragmentBase {
protected abstract String getListTag();
}
Now declare your sub classes something like this:
public class FragmentList1 extends ListFragmentBase {
public static final String LIST_TAG = "TAG_LIST_1";
#Override
protected String getListTag() {
return LIST_TAG;
}
}
Now the polymorphic way to get the instance tag is like this:
ListFragmentBase frag = new FragmentList1();
frag.getListTag();
Get the tag statically like so:
FragmentList1.LIST_TAG;