How to define a Backpress Action in
class thats extends Fragment implements ActionBar.TabListener, how to define a backpressed action?
Fragments don't have an onBackPressed() callback like Activities do. You can try making your Activity maintain (or obtain) a reference to the fragment and have it call the fragment from within onBackPressed().
Fragment code:
public boolean onBackPressed() {
// your special behavior here
// return true if you have consumed the back press
}
Activity code:
public void onBackPressed() {
MyFragment fragment = getFragmentManager().findFragmentById(/* some unique id*/);
// could alternatively use findFragmentByTag() using a unique String
if (fragment != null) {
if (fragment.onBackPressed()) return;
}
// back press not consumed; allow usual behavior
super.onBackPressed();
}
Related
In my application I use ViewPager for show two fragments into activity.
In one of this fragments I use NavigationDrawer, I want when click on onBackPress close this NavigationDrawer.
I write below code for open this Drawer :
reviewSerialFrag_DrawerLayout.openDrawer(Gravity.END);
I want when click on onBackPress close this drawer with below code :
reviewSerialFrag_DrawerLayout.closeDrawer(Gravity.END);
How can I it? Please help me
/Try to use below code snipet/
#Override
public void onBackPressed()
{
int count = getFragmentManager().getBackStackEntryCount();
// count --> Is your current fragment
if (count == 0)
{
if(reviewSerialFrag_DrawerLayout.isDrawerVisible(GravityCompat.END))
{
reviewSerialFrag_DrawerLayout.closeDrawer(GravityCompat.END);
}
}
}
In your Activity onBackPressed() write below code
#Overrdie
public void onBackPressed(){
Fragment currentFragment = getActivity().getFragmentManager().findFragmentById(R.id.fragment_container);
if(currentFragment instanceof YourDrawerFragment && reviewSerialFrag_DrawerLayout.isDrawerVisible(GravityCompat.END))
{
reviewSerialFrag_DrawerLayout.closeDrawer(GravityCompat.END);
return;
}
super.onBackPresses():
}
You can do this to close your drawer on back press
#Overrdie
public void onBackPressed(){
super.onBackPresses():
if(reviewSerialFrag_DrawerLayout.isDrawerVisible(GravityCompat.END)){
reviewSerialFrag_DrawerLayout.closeDrawer(GravityCompat.END);
}
}
In general, I find it easiest to use the Observer pattern and delegate the back pressed event down to the fragment. This will allow you to keep the activity and fragment concerns separated.
interface OnBackPressedListener {
public void onBackPressed();
}
Then, in your fragment, implement this OnBackPressedListener
class MyFragment extends Fragment implements OnBackPressedListener{
public void onBackPressed(){...}
}
And finally, in your activity, you can do the following:
class MyActivity extends Activity {
#Override
public void onBackPressed(){
// Grab all the fragments that are 'observing' the back press event
Fragment currentFragment =
getFragmentManager().findFragmentById(R.id.fragment_container);
if(currentFragment != null && currentFragment instanceof OnBackPressedListener) {
// delegate this back press event down to the fragment
OnBackPressedListener backFragment = (OnBackPressedListener) currentFragment;
backFragment.onBackPressed();
}
super.onBackPressed():
}
In Jon's solution, the super.onBackPressed will always be called. This may not necessarily be the case.
To do this, it is enough that the implementation returns a boolean and act according to the result.
My solution almost identical here
public interface IOnBackPressed {
/**
* Si vous retouné true le back press ne sera pas pris en compte, sinon l'activité agira naturellement
* #return true si votre traitement est prioritaire sinon false
*/
boolean onBackPressed();
}
see link for more details
I have a BaseActivity() that have many activities and a BaseFragment() that have many fragments. Each activity contains 2-3 fragments and I need to make a generic method to handle each onBackPressed from all fragments (all - means all app screens) but this method should be in Base Fragment() (every fragment extends it). I supose that I'll need a kind of listener to tie OnBackPressed() from BaseActivity() to genericMethod() from BaseFragment()
Thanks in advice.
#Choletski:
onBackPressed()
It will be called when the activity has detected the user's press of the back key. The default implementation simply finishes the current activity, but you can override this to do whatever you want.while overriding the default back button action as it is not suggested to change the android default user experience.
Override the onBackPressed() method and take the action inside this function.
#Override
public void onBackPressed() {
// Write your code here
super.onBackPressed();
}
How to implement onBackPressed() in Android Fragments?
The simplest solution rest to be a bit "hard programmed" in my case, like I mentioned in my question I need a method from BaseFragment() to handle all back press actions from all screens that means all fragments that extends this BaseFragment().
#Sharp Edge solution may be accepted but why to handle it in each SimpleActivity() that extends BaseActivity() if I can just add a single method in BaseFragment() and all simple activities that extends BaseActivity() will don't care about that.
#escape-llc solution is confused and not the expected one... I can handle it easier using EventBus or Otto and send from onResume() from each fragment to SimpleActivity(). So I'll receive the actual open fragment and I'll now what action to do when onBackPressed() is executed...
So, like I said, my solution is to use just a simple generic method in BaseFragment():
public void doBackBtnPressedAction(View view) {
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//logical part, in my case some server requests
return true;
}
}
return false;
}
});
}
This is how i handled it when i had a webview in fragment and wanted to handle onBackPressed for the webview?
public class Tab2 extends Fragment {
ProgressBar progress;
WebView x;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab_2,container,false);
x = (WebView)v.findViewById(R.id.webView);
x.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN)
{
WebView web = (WebView)v;
switch (keyCode)
{
case KeyEvent.KEYCODE_BACK:
if(web.canGoBack())
{
web.goBack();
return true;
}
break;
}
}
return false;
}
});
You have to make a custom Activity class for this.. and override its on onBackPressed() and add your logic in their. Then make sure wherever Fragments are used, you have to make the associated Activity sub class of this CustomActivity..
So whenever no matter on which Fragment user is, onBackPressed() of that Activity will be called and add super() to it.. so that it will call the base class's method and your code will run on each fragment.
example:
MyCustomActvity extends FragmentActivity{
#Override
public void onBackPressed(){
// your logic here
super.onBackPressed();
}
}
Now You know that Fragments must have at least 1 Base Activity, so just override that Activity's onBackPressed()
MyActivity extends MyCustomActivity{
// 3 fragments are called/replaced from this activity
// other code
#Override
public void onBackPressed(){
super.onBackPressed(); // it will invoke base class method and your code
}
}
Just extend MyCustomActivity for the ones which use Fragments.
Here is a great way to handle it in a general fashion. We use it now in all of our fragment-based apps.
First create an interface for fragments to implement. This represents whether they want to handle the back key at all. If not, don't implement the interface.
public interface IHandleBackPressed {
boolean handleBackPressed(Activity ax);
}
This is essentially a proxy for the activity's onBackPressed method.
Next, override the Activity.onBackPressed method with this boilerplate:
#Override
public void onBackPressed() {
final Fragment fx = getFragmentManager().findFragmentById(R.id.content);
if(fx != null) {
if(fx instanceof IHandleBackPressed) {
final IHandleBackPressed ihbp = (IHandleBackPressed)fx;
if(ihbp.handleBackPressed(this)) {
// we handled it
return;
}
}
}
// onBackPressed unhandled by us
super.onBackPressed();
}
This can be the same always. If you have multiple fragment areas, simply repeat the sequence for each one. If you have additional logic, integrate it before or after, but before you call super.onBackPressed to let the system take over (i.e. exit your activity).
Here is a sample of what a Fragment can do. This example uses a WebView and it wants to "use" the back key to manage the Back Stack of the WebView:
public class BrowseUrlFragment extends Fragment implements IHandleBackPressed {
WebView wv;
public boolean handleBackPressed(Activity ax) {
if(wv != null && wv.canGoBack()) {
wv.postDelayed(goback, 150);
return true;
}
return false;
}
}
Hello my Android application is using fragments. I am wondering if there is a way I can use the idea of the onBackPressed() method in my app with fragments. I have previous and next buttons, but as of now I am just creating new fragments and replacing, and none of the data gets saved. Is there a way to save my data/go back once I have gone forward?
The concept of Fragment is different of Activity.
One Activity could have a many Fragments, read that:
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities. You
can think of a fragment as a modular section of an activity, which has
its own lifecycle, receives its own input events, and which you can
add or remove while the activity is running (sort of like a "sub
activity" that you can reuse in different activities).
See more here: http://developer.android.com/guide/components/fragments.html
SOLUCTION
So if you wanna handle the onBackPressed behavior in you Fragment you could do that:
package com.example.stackoverflowsandbox;
import android.app.Activity;
import android.app.Fragment;
public class MainActivity extends Activity {
public class MyFragment extends Fragment {
public void onBackPressed() {
// your code here...
}
}
private MyFragment myFragment;
#Override
public void onBackPressed() {
// super.onBackPressed(); // comment to not back
this.myFragment.onBackPressed(); // the onBackPressed method of Fragment is a custom method
}
}
Sorry, do not know if I understand your question, but if the idea and have control of direct backbutton in its fragment, and from it to perform some task of data persistence, you can add your fragment to control stack FragmentManager, the as follows.
FragmentManager fm = getSupportFragmentManager();
MyFragment mMyFragment = new MyFragment();
fm.beginTransaction()
.add(mMyFragment, "mMyFragment")
.addToBackStack( null )
.commit();
In the fragment you need to implement the interface OnBackStackChangedListener
In Fragment:
public class MyFragment extends Fragment implements OnBackStackChangedListener {
#Override
public void onBackStackChanged() {
//your code here
}
}
If you just keep the values
public class MyFragment extends Fragment {
String valeu;
#Override
public void onCreate( final Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
if ( savedInstanceState != null ) {
this.valeu = savedInstanceState.getString( "key" );
}
}
#Override
public void onSaveInstanceState( final Bundle outState ) {
super.onSaveInstanceState( outState );
outState.putString( "key", "Your content" );
}
}
while moving to front fragment, save the state of previous fragment using onSaveInstanceState()
while moving back restore the state in onCreate() or onCreateView() in the previous fragment
My app works with a long form which I decided to divide in multiple Fragments in a ViewPager. When you press the "save" option, the validation process starts.
Basically the validation is that some EditTexts are not empty. I'm looping through all Fragments in the ViewPager check if all fields has valid values.
// Inside Fragment
public boolean areFieldsValid() {
return !mEditText.getText().toString().isEmpty()
}
public void showErrors() {
mEditText.setError("cannot be blank");
}
If a field inside a Fragment is not valid, then viewPager.setCurrentItem(position, true); and fragment.showErrors() are called to go to that Fragment and show the user the error.
The problem comes when onCreateView() hasn't been called on the Fragment that has the error.
This happens either because you haven't navigated to that Fragment yet (supposing the user's on fragment1, error is on fragment7 and the user pressed "save" while on fragment1) or because the user rotated the device and all views are destroyed on every Fragment.
This problem/issue is not only that mEditText would be null, but also that the Fragment saved its state, so it might not even been blank. In other words, the following code is not an option, because even if the pointer is null, it might not be empty.
// Inside Fragment
public boolean areFieldsValid() {
return mEditText != null && !mEditText.getText().toString()isEmpty();
}
At this point I'm wondering if my architecture is wrong. I decided to go with ViewPager cause the form is really long, and I've been passing data from Fragment to Activity through callbacks.
Given the above settings, how can I validate fields and show the user which field is the one with the error?
You can't just assume that UI components will be there anytime you want. That fragment might be gone, killed or worse, destroyed without saving it's instance state.
What I offer is to save data on database and check if everything is set on save button click event. This can be done using ContentProviders and SQLiteDatabase. As Virgil Said in here "Persist more, persist often."
I have implemented a similar thing, but my approach is to go fragment by fragment. Hope this helps.
I add an interface,
public interface AddActionInterface {
public void onAddButtonClicked();
}
I created a base fragment which implements this interface as,
public abstract class BaseFragment extends Fragment implements AddActionInterface {
#Override
public void onAddButtonClicked() {
if (isAdded() && isVisible()) {
executeAction();
}
}
protected abstract void executeAction();
}
Then we will call our Interface object like this in the activity. Create a List like below,
List<AddActionInterface> listeners = new ArrayList<AddActionInterface>();
and add your fragment to the list inside the view pager as,
listeners.add(fragment);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment, tag).commit();
Simply call the below in the onOptionsItemSelected method.
if (item.getItemId() == R.id.action_add) {
for (AddActionInterface listener : listeners) {
listener.onAddButtonClicked();
}
}
What the above method does is calls the onAddButtonClicked() method which is implemented in the BaseFragment.
Trick here is that every time the button in the action bar is clicked it will pass the control to the BaseFragment which checks if the current fragment is still attached then will call the executeAction() method of the respective fragment which being abstract every fragment can have their own implementation.
So say for FragmentA you will simply have to extend it from BaseFragment and override executeAction() method. You can write fragment specific implementations.
This process is called dependency inversion principle. See if you can put all these pieces in right place else let me know. :) Wow this is huge. :)
On the viewpager class:
public void validate() {
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
Fragment fragment = mSectionsPagerAdapter.getItem(i);
if(!(fragment instanceof Validetable)) {
return;
}
Validetable validetable = (Validetable) mSectionsPagerAdapter.getItem(i);
Fragment invalidFragment = validetable.validate();
if (invalidFragment == null) {
Toast.makeText(getActivity(), "valido", Toast.LENGTH_LONG).show();
}
else {
mViewPager.setCurrentItem(i);
break;
}
}
On each fragment you do:
public static boolean isValid = true;
#Override
public Fragment validate() {
if ( StringUtils.isBlank(ColetaLocal.getInstance().getNivel())) {
isValid = false;
return this;
}
isValid = true;
return null;
}
#Override
public void onResume () {
super.onResume();
treatErrorsShowing();
}
private void treatErrorsShowing() {
if (!isValid) {
showErrors();
}
else {
clearErrors();
}
}
I ended up validating each Fragment before moving to the next one.
Reason:
The initial idea was to validate on save, and if there was an Fragment with invalid data, move to that fragment and show the errors. But since there is no way to determine the state of Views inside a Fragment if it is not visible, you cannot validate input.
I have a fragment:
public class MyFragment extends Fragment{
...
#Override
public View onCreateView(...){...}
...
}
I instantiate it:
MyFragment myFragment = new MyFragment();
I use the above fragment to replace the current fragment:
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// replace fragment
fragmentTransaction.replace(R.id.fragment_placeholder, myFragment, "myTag");
// NOTE: I did not add to back stack
Now, myFragment is showing on the screen. NOTE: I did not add myFragment to back stack.
My two questions:
1. If now, I press mobile phone back button, which fragment's life cycle callback will be invoked??
2. How can I customize the back button click listener in MyFragment class? (please do not suggest me to do myFragment.getView().setOnclickListener, but do it in MyFragment class)
Question 1: See http://developer.android.com/reference/android/app/Fragment.html#Lifecycle:
"As a fragment is no longer being used, it goes through a reverse series of callbacks:
onPause() - fragment is no longer interacting with the user either because its activity is being paused or a fragment operation is
modifying it in the activity.
onStop() - fragment is no longer visible to the user either because its activity is being stopped or a fragment operation is modifying it
in the activity.
onDestroyView() - allows the fragment to clean up resources associated with its View.
onDestroy() - called to do final cleanup of the fragment's state.
onDetach() - called immediately prior to the fragment no longer being associated with its activity."
Question 2: If you must know that it was the back button specifically that is triggering the callbacks, You can capture the back button press in your Fragment's Activity and use your own method to handle it:
public class MyActivity extends Activity
{
//...
//Defined in Activity class, so override
#Override
public void onBackPressed()
{
super.onBackPressed();
myFragment.onBackPressed();
}
}
public class MyFragment extends Fragment
{
//Your created method
public void onBackPressed()
{
//Handle any cleanup you don't always want done in the normal lifecycle
}
}
androidx.activity 1.0.0-alpha01 is released and introduces ComponentActivity, a new base class of the existing FragmentActivity and AppCompatActivity.
You can now register an OnBackPressedCallback via addOnBackPressedCallback to receive onBackPressed() callbacks without needing to override the method in your activity.