Recover data on Fragment without call BDD - android

In my application, datas are loading while the splash screen is displayed. When it's done, the user can enter new data that are displayed in a fragment and insert in the database. Moreover, new data entered by the user allows to compute a variable that is also displayed in a custom view (in the fragment) and insert in the database. So, outside the splash screen, I'm using only db.insert().
I have several fragments and I want to recover my datas when I swith from one fragment to another without call the database with db.get()
Now, when I switch to another fragment (using navigation drawer) and go back to the previous one, my data are no more displayed...
Indeed, I want to limit call to the database when the app is running.
UPDATE
A little update to be more explicit :)
I have an Activity handling a Navigation Drawer. For the moment, I switch between 2 fragments (Frag1 & Frag2).
In the first fragment I have a custom view (named for example CustomView1) and some TextView (TextView1 & TextView2).
Through an item in action bar, TextView1 can be updated by the user. The value set for TextView1 is also insert in a database. Automatically, TextView2 is updated with the previous value of TextView1 as below :
/**
* Listener to see if TextView1 change
**/
TextView1.addTextChangedListener(new TextWatcher() {
String X;
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
X = charSequence.toString();
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void afterTextChanged(Editable editable) {
TextView2.setText(X);
}
});
When I switch to Frag2 and back to Frag1, TextView1 & TextView2 are empty and nothing is display.
I tried to use onSaveInstanceState like this :
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putFloat("mTextView1", mTextView1);
savedInstanceState.putFloat("mTextView2", mTextView2);
}
and in onCreateView :
if(savedInstanceState != null) {
// Restore last state
mTextView1 = savedInstanceState.getFloat("mTextView1");
mTextView2= savedInstanceState.getFloat("mTextView2");
}
My savedInstanceState is always returning null...
This is how I switch between fragment :
private void selectItem(int position) {
// update the main content by replacing fragments
switch(position){
case 0:
Fragment1 Frag1 = new Fragment1();
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
fragTransaction.replace(R.id.content_frame,Frag1,"TAG1");
fragTransaction.commit();
break;
case 1:
Fragment2 Frag2 = new Fragment2 ();
FragmentTransaction fragTransaction1 = getSupportFragmentManager().beginTransaction();
fragTransaction1.replace(R.id.content_frame,Frag2,"TAG2");
fragTransaction1.commit();
break;
}
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mSection[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
Solution
I am using SharedPreference during user action to store data I want (moreover, these data are insert in the database)
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putFloat("mKey1", value1);
editor.commit();
In onCreate, I get data I stored previously :
sharedPref = this.getActivity().getPreferences(Context.MODE_PRIVATE);
if(sharedPref!=null) {
variable1 = sharedPref.getFloat("mKey1",DEFAULT_VALUE);
}
TextView1.setText(String.valueOf(variable1));
As TextView1 is updated, TextView2 is update too with the listener taking the previous value of TextView1.

I solved the issue by using SharedPreference

Related

how to save android recycler view state (scroll position) in an activity with two fragments

I am new to Android and, as a first step, am building an app, for running in a handset, with an activity in which I put two fragments. The first fragment has a recycler view of items that are supposed to represent article titles. When I click on one, the second fragment opens and shows the title (in a text view) and the content (in another text view) of the article (for the moment, for simplicity, I put the article title as fake titles and I have a setting for which the content is not shown but it is shown the title in the content text view too).
I want to save the scroll position of the recycler view.
When I scroll down, having on top of the screen an article title different from the first, I choose an article and my second fragment opens with the expected contents, and that's ok. When I rotate to landscape, the same fragment contains the same content, ok. So:
1) when I press the back button from the landscape, returning to the first fragment, I get the same setting for the recycler view, ok;
2) when I rotate again to portrait, remaining on the second fragment, it is ok too. Now, if I press the back button to return to the first fragment, to the list of articles, the recycler view is NOT set to start with the item I initially scrolled to. What can I do?
I have this code in the first fragment, the one containing the recycler view:
#Override
public void onSaveInstanceState(Bundle state) {
int lastFirstVisiblePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
state.putInt(Articles.RECYCLER_POSITION_KEY, lastFirstVisiblePosition);
super.onSaveInstanceState(state);
}
#Override
public void onActivityCreated(Bundle state) {
super.onActivityCreated(state);
if (state != null) {
int lastFirstVisiblePosition = state.getInt(Articles.RECYCLER_POSITION_KEY);
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);
}
}
What am I missing? Thanks.
[EDIT] In my listener is this, for invoking the second fragment:
ArticleReaderFrag newFragment = new ArticleReaderFrag();
Bundle args = new Bundle();
args.putString(NEW_FRAG_TITLE_KEY, item);
args.putString(NEW_FRAG_BODY_KEY, itemb);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity, newFragment);
transaction.addToBackStack(null);
transaction.commit();
Reuse Fragment
When the Activity launches for the first time create a new instance of Fragment and use a TAG to save it with FragmentManager. When the activity gets recreated after orientation change. Retreive the old instance using the Tag.
Here is a sample code which you should have in your activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity);
fragmentManager = getSupportFragmentManager();
if(savedInstanceState==null) {
userFragment = UserNameFragment.newInstance();
fragmentManager.beginTransaction().add(R.id.profile, userFragment, "TAG").commit();
}
else {
userFragment = fragmentManager.findFragmentByTag("TAG");
}
}
How to save scroll position?
That happens by default. Read this answer for more detail.
EDIT
I believe this is your method
public createSecondFragment(int position){
ArticleReaderFrag newFragment = new ArticleReaderFrag();
Bundle args = new Bundle();
args.putString(NEW_FRAG_TITLE_KEY, item);
args.putString(NEW_FRAG_BODY_KEY, itemb);
newFragment.setArguments(args);
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
Save the position of on rotation and use that position to load the specific fragment.
Call the method when the activity is recreated after orientation change
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity);
if(savedInstanceState!=null) {
int lastSavedPosition = // Your logic
createSecondFragment(lastSavedPosition )
}
}

How to avoid fragment reload after onBackPressed Android

I used fragment to show Vehicle list in recycler view.
Whenever user clicks on Vehicle then it shows its details in another fragment,
but when i comeback on Vehicle list fragment after getting its details using back button pressed then Vehicle list fragment loads like first time.
I used load more in recycler view.
That's why whenever user scrolls much more and if he wanted to see details of the vehicle and then come back on list it reloads like first time and user gets first item back...
Recycler view scroll listener in Vehicle list Fragment
rvVehicleList.addOnScrollListener(new EndlessRecyclerViewScrollListener((LinearLayoutManager) rvVehicleList.getLayoutManager()) {
#Override
public void onLoadMore(int page, int totalItemsCount) {
if (!vehicleSearchResponseModel.getData().get_links().getNext().equals("")) {
pageNo++;
searchMoreVehicle(pageNo);
}
}
});
Recycler view's on Click from adapter for vehicle info Fragment
holder.llMainView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = ((FragmentActivity) context).getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Bundle bundle = new Bundle();
bundle.putSerializable("vehicleInfo", vehicleSearchPagerList.get(position));
VehicleAdInfoFragment vehicleAdInfoFragment = new VehicleAdInfoFragment();
vehicleAdInfoFragment.setArguments(bundle);
fragmentTransaction.replace(R.id.frContainer, vehicleAdInfoFragment);
fragmentTransaction.addToBackStack(vehicleAdInfoFragment.getTag());
fragmentTransaction.commit();
}
});
To restrict the recreation of the fragment, what I did:
In onCreateView you can store inflated view in a global variable and initialize it only if it is null, like in this code:
var root:View?=null
var apiDataReceived=false
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
if (root==null)
root=inflater!!.inflate(R.layout.fragment_layout, container, false)
return root
}
Now if you are parsing some data and fill it into RecyclerView or any other View
Make a global variable like in the above code apiDataReceived
Set it to true if you successfully parsed data.
Before apiCalls place a condition like this:
if (!apiDataReceived) {
apiCalls()
}
So if apiCalls() would be called only if data is not parsed.
Do your HTTP calls and parsing or any other thing in a method which called after onCreateView like onStart
The above code is in kotlin, If you are facing any issue, let me know in the comments.
You can try something like this in your vehicle list fragment:
#Override
public void onPause(){
super.onPause();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = preferences.edit();
mCurrentIndex = ((LinearLayoutManager) rvVehicleList.getLayoutManager()).findFirstVisibleItemPosition();
editor.putInt("current_position", mCurrentIndex);
editor.apply();
}
#Override
public void onResume(){
super.onResume();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
mCurrentIndex = preferences.getInt("current_position", 0);
rvVehicleList.getLayoutManager().scrollToPosition(mCurrentIndex);
}
One solution could be where you do not need to replace fragment instead use add() method.
This is the only thing that worked in my case to avoid reloading of previous fragment.
fun addFragment(fragment: Fragment)
{
val transaction = supportFragmentManager.beginTransaction()
val currentFragment = supportFragmentManager .findFragmentById(R.id.container) //get current fragment
transaction.hide(currentFragment!!) //hide current fragment
transaction.add(R.id.container, fragment) //add next fragment
transaction.addToBackStack(null)
transaction.commit()
}

Android: App/Activity loses connection to ActionBar

Good Morning All,
I am having trouble with my Activity's ActionBar keeping proper functionality after it is left for a period of time. Basically I have different ActionBar views set up dependant on which page in a ViewPager that I'm on. The middle page of 3 implements ActionBar.NAVIGATION_MODE_LIST with a list that is used to filter the content of the ListView on that page. On the other two pages this list is not shown. My code for handling this:
public class SectionsPagerAdapter extends MyPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment;
if(i==0){
ActionBar bar = MyApp.this.getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
fragment = page1;
}else if(i==1){
fragment = page2;
}else{
fragment = page3;
}
return fragment;
}
#Override
public int getCount() {
return 3;
}
#Override
public void onPageSelected(int position) {
if(position== 1 && menuSearch != null){
menuSearch.setVisible(true);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
}else{
if(menuSearch != null){
menuSearch.setVisible(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
}
currentPage = position;
}
public int getCurrentPage() {
return currentPage;
}
I am currently able to force the failure for testing by starting a new activity, which I can then force close, and when I come back to this main activity the ActionBars drop down menu is shown on every page. The menu also loses connection to the filtering properties it performed in 2nd page.
UPDATE 1:
I now believe that my problem lies with a disconnect between my ViewPager, ActionBar and the Fragments they control. I added the following code to the onNavigationItemSelected portion of my Activity:
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
Log.i(TAG,"Fragment ID:" + String.valueOf(fragment.getId()));
if(fragment.isAdded()){
Log.i(TAG,"fragment.isAdded");
}
When I first run the app these Logs return the following:
Fragment ID: 2131492869
fragment.isAdded
However, after I force a crash or leave the app and come back later, the Logs return
Fragment ID: 0
The fragment.isAdded is false at this point, but I'm not sure why.
Any help would be greatly appreciated.
Thanks,
Josh
Can anyone give me insight about why this may be happening? Should I be saving the actionbar state somehow in onPause and then restoring in onResume?
Yes, this is happening becuase you not save the last index ,
so basically what you need to do is
in onResume
set the viewPager current index like this : viewPager.setCurrentItem(lastFragmentIndex);
I think you need to save your fragments in onSaveInstanceState and restore them in onRestoreInstanceState like this:
protected void onSaveInstanceState(Bundle outState) {
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, 1, fragment1);
fm.putFragment(outState, 2, fragment2);
}
and restore
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
fragment1 = (Fragment) fm.getFragment(savedInstanceState, 1);
}

Android Fragment, going back without recreating/reloading Fragment

I've seen quite a few questions on SO about Fragments and I still can't seem to figure out if what I want to do is possible, and more so if my design pattern is just flawed and I need to re-work the entire process. Basically, like most questions that have been asked, I have an ActionBar with NavigationTabs (using ActionBarSherlock), then within each Tab there is a FragementActivity and then the FragmentActivities push new Fragments when a row is selected (I'm trying to re-create an iOS Project in Android and it's just a basic Navigation based app with some tabs that can drill down into specific information). When I click the back button on the phone the previous Fragment is loaded but the Fragment re-creates itself (so the WebServices are called again for each view) and this isn't needed since the information won't change in a previous view when going backwards. So basically what I want to figure out is how do I setup my Fragments so that when I push the back button on the phone, the previous Fragment is just pulled up with the previous items already created. Below is my current code :
//This is from my FragmentActivity Class that contains the ActionBar and Tab Selection Control
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
int selectedTab = tab.getPosition();
if (selectedTab == 0) {
SalesMainScreen salesScreen = new SalesMainScreen();
ft.replace(R.id.content, salesScreen);
}
else if (selectedTab == 1) {
ClientMainScreen clientScreen = new ClientMainScreen();
ft.replace(R.id.content, clientScreen);
}.....
//This is within the ClientMainScreen Fragment Class, which handles moving to the Detail Fragment
row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Do something if Row is clicked
try{
String selectedClientName = clientObject.getString("ClientName");
String selectedClientID = clientObject.getString("ClientID");
String selectedValue = clientObject.getString("ClientValue");
transaction = getFragmentManager().beginTransaction();
ClientDetailScreen detailScreen = new ClientDetailScreen();
detailScreen.clientID = selectedClientID;
detailScreen.clientName = selectedClientName;
detailScreen.clientValue = selectedValue;
int currentID = ((ViewGroup)getView().getParent()).getId();
transaction.replace(currentID,detailScreen);
transaction.addToBackStack(null);
transaction.commit();
}
catch (Exception e) {
e.printStackTrace();
}
}
});....
//And then this is the Client Detail Fragment, with the method being called to Call the Web Service and create thew (since what is displayed on this screen is dependent on what is found in the Web Service
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved) {
return inflater.inflate(R.layout.clientdetailscreen, group, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Setup Preferences File Link
this.preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
//initialize the table object
mainTable = (TableLayout)getActivity().findViewById(R.id.mainTable);
//setup the detail table
setupRelatedClientSection();
}
The Client Detail Screen can then drill down one more time, using the same method as the Client Main Screen but when I go back from that new screen to the Detail Screen the seuptRelatedClientSection() method is called again and so the entire Fragment is rebuilt when really I just want to pull up a saved version of that screen. Is this possible with my current setup, or did I approach this the wrong way?
Try using fragementTransaction.add instead of replace
I believe that you are looking for show() and hide().
I think you can still add them to the backstack.
transaction.hide(currentFragment);
transaction.show(detailScreen);
transaction.addToBackStack(null);
transaction.commit();
I didnt have my code to look at but i believe this is how it would go... Try it out unless someone else has a better way.
I have not tried the backstack with show() hide() but i believe that it takes the changes that are made before the transactions commit and will undo them if the back button is pressed. Please get back to me on this cause i am interested to know.
You also have to make sure that the detail fragment is created before you call this. Since it is based on the click of someitem then you should probably create the details fragment every time you click to make sure the correct details fragment is created.
I'm posting this answer for people who may refer this question in future.
Following code will demonstrate how to open FragmentB from FragmentA and going back to FragmentA from FragmentB (without refreshing FragmentA) by pressing back button.
public class FragmentA extends Fragment{
...
void openFragmentB(){
FragmentManager fragmentManager =
getActivity().getSupportFragmentManager();
FragmentB fragmentB = FragmentB.newInstance();
if (fragmentB.isAdded()) {
return;
} else {
fragmentManager.
beginTransaction().
add(R.id.mainContainer,fragmentB).
addToBackStack(FragmentB.TAG).
commit();
}
}
}
public class FragmentB extends Fragment{
public static final String TAG =
FragmentB.class.getSimpleName();
...
public static FragmentB newInstance(){
FragmentB fragmentB = new FragmentB();
return fragmentB;
}
#Override
public void onResume() {
super.onResume();
// add this piece of code in onResume method
this.getView().setFocusableInTouchMode(true);
this.getView().requestFocus();
}
}
In your MainActivity override onBackPressed()
class MainActivity extends Activity{
...
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
You're right, there has been a number of previous questions / documentation on the topic ;)
The documentation on Fragments, specifically the section about Transactions and Saving State, will guide you to the answer.
http://developer.android.com/guide/components/fragments.html#Transactions
http://developer.android.com/guide/components/activities.html#SavingActivityState
Android - Fragment onActivityResult avoid reloading
Fragments can have support for onSaveInstanceState but not onRestoreInstanceState, so if you want to save a reference to the table views, save them to the Bundle and you can access the saved view in your onActivityCreated method. You could also use the Fragments back stack.
This guide/tutorial has very detailed instructions/examples on the back stack and retaining fragment state.
Good luck

Handling ActionBar title with the fragment back stack?

I have an Activity where I load in a ListFragment and, upon clicking, it drills down a level and a new type of ListFragment is shown, replacing the original one (using the showFragment method below). This is placed on the back stack.
At the beginning, the activity shows the default title in the action bar (i.e. it's set automatically based on the application's android:label).
When showing the list for the next level in the hierarchy, the name of the item clicked on should become the action bar's title.
However, when pressing Back, I would like the original default title to be restored. This isn't something FragmentTransaction knows about, so the title isn't restored.
I've vaguely read about FragmentBreadCrumbs, but this seems to require using a custom view. I'm using ActionBarSherlock and would prefer to not have my own custom title view.
What is the best way of doing this? Is it possible without a load of boilerplate code and having to keep track of the titles shown along the way?
protected void showFragment(Fragment f) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, f);
ft.addToBackStack(null);
ft.commit();
}
In every fragment and every activity I change the title like this. This way the active title will always be correct:
#Override
public void onResume() {
super.onResume();
// Set title
getActivity().getActionBar()
.setTitle(R.string.thetitle);
}
There is some cases where onResume isn't called inside fragments. In some of these cases we can use:
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
// Set title
getActivity().getActionBar()
.setTitle(R.string.thetitle);
}
}
As the original answer is quite old, this might come of help as well. As the documentation states, one might want to register a listener to listen on the back stack changes in the hosting Activity:
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
Then, identify the situation in the callback method and set a proper title, without accessing the ActionBar from the Fragment.
This is a more elegant solution as the Fragment doesn't have to know about the ActionBar existence and Activity is usually the place that is managing the backstack so having it handled over there seems to be more appropriate. Fragment should at all time be considered only by its own content, not the surroundings.
More on the topic in the documentation.
Let the controlling activity do all the work as follows:
Listen for backstack events (in onCreate() of activity):
// Change the title back when the fragment is changed
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
Fragment fragment = getFragment();
setTitleFromFragment(fragment);
}
});
Get the current fragment from the container:
/**
* Returns the currently displayed fragment.
* #return
* Fragment or null.
*/
private Fragment getFragment() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
return fragment;
}
Set the fragment inside the content view:
private void setFragment(Fragment fragment, boolean addToBackStack) {
// Set the activity title
setTitleFromFragment(fragment);
.
.
.
}
Warpzit is right. This also solves title problem when orientation of device is changed. Also if you use support v7 for action bar, you can get action bar from fragment like this :
#Override
public void onResume() {
super.onResume();
((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}
It is best to let the OS do as much of the work as possible.
Assuming each fragment is properly named using .addToBackStack("title") then
you can override onBackPressed something like this to achieve desired behavior:
// this example uses the AppCompat support library
// and works for dynamic fragment titles
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
int count = fragmentManager.getBackStackEntryCount();
if (count <= 1) {
finish();
}
else {
String title = fragmentManager.getBackStackEntryAt(count-2).getName();
if (count == 2) {
// here I am using a NavigationDrawer and open it when transitioning to the initial fragment
// a second back-press will result in finish() being called above.
mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
}
super.onBackPressed();
Log.v(TAG, "onBackPressed - title="+title);
getSupportActionBar().setTitle(title);
}
}
I use a similar solution to Lee approach, but replacing onBackStackChanged() method instead.
First I set the fragment name when adding the transaction to the back stack.
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_content, fragment)
.addToBackStack(fragmentTitle)
.commit();
Then I override the onBackStackChanged() method and I call setTitle() with the last backstack entry name.
#Override
public void onBackStackChanged() {
int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
FragmentManager.BackStackEntry lastBackStackEntry =
getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);
setTitle(lastBackStackEntry.getName());
}
Use Fragments method:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
It is called on every Fragment appearance, but onResume is not.
The best approach is to make use of the android provided Interface OnBackStackChangedListener method onBackStackChanged().
Lets say we have a navigation drawer with 4 options to which the user can navigate to. In that case we will have 4 fragments. Lets see the code first and then I will explain the working.
private int mPreviousBackStackCount = 0;
private String[] title_name = {"Frag1","Frag2","Frag3","Frag4"};
Stack<String> mFragPositionTitleDisplayed;
public class MainActivity extends ActionBarActivity implements FragmentManager.OnBackStackChangedListener
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
....
....
getSupportFragmentManager().addOnBackStackChangedListener(this);
mFragPositionTitleDisplayed = new Stack<>();
}
public void displayFragment() {
Fragment fragment = null;
String title = getResources().getString(R.string.app_name);
switch (position) {
case 0:
fragment = new Fragment1();
title = title_name[position];
break;
case 1:
fragment = new Fragment2();
title = title_name[position];
break;
case 2:
fragment = new Fragment3();
title = title_name[position];
break;
case 3:
fragment = new Fragment4();
title = title_name[position];
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container_body, fragment)
.addToBackStack(null)
.commit();
getSupportActionBar().setTitle(title);
}
}
#Override
public void onBackStackChanged() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
if(mPreviousBackStackCount >= backStackEntryCount) {
mFragPositionTitleDisplayed.pop();
if (backStackEntryCount == 0)
getSupportActionBar().setTitle(R.string.app_name);
else if (backStackEntryCount > 0) {
getSupportActionBar().setTitle(mFragPositionTitleDisplayed.peek());
}
mPreviousBackStackCount--;
}
else{
mFragPositionTitleDisplayed.push(title_name[position]);
mPreviousBackStackCount++;
}
}
In the code shown we have the displayFragment() method. Here I display the fragment on the basis of option chosen from the navigation drawer.The variable position corresponds to the position of the item clicked from the ListView or RecyclerView in the navigation drawer. I set the actionbar title accordingly with getSupportActionBar.setTitle(title), where the title stores the appropriate title name.
Whenever we click the item from nav drawer a fragment is displayed depending on the item clicked to the user. But on the back end side this fragment is added to the backstack and the method onBackStachChanged(), gets hit. What I have done is that I have created a variable mPreviousBackStackCount and initialized it to 0. I have also created an additional stack which will store the action bar title names. Whenever I add a new fragment to the backstack, I add the corresponding title name to my created stack. On the opposite side whenever I press the back button onBackStackChanged() is called and I pop the last title name from my stack and set the title to the name derived by the peek() method of the stack.
Example:
Lets say our android backstack is empty:
Press Choice 1 from nav drawer:
onBackStachChanged() is called and the Fragment 1 is added to android backstack, backStackEntryCount is set to 1 and Frag1 is pushed to my stack and size of mFragPositionTitleDisplayed becomes 1.
Press Choice 2 from nav drawer:
onBackStachChanged() is called and the Fragment 2 is added to android backstack, backStackEntryCount is set to 2 and Frag2 is pushed to my stack and size of mFragPositionTitleDisplayed becomes 2.
Now we have 2 elements both in the android stack and my stack. When you press back button onBackStackChanged() is called and the value of backStackEntryCount is 1. The code enters the if part and pops out the last entry from my stack. So, the android backstack has only 1 fragment - "Fragment 1" and my stack has only 1 title - "Frag1". Now I just peek() the title from my stack and set the action bar to that title.
Remember: To set the action bat title use peek() and not pop() else your application will crash when you open more than 2 fragments and try to go back by pressing back button.
You can Solve with onKeyDown!
I have a bool
mainisopen=true <-- MainFragment is Visible
other Fragment mainisopen=false
and here is My Code:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == false) {
mainisopen = true;
HomeFrag fragment = new HomeFrag();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmet_cont, fragment);
fragmentTransaction.commit();
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
navigationView.setNavigationItemSelectedListener(this);
this.setTitle("Digi - Home"); //Here set the Title back
return true;
} else {
if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == true) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Wollen sie die App schliessen!");
builder.setCancelable(true);
builder.setPositiveButton("Ja!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
System.exit(1);
}
});
builder.setNegativeButton("Nein!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "Applikation wird fortgesetzt", Toast.LENGTH_SHORT).show();
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
As described here my solution is adding this code to MainActivity onCreate method(): and changing actionbar title
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
Fragment currentFragment = fragmentManager.findFragmentById(R.id.My_Container_1_ID);
currentFragment.onResume();
}
});
and changing actionbar title in fragment's onResume() method
#Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
ActionBar actionBar = activity.getSupportActionBar();
if(actionBar!=null) {
actionBar.setTitle("Fragment Title");
actionBar.setSubtitle("Subtitle");
}
}
To update the actionbar title on back press. Just simply put
getActivity.setTitle("title")
inside onCreateView method.

Categories

Resources