I am new to android development, and situation here is simple:
There's a fragment that's being called by MainActivity, where the fragment is being replaced with the frameLayout. And now, what I want here is to open up a fragment that contains the detail information from the following onItemClick.
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fragment w = new statDetail();
getActivity().getSupportFragmentManager().beginTransaction().add(w,"detail").addToBackStack(null).commit();
}
//From MainActivity
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
if (position==0) {
fragmentManager.beginTransaction()
.replace(R.id.container, ExpenseFragment.newInstance(position + 1))
.commit();
}
else {
fragmentManager.beginTransaction()
.replace(R.id.container, StatFragment.newInstance(position + 1))
.commit();
}
}
Above is the code that I'm trying to accomplish to open up another fragment, but it simply stops the whole program. OnItemClick works fine here, and I have already tried just creating a new activity, but because this uses the pre-made navigationdrawer that android studio provides, pressing back button would come to the first page when I wanted to show up the 2nd page, which I have no idea how to modify. I feel like fragment would be easier to use here, but I really don't know.
In the future, I want to implement the back navigation and would appreciate if anyone can help me solve the problem! Thanks!
Related
I think the issue is probably simple, but being pretty new to Java and to Android dev, I'm not really sure. What I'm trying to do is close a fragment that's been toggled on using a ToggleButton, but I can't find a way to then hide or close it. Any help would be appreciated.
Below is the code for the OnCheckChanged() listener.
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
MyFragment frag = new MyFragment();
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.main_activity, frag, "Fragment1");
if(isChecked)
{
transaction.show(frag);
}
else
{
transaction.hide(frag);
//transaction.remove(frag);
}
transaction.commit();
}
Every time you hit onCheckedChanged, you are adding a NEW fragment and then either hiding or showing it. All other fragments you have previously created are still attached laid one on top of the other.
You should get the current fragment that is attached to the layout if there is one. If there isn't one already, create it and add it to the container.
For example, this should point you in the right direction.
private void HideFragment(){
// Get the fragment that is attached to the layout
Fragment frag=getFragmentManager().findFragmentById(R.id.centerFrame);
if (frag==null){
// The fragment does not exist yet so create one and add it
frag=new MyFragment();
getFragmentManager()
.beginTransaction()
.replace(R.id.centerFrame,frag)
.commit();
}
//Hide the fragment
getFragmentManager()
.beginTransaction()
.hide(frag)
.commit();
}
Update:
It is a little ambiguous what you are actually trying to achieve. You keep saying "close" the fragment but your code suggests you just want to toggle its visibility on and off.
If you actually want to completely remove the fragment then you should use the Remove method of the FragmentTransaction.
I was looking through an example in
https://github.com/lemycanh/DrawerOnTopActionBar/blob/master/src/com/example/drawerontopactionbar/MainActivity.java
I managed to incorporate that successfully in my app but I could not quite understand the onItemClick function.
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mDrawerLayout.closeDrawer(mDrawerList);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ftx = fragmentManager.beginTransaction();
if(position == 0) {
ftx.replace(R.id.main_content, new FragmentFirst());
} else if(position == 1) {
ftx.replace(R.id.main_content, new FragmentSecond());
}
ftx.commit();
}
From the raw code it'll create a new Fragment every time there is an item click in the drawer. I understand that if this was called the first time you'll need an instance of it. But wouldn't it make sense to save the fragment somewhere in the activity? Then switch to it upon item click?
Something like
Fragment fOne;
Fragment fTwo;
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mDrawerLayout.closeDrawer(mDrawerList);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ftx = fragmentManager.beginTransaction();
if(position == 0) {
if( fOne==null){
fOne = new FragmentFirst();
}
ftx.replace(R.id.main_content, fOne);
} else if(position == 1) {
if( fTwo==null){
fTwo = new FragmentSecond();
}
ftx.replace(R.id.main_content, fTwo);
}
ftx.commit();
}
it mostly depends on the situation or necessity. From a memory usage perspective, it's good to save the instances for later usage.
After all fragments are all about offering a smoother ux utilising pre-readiness.
Fragment are hard coded in xml can not replaced.... but when you call replace(..) then what happen ?? Ok just consider you have 2 fragment A,B . In primary stage A initialize in MainActivity... now you are going to call B using replace(....). that means A will go onPause(),onStop() state of lifecycle and B will be initialized and paced in MainActivity... If you want to reuse A then you need to call again A using replace (..). and then A will reinitialize and B goes to onPause(),onStop(). But there is another way you can do this
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
hide(A);
show(B);
ft.commit();
if you use above code block then A still be in the running state of its lifecycle but not visible (just detached from UI ).. so it would be better if you need those fragment again use hide()or show() method because it is less expensive operation then reinitialize the fragment.
or
it's better to use the attach() and detach() transactions instead of show() and hide() if you wish to retain the fragment instance but remove it from the UI
and still there is a way to save fragment instance .
In the fragment, save instance state by override onSaveInstanceState and restore on onActivityCreated:
And important point, in the activity, you have to save fragment's instance on onSaveInstanceState and restore on onCreate.
for more info Saving Fragment States
I have a class derived from FragmentPagerAdapter and user swiping works fine (4 tabs), but I would like to show a page/tab/fragment from code. For example, when user taps a textview, I would like to switch to a specific fragment.
I've tried the following without success:
public void onClockClick(View v)
{
FragmentManager fm = getFragmentManager();
if (fm != null) {
fm.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.show(mainFragment)
.commit();
}
}
Am I barking up the wrong tree?
Well, if you know the position of the fragment you want to show then you can simply do: pager.setCurrentItem(pos)
This will automatically animate to the fragment at the desired position. Simple.
If you want to add another fragment to the existing FragmentPagerAdapter, then you'll need to first add the fragment's instance to that adapter and then call notifiyDataSetChanged on it.
I ran into this problem where my ListFragment is empty after having popBackStackImmediate
I have a main activity which extends FragmentActivity and I have a Navigation drawer attached with my main activity. The first navigation calls a Fragment which has a custom adapter which implements FragmentStatePagerAdapter showing three tabs. each of the tabs are ListFragments them selves. when i click on any row of the list it goes to another fragment, till this everything works fine, now I want to implement the backstack so that when user presses back button from the detail page they can come back to the FragmentList page again. when I press back button it takes me back to the FragmentList page that is fine but the page is empty, for some reason the list does not shows up. I've checked the log and found the data is there and it goes till last bit of the code with out any error but the page is empty, can any one help me with that?
When I select any item from the List of any tab I add the transaction into the backstack and move on to detail fragment:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Fragment fragment = new LiveMatchDetailMainFragment();
android.support.v4.app.FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack("LiveFragment");
fragmentTransaction.commit();
}
In my main activity I have this:
#Override
public void onBackPressed(){
Log.d("stack","back pressed");
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0)
{
//fm.popBackStack();
boolean done = fm.popBackStackImmediate();
Log.i("stack", String.valueOf(done));
} else
{
Log.i("stack", "nothing on backstack, calling super");
super.onBackPressed();
}
}
which takes me to exact tab where I clicked the item from the list, but only problem is the fragment is empty.
In the FragmentList page I have:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
LiveMatchList = new ArrayList<HashMap<String, Object>>();
//Log.d("LiveMatchList setUserVisibleHint","setUserVisibleHint");
new LoadUpcomingMatchList().execute();
}
}
which creates a list using custom adapter, and it works fine for the first time, when I pop the backstack this method gets call and loads all the data comes down till onPostExecute but the list is never showed, sorry guys I am not new to stackoverflow but this is my second question post so I might not follow the convention correctly...
Apparently with some help I fixed the issue...
I added the fragment transactions within the FragmentActivity and used my onListItemClick() callback method to call a FragmentActivity method, which should run the transaction.
In EventFragment replaced:
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
viewpager.setAdapter(new MyAdapter(fragmentManager));
with:
viewpager.setAdapter(new MyAdapter(getChildFragmentManager()));
it was the getChildFragmentManager() that solved my problem mainly.
I'm working with Fragments for the first time and running into a weird behavior. I have an activity with a Fragment covering the entire view. It contains a ListView. When I tap on an item in the ListView, I want to show a details fragment which contains information about that item.
This is how I'm presenting the new fragment view:
FragmentManager fragmentManager = this.getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
DetailsFragment fragment = new DetailsFragment();
transaction.add(R.id.viewRoot, fragment).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack("Details").commit();
R.id.viewRoot is the id of the root layout in the first Fragment's layout xml, so the new Fragment fills the entire screen.
My Problem: When the new fragment is visible and covering the entire screen, any clicks or taps which land on the background (i.e. not hitting a button or textfield on the new fragment) appear to be going through the view and landing on the first fragment. So, I tap on a ListView item, which causes the DetailsFragment to get added to the screen. Then when I tap on the background of the DetailsFragment, that tap falls through and lands on the ListView causing another DetailsFragment to be added for whatever item I happened to hit behind the scenes.
Am I adding my new fragment incorrectly? Why are my clicks/taps falling through?
EDIT for caiuspb:
public class BaseFragment extends Fragment {
public void pushNewFragment(Fragment fragment, String description) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.remove(this);
transaction.add(R.id.viewRoot, fragment).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(description).commit();
}
}
All of my fragments extend my BaseFragment class.
I experimented with the suggestions I received, but none of them worked. In the end, my solution was to add an OnTouchListener to the root view of my subfragments which discarded all touches.
View view = inflater.inflate(R.layout.mylayout, container, false);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
There is another post here on StackOverflow about that strange Fragment behaviour. Try to remove your Fragment before you add it because the replace Method seems to be buggy
edit:
Move the Fragment logic into your Activity because your activity contains your Fragments. Today I tried to use the FragmentActivity and Fragments from the Android Support Library. Now I can use the replace method withouht any bugs. This is my approach and this works:
public class DashActivity extends FragmentActivity{
...
private void changeRightFragment(Fragment fragment) {
Log.i(TAG, "Loading Fragment into container: " + fragment.getClass().toString());
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.rightfrag, fragment);
// Add a transition effect
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// Add the Fragment to the BackStack so it can be restored by
// pressing the Back button
ft.addToBackStack(null);
ft.commit();
}
...
public void setUserFragment(User user) {
UserFragment fragment = new UserFragment(user);
changeRightFragment(fragment);
}
So if you want to invoke a changeFragment method from inside your Fragment use a callback mechanism to the Activity