I have an implementation of fragment as explained here. I have expanded the BaseFragment class to handle navigation icon onClickListener. The code for that looks like this,
private void onBackButtonPressed(int tag)
{
switch (tag)
{
case Global.DISPLAY_STORE:
{
setTitle("Loyalty Cards",Color.BLACK);
setToolBar(getResources().getColor(R.color.white), Global.ADD_CARD,R.drawable.add_round_btn);
break;
}
case Global.ADD_CARD:
{
setTitle("Wallet", Color.WHITE);
setToolBar(getResources().getColor(R.color.colorPrimary), Global.DISPLAY_STORE,R.drawable.icon_shopping);
getFragmentManager().popBackStack();
break;
}
case Global.CARD_DETAILS:
{
setTitle("Loyalty Cards",Color.BLACK);
setToolBar(getResources().getColor(R.color.white), Global.ADD_CARD,R.drawable.add_round_btn);
getFragmentManager().popBackStack();
break;
}
}
}
I am using this block of code to change the ToolBar icons and colors when back button is pressed.This code resides in BaseFragment.
Here is how I implement the code to handle back press,
I have my fragment extending the BaseFragment
public class CardDetailsFragment extends BaseFragment
Now inside the BaseFragment onCreate I have this code,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainActivity = (MainActivity) getActivity();
ImageButton righBarButton = (ImageButton) mainActivity.getToolbar().findViewById(R.id.btn_ToolBarRightBtn);
righBarButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onRighBarButtonClicked(Integer.parseInt(v.getTag().toString()));
}
});
mainActivity.getToolbar().setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackButtonPressed(BACK_BUTTON_TAG);
}
});
getFragmentManager().addOnBackStackChangedListener(this);
shouldDisplayHomeUp();
}
By this every fragments back button press and the right bar button item click in toolbar are handled.
Consider the scenario I have three fragments A , B and C.
From Fragment A I open Fragment B and Then Fragment C. Now From fragment C I click the back button to reach fragment B. The back button event is handled by above code and it works fine.
Now from Fragment B, I click the back button to reach Fragment A. But when I click back button I am getting exception
java.lang.IllegalStateException: Fragment CardDetailsFragment{d8b35b5} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:636)
here the CardDetailsFragment is the FragmentC, but I am clicking the
back button from Fragment B
The first time the user presses the back button, Fragment C is no longer necessary and thus it is detached from the activity. The problem lies in the fact that even though Fragment C is detached, it is still registered as a listener for back stack change events.
The second time the user presses the back button, your onBackStackPressed() method is called in Fragment C. When getResources() is executed, there is no activity attachment so getResources() fails.
Something like this in your fragment should fix it:
#Override
public void onDestroy() {
getFragmentManager().removeOnBackStackChangedListener(this);
super.onDestroy();
}
Related
I have an Activity with a navigation drawer. I have used fragments for this and when I'm moving to different menus in the drawer I add them to the backstack with addToBackStack() method. Basically, Activity starts with Fragment A then if I go to Fragment B or Fragment C I can press back and go back to Fragment A.
But now I have a notification which should open the Activity with Fragment B open and when I press back it should take me back to Fragment A. Is there a way to handle this like in startActivities.
Here is the code for the Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
getSupportFragmentManager().beginTransaction().replace(R.id.layoutContent, new FragmentA()).commit();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonA:
getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
break;
case R.id.buttonB:
transactFragment(new FragmentB());
break;
...
}
}
private void transactFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.layoutContent, fragment).addToBackStack(fragment.getClass().getName()).commit();
}
Why not just define the fragments with tags and call them as you need to?
Here's a sample of how I work with my fragments:
Start your fragments in the Activity's onCreate() method, like so:
/** Load screen1 */
Instructions screen1 = new Instructions();
screen1.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().
add(com.example.tool.R.id.
instructions_container, screen1, "screen1").
addToBackStack(null).
commit();
Then you can grab them and show/hide them as much as you like:
/** screen 1 */
public void showInstructions()
{
// show screen 1
getFragmentManager().findFragmentByTag("screen1").
getView().setVisibility(View.VISIBLE);
}
In my project, we use a global enum to check which fragment is currently up, so we don't have to rely on the stack, which has more than once proven itself to be unreliable.
onBack calls a switch, which checks this enum and acts accordingly. For your use-case, you can just track when this notification has been called, so it will go from Fragment B, to Fragment A.
In my Application, i'm transitioning to a DetailsFragment when the user clicks on a list item. and there are TWO options to be back at the main Fragment (the List Fragment).
Press the back button. (no problem here, because I handle this in onBackPressed() in MainActivity)
Press the Toolbar back arrow (Here is my problem).
When the user presses the toolbar back arrow, I call the following
getActivity().getSupportFragmentManager().popBackStack();
how can I intercept this event in MainActivity?
(There are some manipulations I am doing in MainActivity when the List Fragment is visible to the user.
Just put this code
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
I have activity,which have 5 fragments in ViewPager and in toolbar I have made one button and I want to perform click operation on button differently for selected tab fragment.I have have:
usertextView_done= (TextView) getActivity().findViewById(R.id.actionbar_tv_icon_done);
But it creates the single instance for all fragment or all tab.
If this toast appears from every fragment when clicked, then you can do following.
-- in your activity
public void calledFromFragment(String tag)
{
if(tag.equalsIgnorecase("fragment-1-tag")
{
// TODO : for fragment 1
}
else if(tag.equalsIgnorecase("fragment-2-tag"){
// TODO : for fragment 2
}
.
.
and so on
}
---- in your fragment
textView_done.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) {
((Activity)context).calledFromFragment("fragment-1-tag");
});
do this for each fragment and let me know.
I am assuming that the button and toolbar exist in the parent activity.
So here is a simple way to do it:
The idea is to create a static integer in the activity and update it in the fragments with its respective number, this way you can keep track of which fragment is currently visible.
Example:
//in activity class
public static int CURRENT_FRAGMENT = 1; //set this to your first fragment that is being viewed
Now, you have to update the integer from each fragment that is resumed.
//in each fragment add this code
#Override
public void onResume(){
ParentActivity.CURRENT_FRAGMENT = 2; //in case you're in fragment 2
}
And finally in your activity, add this:
//in actvity class
button .setOnClickListener(new OnClickListener() {
if(CURRENT_FRAGMENT ==1 ){
//do things for fragment 1
} else if (CURRENT_FRAGMMENT ==2 ){
//do things for fragment 2
}
etc...
}
I'm building a pretty simple app. At it's core are 2 screens:
1) list-screen: a list of items
2) detail-screen: a detailed view of an item
I used one Activity (which extends AppCompatActivity) with an Action-Bar, a Navigation-Drawer and a main-content part (a FrameLayout).
I used 2 different fragments for the 2 screens:
When opening the app I inflate the list-fragment into the main-content part.
When an item in the list is clicked I inflate the detail-fragment into the main-content part and it all works well.
On the detail-screen I want the Action-Bar to display an up-button that goes back to the list-screen.
Considering the fact that I am using fragments, rather than separate activites, how can I achieve that?
You can enable the up button each time the Detail Fragment loads, and disable it whenever any of the other Fragments load.
First define these methods in your Activity, which you can call from the Fragments in order to show/hide the up button:
public void showUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
public void hideUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
You will probably want to just enable the up button in on Resume() of the detail Fragment (MainActivity is just an example, change to the name of your Activity) .....
#Override
public void onResume() {
super.onResume();
MainActivity activity = (MainActivity)getActivity();
if (activity != null) {
activity.showUpButton();
}
}
Then in the other Fragments:
#Override
public void onResume() {
super.onResume();
MainActivity activity = (MainActivity)getActivity();
if (activity != null) {
activity.hideUpButton();
}
}
The next thing is to make the up button actually go back. First ensure that you're adding the Fragment with the up button to the back stack, and then add this to that Fragment.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
((MainActivity)getActivity()).onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Then in the Activity, override onBackPressed() and pop from the back stack if the FragmentManager has any entries:
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 1) {
fragmentManager.popBackStackImmediate();
} else {
super.onBackPressed();
}
}
I have 2 viewpagers: swipeViewPager and nonSwipeViewPager in MainActivity
In swipeableViewPager, I have fragments: homeFragmentFirst and homeFragmentSecond
In nonSwipeableViewPager, I have fragments: AppleFragment , MangoFragment, GrapesFragment and CherryFragment
First I want swipeableViewPager to be viewed in MainActivity. So I have set setVisibility of nonSwipeableViewPager as GONE.
After selecting an item from the drawer, I setVisibility of swipeableViewPager as GONE and of nonSwipeableViewPager as Visible.
By this, I open AppleFragment
Situation:
Suppose, I selected Grapes from drawer, GrapesFragment opens.
I do my some stuff there. And then when I press back button, the app closes i.e. the MainActivity closes.
What I want is when back button is pressed, I want to again bring back homeFragmentFirst from swipeableViewPager.
I have tried below code in GrapesFragment
#Override
public void onBackPressed() {
super.onBackPressed();
// Do some operations here
MainActivity mainActivity = new MainActivity();
mainActivity.swipeHomeViewPager.setVisibility(View.VISIBLE);
mainActivity.nonSwipeHomeViewPager.setVisibility(View.GONE);
}
I solved it myself. I added following in MainActivity:
#Override
public void onBackPressed() {
if (nonSwipeHomeViewPager.getVisibility() == View.VISIBLE) {
nonSwipeHomeViewPager.setVisibility(View.GONE);
swipeHomeViewPager.setVisibility(View.VISIBLE);
swipeHomeViewPager.setCurrentItem(0,true);
} else if (swipeHomeViewPager.getVisibility() == View.VISIBLE){
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
You can implement backstack for the fragments (while adding / replacing fragment) add it to backstack. the back press will automatically take you to previous fragment.
otherwise you need to override OnBackPressed method to handle with your own logic.