android how to manage fragments in navigation bar without data lose - android

I have a store app
In this application, the fragments each take their own data from the server and are managed by navigation bar.
I want to receive data any fragment one only once , and will only maintain its last state some time later.
I used the replace , hide functions, but I did not get good results
Can anyone suggest a solution?
nav_partition.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
FragmentManager fm = getSupportFragmentManager();
Fragment lastFragment = fm.findFragmentById(R.id.fragment_layout);
if (lastFragment instanceof FragmentPartition)
return;
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.hide(lastFragment);
if (fragmentPartition == null)
fragmentPartition = new FragmentPartition();
G.log_toast("is added : " + fragmentPartition);
fragmentTransaction.replace(R.id.fragment_layout, fragmentPartition);
fragmentTransaction.commit();
}
});

You can use savedinstancestate bundle for saving your data. and it can be reused on resuming. Try bellow code.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("bln", true);
savedInstanceState.putInt("it", 1);
savedInstanceState.putString("str", "Hello");
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean bln = savedInstanceState.getBoolean("bln");
int in = savedInstanceState.getInt("it");
String str = savedInstanceState.getString("str");
}

Related

onBackPressed with fragments and saving fragment state

I want to save fragment state so I use fragment .add() and fragment .show() methods.
the part I use to add fragments in my mainActivity is
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.main_content, fragment, CURRENT_TAG);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
On back pressed in the fragment and getting back to it the get fragment by tag returns null
if (getSupportFragmentManager().findFragmentByTag(CURRENT_TAG) != null) {
drawerLayout.closeDrawers();
Runnable mPendingRunnable = new Runnable() {
#Override
public void run() {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
hideTransactions(CURRENT_TAG);
fragmentTransaction.show(getSupportFragmentManager().findFragmentByTag(CURRENT_TAG)).commit();
}
};
although onBackPressed the state of the fragment is right(TAG_PREV returns not null) in the on back pressed method
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawers();
return;
}
if (CURRENT_TAG == TAG_HOME) {
return;
}
navItemIndex = 0;
TAG_PREV = CURRENT_TAG;
CURRENT_TAG = TAG_HOME;
loadHomeFragment();
super.onBackPressed();
}
addToBackStack saves the transaction, in this case: fragmentTransaction.add(R.id.main_content, fragment, CURRENT_TAG); and when you press back(onBackPressed) you are reversing that transaction. So that will imply you are removing the previously added fragment.
It is not possible to maintain entire fragment in back stack, There is no provision given by android teams.
You have to use onSaveInstanceState for saving current state/data when you are leaving fragment.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, " onSaveInstanceState.");
savedInstanceState.putString("greeting", "Hello");
}
After that when again coming on that fragment you have to use.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String greeting = (savedInstanceState != null) ? savedInstanceState.getString("greeting") : "null";
Log.i(TAG, " onCreate: " + greeting);
}
Please let me if it dosen't work for you.

savedInstanceState in Fragment doesn't work

something wrong with my savedInstanceState into my Fragment.
The Save Instance doesn't work. When I switch back to "HomeMenu", all fields disappear.
On my Activity:
private void selectItemFromDrawer(int position) {
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = null;
String tagFrag = "default";
switch (mNavItems.get(position).mTitle) {
case R.string.menu_home:
fragment = fragmentManager.findFragmentByTag("menu_home");
if(fragment == null) {
fragment = new HomeMenuFragment();
} else {
Toast.makeText(customActivity.this, "already Exists", Toast.LENGTH_SHORT).show();
}
tagFrag = "menu_home";
break;
case R.string.menu_pref:
fragment = fragmentManager.findFragmentByTag("menu_pref");
if(fragment == null)
fragment = new PreferencesFragment();
tagFrag = "menu_pref";
break;
}
if(fragment != null) {
fragmentManager.beginTransaction()
.replace(R.id.mainContent, fragment, tagFrag)
.commit();
}
}
Thats works.. The Fragment change properly..
The TOAST works only if the Fragment is ACTIVE.. but not if I switch back to him.
example:
Home => Home => "toast Appear"
Home => Pref => Home => "No Toast".
Into my HomeMenuFragment I use this code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_main, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
strUser = savedInstanceState.getString("strUser", "");
strName = savedInstanceState.getString("strName", "");
strDesc = savedInstanceState.getString("strDesc", "");
if(((TextView) getActivity().findViewById(R.id.user))!=null)
((TextView) getActivity().findViewById(R.id.user)).setText(strUser);
if(((TextView) getActivity().findViewById(R.id.name))!=null)
((TextView) getActivity().findViewById(R.id.name)).setText(strName);
if(((TextView) getActivity().findViewById(R.id.desc))!=null)
((TextView) getActivity().findViewById(R.id.desc)).setText(strDesc);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("strUser", ((TextView) getActivity().findViewById(R.id.user)).getText().toString());
outState.putString("strName", ((TextView) getActivity().findViewById(R.id.name)).getText().toString());
outState.putString("strDesc", ((TextView) getActivity().findViewById(R.id.desc)).getText().toString());
}
According to the Bundle documentation, there is no putString() nor getString() methods, you need to use putStringArrayList (String key, ArrayList<String> value). I also suggest you avoid so many calls on getActivity().findViewById(), it is well known that it causes a lot of overhead. Also, why are you obtaining view values from the activity inside a fragment? Looks like duplicated code. Anyway, try this:
TextView tvUser, tvName, tvDesc;
#Override
public void onCreate(Bundle savedInstanceState) {
// Store TextView references calling findViewById only once
tvUser = ((TextView) getActivity().findViewById(R.id.user));
tvName = ((TextView) getActivity().findViewById(R.id.user));
tvDesc = ((TextView) getActivity().findViewById(R.id.user));
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
ArrayList al = new ArrayList();
al = savedInstanceState.getStringArrayList("args");
tvUser.setText(al.get(0));
tvName.setText(al.get(1));
tvDesc.setText(al.get(2));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList al = new ArrayList();
al.add(0, tvUser.getText());
al.add(1, tvName.getText());
al.add(2, tvDesc.getText());
outState.putStringArrayList("args", al);
}
OK, I'll try to explain that, is not pretty easy.
In this case, your fragment is not destroyed, only the view inside it does.
So every view you used must implement state save/restore.
I left you a better explanation that mine, hope this helps you!
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
Update
Try to create fragment using newIstance, this is a best practice to avoid the loss of the parameters.
For example:
public static MyFragment newInstance(String user, String name, String desc) {
MyFragment mFragment = new MyFragment();
Bundle args = new Bundle();
args.putString("user", user);
args.putString("name", name);
args.putString("desc", desc);
mFragment.setArguments(args);
return mFragment;
}
And in the fragment onCreate() you can access the data by using:
String strUser = getArguments().getString("user", "");
If the fragment is recreated by Android, the Bundle will be available.
Remember, setArguments can only be called before the Fragment is attached to the Activity.
Let me know!
It might be that you call the super.onSaveInstanceState(outState) before you put the strings which you hope to save. To solve this, you simply need to call super.onSaveInstanceState(outState) AFTER putting the values you hope to save. Here are the changes you need to make:
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("strUser", ((TextView) getActivity().findViewById(R.id.user)).getText().toString());
outState.putString("strName", ((TextView) getActivity().findViewById(R.id.name)).getText().toString());
outState.putString("strDesc", ((TextView) getActivity().findViewById(R.id.desc)).getText().toString());
//moved it here
super.onSaveInstanceState(outState);
}
I hope this solves your problem. Give it a try and let me know.

fragment created twice on orientation change

I know this question has been asked, but I haven't succeeded with the answers.
I have a fragment with a recycler view in it. I have a button which can show and hide this fragment. This all works fine until the orietation of the screen is changed. Then the fragment is recreated, and the one on top is shown and hidden, but there is one behind which stays there.
I understand I need to use
if (savedInstanceState == null)
somewhere, but cannot manage to succeed where. Thanks very much,
Here is my code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
recyclerViewFragment = new RecyclerViewFragment();
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
trans.commit();
trans.show(recyclerViewFragment);
Button showHideButton = (Button)findViewById(R.id.button_show_hide);
showHideButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showHideFragment(recyclerViewFragment);
}
});
showHideButton.playSoundEffect(SoundEffectConstants.CLICK);
}
public void showHideFragment(final Fragment fragment){
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.setCustomAnimations(android.R.anim.slide_in_left , android.R.anim.slide_out_right);
if (fragment.isHidden()) {
trans.show(fragment);
Log.d("hidden","Show");
} else {
trans.hide(fragment);
Log.d("Shown","Hide");
}
trans.commit();
}
Thanks very much guys!!!!!!!
I suggest make some changes to your code
I suppose that RECYCLER_FRAGMENT is a constant that contains a tag used to mark your fragment
RecyclerViewFragment recyclerViewFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
if(savedInstanceState == null) {
recyclerViewFragment = new RecyclerViewFragment();
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
trans.commit();
}else{
recyclerViewFragment = getSupportFragmentManager().findFragmentByTag(RECYCLER_FRAGMENT);
if(savedInstanceState.getString("vi").equals("hid")){
getSupportFragmentManager().beginTransaction().hide(recyclerViewFragment).commit();
}
}
Button showHideButton = (Button)findViewById(R.id.button_show_hide);
showHideButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
if (fragment.isVisible()) {
manager.beginTransaction().hide(recyclerViewFragment).commit();
} else {
manager.beginTransaction().show(recyclerViewFragment).commit();
}
}
});
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(recyclerViewFragment.isVisible() == true){
outState.putString("vi","vis");
}else{
outState.putString("vi", "hid");
}
}
Instead of this:
trans.add(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
try this:
trans.replace(R.id.recycle_view_container, recyclerViewFragment, RECYCLER_FRAGMENT);
The name is misleading. Replace actually works as add also, if the first time. What is happening is that when your activity rotate, onCreate() is called again so you are adding the same fragment on top of the existing one

Android SlidingMenu lag at first use

I am experiencing some problems with the slidingmenu library from jfeinstein10: https://github.com/jfeinstein10/SlidingMenu
The problem I observe is that at first use, when the menu opens, it lags showing some black space. Then when I close it and reuse it again, everything is smooth, nothing to declare.
I basically have an Activity extending the SlidingFragmentActivity, and 2 fragments. One for the left menu and another one for the main content.
Here is how I implemented it, I'm investigating this but I really don't know what I've missed:
public class CavendishSlidingFragmentActivity extends SlidingFragmentActivity {
private final String TAG = CavendishSlidingFragmentActivity.class.getName();
protected FragmentManager fragmentManager;
protected Fragment mContent;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
fragmentManager = getSupportFragmentManager();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
getSlidingMenu().setBehindOffset((int) (metrics.widthPixels * 0.2));
}
public void onClickMenu(View v)
{
if (getSlidingMenu().isMenuShowing())
getSlidingMenu().showContent();
else
getSlidingMenu().showMenu();
}
public void onClickMenuItem(View v)
{
Long position = (Long) v.getTag();
switch (position.intValue())
{
case Utils.CAT_RESTAURANT:
showFragment(new RestaurantFragment());
break;
case Utils.CAT_SLEEP:
showFragment(new SleepFragment());
break;
case Utils.CAT_DISCOVER:
showFragment(new DiscoverFragment());
break;
case Utils.CAT_TRANSPORT:
showFragment(new TransportFragment());
break;
}
}
private void showFragment(Fragment fragment)
{
if (mContent != fragment)
{
mContent = fragment;
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_fragment, fragment);
fragmentTransaction.commit();
}
getSlidingMenu().showContent();
}
}
MainActivity.java
public class MainActivity extends CavendishSlidingFragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setBehindContentView(R.layout.fragment_menu);
setContentView(R.layout.fragment_main);
// set the Above View Fragment
if (savedInstanceState != null)
mContent = fragmentManager.getFragment(savedInstanceState, "mContent");
if (mContent == null)
{
mContent = new RestaurantFragment();
}
fragmentManager
.beginTransaction()
.replace(R.id.main_fragment, mContent)
.commit();
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
fragmentManager.putFragment(outState, "mContent", mContent);
}
}
I can provide further code but I think the issue might be here, since the menu is drawing itself at first use, causing the lag. I believe I have to force the menu to be drawn before showing it.
Looks like there is something wrong with the library when rendering the menu while it's being opened. My design didn't fit the library I guess, well I just passed to the default Android navigation drawer. Bit different in behavior but the design remains unchanged, works fine for me.

android fragment state restored only on back button , not when i select a fragment randomly from listview

Hi i have a listview sidebar and i am displaying fragments based on user selection in listview.
This is how i am replacing fragments
public void switchFragment(Fragment fragment, boolean addBackStack) {
try {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
currentFragment = fragment;
//if (addBackStack)
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
}
}
This is my sample fragment code.Now when i replace fragments i am saving instance state in onpause and restoring it in onresume but it only works when i press back button. When i manually navigate back to fragment from listview ,fragment state is not restored.Why?
public class Fragment1 extends BaseFragment {
int currentFragmentInd = 1;
private Button startButton;
private Button endButton;
private long savedStartTime;
private TextView setStartText;
private TextView setEndText;
private String starttime;
private String endtime;
public int getIndex() {
MyApplication.getApplication().setCurrentChild(0);
MyApplication.getApplication().setCurrentGroup(0);
return currentFragmentInd;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState !=null)
{
}
}
#Override
public void onResume() {
super.onResume();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
setEndText.setText(endtime);
setStartText.setText(starttime);
}
#Override
public void onPause() {
super.onPause();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
starttime=setStartText.getText().toString();
endtime=setEndText.getText().toString();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
FrameLayout frameLayout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.layout1, null, false);
((MainActivity) getActivity()).openList(0, 0);
if (savedInstanceState == null) {
}
startButton= (Button) contentView.findViewById(R.id.button);
endButton= (Button) contentView.findViewById(R.id.button2);
endButton.setEnabled(false);
setStartText= (TextView)contentView.findViewById(R.id.MAtextView2);
setEndText= (TextView)contentView.findViewById(R.id.MAtextView3);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
}
});
endButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
setEndText.setText(now.hour+" : "+now.minute);
}
});
return contentView;
}
}
Late replay but might help somebody else.
This happens because when you click a listview item you create a new inctance of that fragment.
"I assume the fragment you send to switchFragment(Fragment fragment), is created using a 'new' keyword."
Therefore this new instance of a fragment doesnt hold your old data.
This is how I solved this. There are probably better ways, but since nobody replied, I will give my solution.
When you replace the fragment (ft.replace, fragment), give a string reference to that transaction: -ft.replace(R.id.content, fragment, "FRAGMENT_NAME");
When you add the fragment to the backstack with addToBackStack(null); put the name of your fragment where you have null.: -ft.addToBackStack("FRAGMENT_NAME");
Create a method which tells you if that fragment has already been created, and therefore exists in the back stack.:
public boolean isTagInBackStack(String tag){
Log.i(TAG, "isTagInBackStack() Start");
int x;
boolean toReturn = false;
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
Log.i(TAG, "backStackCount = " + backStackCount);
for (x = 0; x < backStackCount; x++){
Log.i(TAG, "Iter = " + x +" "+ getSupportFragmentManager().getBackStackEntryAt(x).getName());
if (tag == getSupportFragmentManager().getBackStackEntryAt(x).getName()){
toReturn = true;
}
}
Log.i(TAG, "isTagInBackStack() End, toReturn = " + toReturn);
return toReturn;
}
Now before you create a new instance of that fragment check in the backstack if a backstack item named "FRAGMENT_NAME" exists.
if it exists, use that item (fragment) instead of creating a new one.
if (isTagInBackStack("FRAGMENT_NAME")){
Log.i(TAG, "Tag is in BackStack!!!! frag is = " + getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity_container, getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
transaction.addToBackStack("FRAGMENT_NAME");
transaction.commit();
}else{
Create the fragment (this happens the first time.
}

Categories

Resources