I create sample app to show some data to user when user start specific activity and click on specific button the app start new fragment (only fragment added to activity for now ) my problem is when user click back the fragment and the activity removed and go to parent activity. using this code to do that
android.support.v4.app.FragmentManager fragmentManager = ((FragmentActivity) context).getSupportFragmentManager();
fragmentManager.popBackStack();
also I use the same code in another action when user click in specific button fragment and back correctly.
Can anyone help me to solve that
Add fragments to back stack when create them and override onBackPressed in your activity.
To add fragment
FragmentManager mFragmentManager = ((FragmentActivity) context).getSupportFragmentManager();
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.addToBackStack("tag of fragment");
ft.add("your container id", fragment);
ft.commit();
onBackPress
#Override
public void onBackPressed() {
if (mFragmentManager.getBackStackEntryCount() > 0)
mFragmentManager.popBackStackImmediate();
else super.onBackPressed();
}
To remove only fragment you have to add that fragment in addToBackStack(TAG) so that when you press back it popout only Fragment which are added to stack
override onBackPressed() and remove fragment. Back stack for fragments works differently as it stores transaction not fragments. So can not pop back a fragment but a transaction
This is work for me to handle backpressin a fragment in a fragment to remove backstack.
#Override
public void onResume() {
super.onResume();
if (getView() == null) {
return;
}
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyEvent.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
Toast.makeText(getActivity(), "Clicked Back", Toast.LENGTH_SHORT).show();
getActivity().finish();
return true;
}
return false;
}
});
}
Related
I am trying to implement back navigation through my fragments which are called in specific order: A->B->C and by going back with hardware button I would like them to remain order.
I am using fragmenttransaction.replace in order to switch fragment with no addToBackStack because it made my ActionBarMenu to misbehave.
Problem is that when I am on fragment C back button is going back directly to A. I found out that it is because click event is executed twice I am going to B and directly to A.
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setOnBackButtonPresed();
}
private void setOnBackButtonPresed() {
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
goBackToDays();
return false;
}
return false;
}
} );
}
private void goBackToDays() {
Log.e("fragmentC", "executing on back action")
}
logcat result:
03-15 08:37:17.353 21245-21245/com.test E/fragmentC: executing on back action
03-15 08:37:17.390 21245-21245/com.test E/fragmentC: executing on back action
Can anyone give me a hint how I can avoide twice button events?
I think "click event is executed twice" because you call both event ACTION_DOWN and ACTION_UP. Try this
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK && event.getAction()== KeyEvent.ACTION_DOWN)
{
goBackToDays();
return false;
}
return false;
}
2 things:
Even though you're replacing fragments, the old OnKeyListener stays there (and get's fired up later on). Try removing the old OnKeyListener on switching fragments.
Just as S-lightning pointed out, you need to bare in mind that there are two actions associated with a key event (KeyEvent.ACTION_DOWN and KeyEvent.ACTION_UP).
You can do two things
"it made my ActionBarMenu to misbehave." FIX this issue.
Override onBackPressed() on your Activity and find find out your current loaded fragment and replace with whichever you want.
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.your_view_container);
if (fragment instanceof FragmentThree) {
//replace with your fragment ie. FragmentTwo
} else if (fragment instanceof FragmentTwo) {
//replace with your fragment ie. FragmentOne
}
Return true if you want to "absorb" the key event and stop it from propagating up to the Activity, which has its own onBackPressed() implementation.
if( keyCode == KeyEvent.KEYCODE_BACK )
{
goBackToDays();
return true; // <-- should be TRUE, not FALSE
}
return false;
Override the onBackPressed() method in hosting activity.
do like this.
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}
.
.
#Override
public void onBackPressed(){
if (getSupportFragmentManager().getBackStackEntryCount() == 1){
finish();
}
else {
super.onBackPressed();
}
}
Here is the well explained answer. link
Suppose I'm in fragment A, then moving to B, then using Back button returns to A.
In the activity I'm performing the following override:
#Override
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
Fragment frag = fm.findFragmentByTag(Consts.A);
if (frag != null){
Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
fm.beginTransaction().remove(frag).commit();
fm.popBackStack();
}
}
and while showing B goes like this:
FragmentManager fm = getActivity().getSupportFragmentManager();
Fragment f = BFragment.newInstance(Consts.B);
fm.beginTransaction()
.replace(R.id.rl_content,
f,
Consts.B)
.addToBackStack(null)
.commit();
Now, which method (if any) will be executed in A, once we execute popBackStack()?
If none, how can we change A's data models or UI components (such as keyboard or a TextView) right after back press? is it component-dependent?
R.id.rl_content is the container.
Please consider 2 cases:
1. A is in R.id.rl and being replaced
2. A is not in R.id.rl and is not being replaced
If you're always going back from Fragment B to Fragment A or vice versa, i would recommend this solution inside the fragments themselves.
#Override
public void onResume() {
super.onResume();
Fragment f = AFragment.newInstance(Consts.A);
if(getView() == null){
return;
}
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.rl_content, f);
trans.addToBackStack(null);
trans.commit();
return true;
}
return false;
}
});
}
You can freely move from B to A and A to B using the same code. if you would like a more dynamic approach e.g. you would like to go from Fragment A to Fragment C, or Fragment B to Fragment C and then when you press back go back to the previous fragment on stack. I would aim to use Kyle Falconer's Solution here
Incase the link dies, I'll post the code here:
#Override
public void onBackPressed(){
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping backstack");
fm.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
}
I haven't tested the second solution, but use the first.
There are quiet a few ways by which you can change A's data models or UI components.
Case 1: when A is in R.id.rl_content and is being replaced by B. In this case you can simply update required models or UI in onCreateView of Fragment A.
Case 2: When A is not being replaced. In this case fragment A doesn't know when to update its view. In the onBackpressed() of your activity you can call Fragment A's updateView() method if Fragment B is being popped.
#Override
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
FragmentB fragmentB = (FragmentB)fm.findFragmentByTag(Consts.B);
if (fragmentB != null){
Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
fm.beginTransaction().remove(fragmentB).commit();
fm.popBackStack();
FragmentA fragmentA = (FragmentA)fm.findFragmentByTag(Consts.A);
if (fragmentA != null) {
fragmentA.updateView();
}
}
}
EDIT
I understand that you also want to handle scenarios like hiding keyboard etc.
For this you might want to pass backpress event to the individual fragments. Somewhat like this:
#Override
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
FragmentB fragmentB = (FragmentB)fm.findFragmentByTag(Consts.B);
if (fragmentB != null){
if (!fragmentB.onBackPress()) {
// This means fragment B doesn't want to consume backpress therefore remove it.
Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
fm.beginTransaction().remove(fragmentB).commit();
fm.popBackStack();
FragmentA fragmentA = (FragmentA)fm.findFragmentByTag(Consts.A);
if (fragmentA != null) {
fragmentA.updateView();
}
}
}
}
And in your Fragment B create a function onBackPress like this:
public boolean onBackPressed() {
// if keyboard is showing then hide it here and return true to consume the back press event or else return false to dismiss this fragment.
}
Hi I have an android app which a recycleview. When user click a button on a recycle viewholder. app will launch a webview (inside fragment). I want to handle when user click back button, webview will goback if there is a history, and if there is no history, the app will close the webview fragment and come back the recycleview.
I have successfull handle the back when there is a history (but webview can not come back to recycle view if there is no history
Here is my webview code
webView.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (webView.canGoBack()) {
webView.goBack();
}else {
getActivity().getSupportFragmentManager().popBackStack();
}
return true;
}
return false;
}
});
Anything wrong with these code? any help is much appreciate. Thanks
Edit i use these code inside recycleview adapter class to handle button click on ViewHolder class
url.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString("topStoryUrl", topStories.get(getLayoutPosition()).getUrl());
TopStoryWebView topStoryWebViewFragment = new TopStoryWebView();
topStoryWebViewFragment.setArguments(bundle);
if (ItemListActivity.mTwoPane) {
((FragmentActivity) context).getSupportFragmentManager()
.beginTransaction()
.add(R.id.item_detail_container, topStoryWebViewFragment)
.commit();
} else {
topStoryWebViewFragment.setArguments(bundle);
((FragmentActivity) context).getSupportFragmentManager()
.beginTransaction()
.add(R.id.frameLayout, topStoryWebViewFragment)
.commit();
}
}
});
addToBackStack will add your fragment to stack, and you can access this stack later
((FragmentActivity) context).getSupportFragmentManager()
.beginTransaction()
.add(R.id.item_detail_container, topStoryWebViewFragment)
// Add this transaction to the back stack (name is an optional name for this back stack state, or null).
.addToBackStack(null)
.commit();
I have try to close the current fragment by using Imagebutton.
I am in Fragment-A and it will turn to the Fragment-B when I click the button.
And when I click the button at Fragment-B , it will turn to the Fragment-C and close the Fragment-B.
If I click the back button at Fragment-C , it will back to the Fragment-A.
The code I have try is like the following
camera_album = (ImageButton) view.findViewById(R.id.camera_album);
camera_album.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
closefragment();
Fragment fragment = FileBrowserFragment.newInstance(null, null, null) ;
MainActivity.addFragment(LocalFileBrowserFragment.this, fragment) ;
}
});
private void closefragment() {
getActivity().getFragmentManager().beginTransaction().remove(this).commit();
}
When I click the back button at fragment-B , it turn to the Fragment-C.
But when I click the back button on Fragment-C , it doesn't back to the Fragment-A.
It back to the empty background. If I want to back to Fragment-A , I have to click the back button once again.
SO , it seem doesn't close the current fragment complete.
How to finish the current fragment like the back button of Android ?
I changed the code from
getActivity().getFragmentManager().beginTransaction().remove(this).commit();
to
getActivity().getFragmentManager().popBackStack();
And it pops out the top fragment
For those who need to figure out simple way
Try getActivity().onBackPressed();
From Fragment A, to go to B, replace A with B and use addToBackstack() before commit().
Now From Fragment B, to go to C, first use popBackStackImmediate(), this will bring back A. Now replace A with C, just like the first transaction.
Try this:
public void removeFragment(Fragment fragment){
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
}
This is a Kotlin way of doing this, I have created button in fragment layout and then set onClickListner in onViewCreated.
according to #Viswanath-Lekshmanan comment
override fun onViewCreated(view: View?, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
btn_FragSP_back.setOnClickListener {
activity?.onBackPressed()
}
}
Try this:
ft.addToBackStack(null); // ft is FragmentTransaction
So, when you press back-key, the current activity (which holds multiple fragments) will load previous fragment rather than finishing itself.
You can try this logic because it is worked for me.
frag_profile profile_fragment = new frag_profile();
boolean flag = false;
#SuppressLint("ResourceType")
public void profile_Frag(){
if (flag == false) {
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
manager.getBackStackEntryCount();
transaction.setCustomAnimations(R.anim.transition_anim0, R.anim.transition_anim1);
transaction.replace(R.id.parentPanel, profile_fragment, "FirstFragment");
transaction.commit();
flag = true;
}
}
#Override
public void onBackPressed() {
if (flag == true) {
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
manager.getBackStackEntryCount();
transaction.remove(profile_fragment);
transaction.commit();
flag = false;
}
else super.onBackPressed();
}
Button ok= view.findViewById(R.id.btSettingOK);
Fragment me=this;
ok.setOnClickListener( new View.OnClickListener(){
public void onClick(View v){
getActivity().getFragmentManager().beginTransaction().remove(me).commit();
}
});
If you need to handle the action more specifically with the back button you can use the following method:
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if( keyCode == KeyEvent.KEYCODE_BACK )
{
onCloseFragment();
return true;
} else {
return false;
}
}
});
In your Fragments onCreateView(...) you can remove a view by calling container.removeView(view);.
So if you want to remove the fragment, then view should be the return value of onCreateView,
for example
public View onCreateView(...){
final View view = inflater.inflate(R.layout.your_fragments_layout,container,false);
//Do something
finishButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
container.removeView(view);
}
});
return view;
}
For remove current fragment B and navigate to stack back (other fragment A):
private fun navigateToBackStack() {
requireActivity().supportFragmentManager
.beginTransaction()
.remove(this)
.commit()
requireActivity().supportFragmentManager.popBackStack()
}
GL
Try this one
getActivity().finish();
I have only one activity and multiple fragments in my application.
Two main fragment A(left) and B(right).
Fragment A1 called from A
B1 called from B
B2 called from B1
All fragments have individual back buttons.
So when I press back button of fragment A1, it should go back to A, similarly when Back button from B2 is pressed, B1 appears and from B1 to B and so on.
How to implement this type of functionality?
public void onBackPressed()
{
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.popBackStack();
}
I have implemented the similar Scenario just now.
Activity 'A' -> Calls a Fragment 'A1' and clicking on the menu item, it calls the Fragment 'A2' and if the user presses back button from 'A2', this goes back to 'A1' and if the user presses back from 'A1' after that, it finishes the Activity 'A' and goes back.
See the Following Code:
Activity 'A' - OnCreate() Method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activityA);
if (savedInstanceState == null) {
Fragment fragInstance;
//Calling the Fragment newInstance Static method
fragInstance = FragmentA1.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, fragInstance)
.commit();
}
}
Fragment : 'A1'
I am replacing the existing fragment with the new Fragment when the menu item click action happens:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_edit_columns) {
//Open the Fragment A2 and add this to backstack
Fragment fragment = FragmentA2.newInstance();
this.getFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null)
.commit();
return true;
}
return super.onOptionsItemSelected(item);
}
Activity 'A' - onBackPressed() Method:
Since all the fragments have one parent Activity (which is 'A'), the onBackPressed() method lets you to pop fragments if any are there or just return to previous Activity.
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else {
getFragmentManager().popBackStack();
}
}
If you are looking for Embedding Fragments inside Fragments, please refer the link: http://developer.android.com/about/versions/android-4.2.html#NestedFragments
#trueblue's answer got me going with one minor but annoying issue. When there is only one fragment on the backstack and you press back button, that frame is removed and the app remains active with a blank screen. User needed to press back button one more time to exit the app. I modified the original code to the following in order to handle this situation
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else if(getFragmentManager().getBackStackEntryCount() == 1) {
moveTaskToBack(false);
}
else {
getFragmentManager().popBackStack();
}
}
When there is only 1 fragment in the backstack, we are basically telling android to move the whole app to back.
Update (and probably a better answer)
So after doing some more reading around this, I found out that you can add fragment manager transactions to back stack and then android handles back presses automatically and in a desired way. The below code snippet shows how to do that
Fragment fragment; //Create and instance of your fragment class here
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment).addToBackStack(null);
fragmentTransaction.commit();
fragmentTransaction.addToBackStack(null);
The last line shows how you add a transaction to back stack. This solves back press issue for fragments in most situations except for one. If you go on pressing back button, then eventually you will reach a point when there is only one fragment in the back stack. At this point, you will want to do one of the two things
Remove the activity housing the fragment from the back stack of the task in which activity is running. This is because you do not want to end up with a blank activity
If the activity is the only activity in the back stack of the task, then push the task in background.
In my case, it was the later, so I modified the overridden onBackPressed method from my previous answer to look like below
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 1) {
moveTaskToBack(false);
}
else {
super.onBackPressed();
}
}
This code is simpler because it has less logic and it relies on framework than on our custom code. Unfortunately I did not manage to implement code for first situation as I did not need to.
You have to implement your own backstack implementation as explained here.
You can call the popFragments() whenever you click the back button in a fragment and call pushFragments() whenever you navigate from one Fragment to other.
Just Do
getActivity().getFragmentManager().popBackStack();
Try this, Its Work for me.
public void onBackPressed() {
if (mainLayout.isMenuShown()) {
mainLayout.toggleMenu();
} else {
FragmentManager fm = getSupportFragmentManager();
Log.print("back stack entry", fm.getBackStackEntryCount() + "");
if (fm.getBackStackEntryCount() > 1) {
fm.popBackStack();
// super.onBackPressed();
// return;
} else {
if (doubleBackToExitPressedOnce) {
fm.popBackStack();
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Press one more time to exit",
Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce = false;
}
}, 3000);
}
}
}
Back button will traverse in the order, in which Fragments were added to backstack. This is provided as a navigation function by default. Now if you want to go to specific Fragment, you can show it from backstack.
You can handle it by adding tag in the backStack. Check my answer here :
https://stackoverflow.com/a/19477957/1572408
hope it helps
#Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
// replace your fragment here
return true;
}
return false;
}
});
}
// Happy Coding
If you press back image you have to create method first like this
private void Backpresses() {
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.contant_main, new Home()).commit();
}
then you have to call like this when you press back image..
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Backpresses();
}
});
It work fine for me.