notifyDataSetChanged stop working after onDestroy in fragment - android

I've got a fragment with a RecyclerView inside. All is working good but when I press home, navigate in other apps and then go back in my app the view gets recreated and my ArrayList (restored from onSaveInstanceState) is not displayed in my recyclerview that don't work even after updating the List.
Here some code:
-Activity
public class MainActivity extends AppCompatActivity {
FragmentTransaction ft;
BottomNavigationView bottomNavigationView;
int current;
private FirebaseAnalytics mFirebaseAnalytics;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null) {
setupFragment();
}
// mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
bottomNavigationView = (BottomNavigationView)
findViewById(R.id.bottom_navigation);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_golden_hour:
changeFragment(new GoldenFragment(), "GOLDEN");
current=0;
break;
case R.id.action_hyperfocal:
changeFragment(new HyperFragment(), "HYPER");
current=1;
break;
case R.id.action_ir:
changeFragment(new IrFragment(), "IR");
current=2;
break;
case R.id.action_nd:
changeFragment(new NdFragment(), "ND");
current=3;
break;
}
return true;
}
});
}
#SuppressLint("CommitTransaction")
private void setupFragment(){
ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.main_fragment, new GoldenFragment()).commit();
current=0;
}
#SuppressLint("CommitTransaction")
private void changeFragment(Fragment fragment, String tag){
ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.main_fragment, fragment, tag).commit();
}
-Fragment
public class GoldenFragment extends Fragment implements DatePickerDialog.OnDateSetListener{
SupportPlaceAutocompleteFragment autocompleteFragment;
RecyclerView rv;
CustomAdapter adapter;
ProgressBar pb;
ArrayList<Hours> hours;
CardView cv;
TextView emptyGoldenText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thisContext = getContext();
setHasOptionsMenu(true);
ampm = !android.text.format.DateFormat.is24HourFormat(thisContext);
hours = new ArrayList<>();
adapter = new CustomAdapter(thisContext, hours);
if(savedInstanceState!=null) {
Log.d(TAG,"inState not null");
hours.clear();
hours = savedInstanceState.getParcelableArrayList("HoursList");
}
Log.d(TAG,"onCreate() called");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if(view == null) {
view = inflater.inflate(R.layout.fragment_golden, container, false);
}
Toolbar goldenToolbar = (Toolbar) view.findViewById(R.id.toolbar_golden);
((AppCompatActivity) getActivity()).setSupportActionBar(goldenToolbar);
emptyGoldenText = (TextView) view.findViewById(R.id.empty_golden_text);
autocompleteFragment = (SupportPlaceAutocompleteFragment) getChildFragmentManager()
.findFragmentById(R.id.place_autocomplete_fragment);
if (autocompleteFragment == null) {
autocompleteFragment = (SupportPlaceAutocompleteFragment) SupportPlaceAutocompleteFragment
.instantiate(thisContext, "com.google.android.gms.location.places.ui.SupportPlaceAutocompleteFragment");
}
autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(Place place) {
Log.i(TAG, "Place: " + place.getName());
try {
new GeoCoding()
.execute("https://maps.googleapis.com/maps/api/geocode/json?address="+place.getName()+"&key="+geoKEY);
} catch (Exception e) {
Toast.makeText(thisContext,"Cannot contact Google's servers, please try later.", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
#Override
public void onError(Status status) {
Log.i(TAG, "An error occurred: " + status);
}
});
getChildFragmentManager().beginTransaction()
.replace(R.id.place_autocomplete_fragment, autocompleteFragment).commit();
//Initialize RecyclerView
rv = (RecyclerView)view.findViewById(R.id.list_golden);
rv.setLayoutManager(new LinearLayoutManager(thisContext));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(rv.getContext(),
getActivity().getResources().getConfiguration().orientation);
rv.addItemDecoration(dividerItemDecoration);
rv.setAdapter(adapter);
cv = (CardView) view.findViewById(R.id.cv_golden);
pb = (ProgressBar) view.findViewById(R.id.progress_bar);
if(savedInstanceState==null) {
Log.d(TAG,"New empty data set");
rv.setVisibility(View.GONE);
cv.setVisibility(View.GONE);
pb.setVisibility(View.INVISIBLE);
}
else{
Log.d(TAG,"Old data set");
adapter.notifyDataSetChanged();
pb.setVisibility(View.INVISIBLE);
rv.setVisibility(View.VISIBLE);
cv.setVisibility(View.VISIBLE);
emptyGoldenText.setVisibility(View.INVISIBLE);
}
Log.d(TAG,"onCreateView() called");
return view;
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("HoursList",hours);
Log.d(TAG,"onSaveInstanceState called");
}
SOLVED
Solved by moving
adapter = new CustomAdapter(thisContext, hours);
from onCreate() to onCreateView().

In the activity, you have to save the fragment's instance in onSaveInstanceState() and restore in onCreate().
#Override
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
fragment = getSupportFragmentManager().getFragment(savedInstanceState, "KEY");
changeFragment(fragment, "MY TAG")
} else {
setupFragment();
}
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Fragment fragment = getSupportFragmentManager().findFragmentByTag("MY TAG");
if (fragment != null) {
getSupportFragmentManager().putFragment(outState, "KEY", fragment);
}
}

try this code: in onSaveInstanceState put arraylist
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("HoursList",hours);
Log.d(TAG,"onSaveInstanceState called");
}
In onActivityCreated you check the following conditions:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState! = null) {
//probably orientation change
Log.d(TAG,"inState not null");
hours.clear();
hours = savedInstanceState.getParcelableArrayList("HoursList");
//check if u get the value print log
}
else {
if (hours != null) {
//returning from backstack, data is fine, do nothing
} else {
//newly created, compute data
hours = computeData();
}
}
}

Related

onSaveInstanceState null in Fragment everytime

I search everywhere and for the solution but not find it, I need some help.
I have an app that has Two activity's, Activity A and Activity B, but B have fragments, the first fragment from B, have an important data that I don't want to lose when the user press back when going to Activity A.
My problem is this every time I'm back to Activity A and go to B, my Data go empty because android clear it, so I made some search and changes on the code and still not find the solution. Now my code :
ACTIVITY A
fbReceive.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, OmniActivity.class));
}
});
I call B from this line and when they go to B this is the code :
ACTIVITY B
public class OmniActivity extends BaseActivity {
private View parent_view;
private TabLayout tab_layout;
FragmentOmni fragmentOmni = new FragmentOmni();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_omni_om);
parent_view = findViewById(R.id.container);
Tools.setSystemBarColor(this, R.color.new_purple_O200);
openFragment(fragmentOmni);
initToolbar();
initComponent();
}
public void openFragment(final Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.containerView, fragment, fragment.getTag());
transaction.addToBackStack(null);
transaction.commit();
}
#Override
public void onBackPressed() {
Intent intent = new Intent(this, MainActivity.class);
startActivityForResult(intent, 1);
}
private void initToolbar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Voltar");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.getNavigationIcon().setColorFilter(getResources().getColor(android.R.color.white), PorterDuff.Mode.SRC_ATOP);
toolbar.setNavigationOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
Intent intent = new Intent(OmniActivity.this, MainActivity.class);
startActivityForResult(intent, 1);
}
});
}
And when the application executes OpenFragment() they go to this code from Fragment :
FRAGMENT A
public class FragmentOmni extends Fragment {
RecyclerView recyclerView;
MDOmniturn controller;
List<HashMap<String, String>> listproduct;
private ArrayList<Product> producttypelist;
Product tpobjproduct;
private ActionMode actionMode;
private ActionModeCallback actionModeCallback;
private ListProductAdapter lpAdapter;
private NestedScrollView nested_scroll_view;
private ImageButton bt_toggle_input;
private Button bt_hide_input;
private View lyt_expand_input;
EditText edOmni, edMani,edEan ;
LinearLayout layoutNoResult;
Handler time;
TextWatcher textexample;
private ProgressBar progressBarProduct;
LinearLayout linearLayout;
public FragmentOmni() {
}
public static FragmentOmni newInstance() {
FragmentOmni fragment = new FragmentOmni();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState != null){
producttypelist = savedInstanceState.getParcelableArrayList("list");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_omni, container, false);
return root;
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList("list", producttypelist);
super.onSaveInstanceState(outState);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (savedInstanceState != null) {
producttypelist = savedInstanceState.getParcelableArrayList("list");
}
initexpand(view);
linearLayout = (LinearLayout) view.findViewById(R.id.container);
edOmni = (EditText) view.findViewById(R.id.edOmni);
edMani = (EditText) view.findViewById(R.id.edBManifesto);
edEan = (EditText) view.findViewById(R.id.edEan);
progressBarProduct = (ProgressBar) view.findViewById(R.id.progressBarProduct);
layoutNoResult = (LinearLayout) view.findViewById(R.id.layoutNoResult);
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.addItemDecoration(new LineItemDecoration(getActivity(), LinearLayout.VERTICAL));
recyclerView.setHasFixedSize(true);
controller = new MDOmniturn(getActivity());
producttypelist = new ArrayList<>();
listproduct = new ArrayList<>();
addListenerTextChange(edOmni);
//set data and list adapter
lpAdapter = new ListProductAdapter(getActivity(), producttypelist);
recyclerView.setAdapter(lpAdapter);
lpAdapter.setOnClickListener(new ListProductAdapter.OnClickListener() {
#Override
public void onItemClick(View view, Product obj, int pos) {
if (lpAdapter.getSelectedItemCount() > 0) {
enableActionMode(pos);
} else {
// read the inbox which removes bold from the row
Product product = lpAdapter.getItem(pos);
Toast.makeText(getActivity(), "Read: " + product.prd_description, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onItemLongClick(View view, Product obj, int pos) {
enableActionMode(pos);
}
});
actionModeCallback = new ActionModeCallback();
}
The savedInstanceState from FRAGMENT A always go NULL, what i'm doing wrong?
Replace your code to the following order
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("list", producttypelist);
}
It might be possible you are expecting data during the wrong state too.
For example, the savedInstanceState will always be null the first time an Activity is started which also points to your fragment, but may be non-null if an Activity is destroyed during rotation, because onCreate is called each time activity starts or restarts.
This line transaction.addToBackStack(null); is unnecessary. You can save the fragment state on its onStop() and onPause() and restore onResume() and onCreate().
You can remove the onBackPressed() in Activity B now
Alternatively, you can use Shared Preferences. When exiting the application, (onBackPressed, onPause ...) save the item you want. For Example the high score of a game.
activity.getSharedPreferences(getString(R.string.app_name),
Context.MODE_PRIVATE).edit().putInt("score", highScore).apply();
And retrieving the score would be:
int value = activity.getSharedPreferences(getString(R.string.app_name),
Context.MODE_PRIVATE).getInt("score", 0);

FragmentTabHost getting empty fragments after popBackStack

I have tried every post in StackOverflow and have not been successful, i have a FragmentTabHost activity with tabs A B C D E
When i go to tab A and then go to tab B everything is ok, but if i return to tab A is blank, then return to tab B is also blank!!
A -> B -> A = Blank -> B = blank
I followed this post to get it working Dynamically changing the fragments inside a fragment tab host?, but the transition between tabs is not working.
I have tried changing my BaseContainerFragment to use getSupportFragmentManager instead of getChildFragmentManager but was unsuccessful, also removing addToBackStack(null) at this point im out of ideas, any help here will be appreciated, thanks.
This is the mainActivity that contain code for creating tabs using fragment.
public class ActivityMain extends FragmentActivity {
public static final String TAB_1_TAG = "tab_1";
public static final String TAB_2_TAG = "tab_2";
public static final String TAB_3_TAG = "tab_3";
public static final String TAB_4_TAG = "tab_4";
public static final String TAB_5_TAG = "tab_5";
private FragmentTabHost mTabHost;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.getTabWidget().setDividerDrawable(null);
mTabHost.getTabWidget().setStripEnabled(false);
mTabHost.addTab(mTabHost.newTabSpec(TAB_1_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_account)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_2_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_discounts)), FragmentPromotionsContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_3_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_payment)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_4_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_gas)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_5_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_rest)), FragmentAccountContainer.class, null);
}
#Override
public void onBackPressed() {
boolean isPopFragment = false;
String currentTabTag = mTabHost.getCurrentTabTag();
Log.e("ActivityMain", "currentTabTag: " + currentTabTag);
if (currentTabTag.equals(TAB_1_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_1_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_2_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_2_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_3_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_3_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_4_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_4_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_5_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_5_TAG)).popFragment();
}
Log.e("ActivityMain", "isPopFragment: " + isPopFragment);
if (!isPopFragment) {
finish();
}
}
}
This is my BaseContainerFragment that allows backtracking and replacment of fragments
public class BaseContainerFragment extends Fragment {
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.replace(R.id.container_framelayout, fragment);
transaction.commit();
getChildFragmentManager().executePendingTransactions();
}
public boolean popFragment() {
Log.e("test", "pop fragment: " + getChildFragmentManager().getBackStackEntryCount());
boolean isPop = false;
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
isPop = true;
getChildFragmentManager().popBackStack();
}
return isPop;
}
}
This is container for the first Tab (this tab holds 2 activities, one is main, and another is called on listview Click)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
new asyncGetFeedClass(context).execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_cardholder, container, false);
toolbar = (Toolbar) v.findViewById(R.id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("TARJETAS");
list = (ListView) v.findViewById(R.id.list);
// Click event for single list row
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
FragmentAccount fragment = new FragmentAccount();
// if U need to pass some data
Bundle bundle = new Bundle();
if (listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID) != null) {
bundle.putString("idBankAccount", listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID));
bundle.putString("idGiftCard", "0");
} else if (listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID) != null) {
bundle.putString("idGiftCard", listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID));
bundle.putString("idBankAccount", "0");
} else {
bundle.putString("idBankAccount", "0");
bundle.putString("idGiftCard", "0");
}
fragment.setArguments(bundle);
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, false);
}
});
return v;
}
The main class for Tab #1
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
Bundle bundle = this.getArguments();
idBankAccount = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_BANKACCOUNTS_ID, "0"));
idGiftCard = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_GIFTCARDS_ID, "0"));
if(idBankAccount > 0){
new asyncGetBankTransactions(context).execute();
} else if(idGiftCard > 0) {
new asyncGetGiftCardTransactions(context).execute();
} else {
new asyncGetX111Transactions(context).execute();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_account, container, false);
toolbar = (Toolbar) v.findViewById(id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("MI CUENTA");
toolbar.setNavigationIcon(R.drawable.icon_user);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
goToCards();
}
});
layoutAccount = (LinearLayout) v.findViewById(id.layoutAccount);
layoutGetCredit = (LinearLayout) v.findViewById(id.layoutGetCredit);
layoutTransactions = (LinearLayout) v.findViewById(id.layoutTransactions);
btnAccount = (Button) v.findViewById(id.btnMyBalance);
btnGetCredit = (Button) v.findViewById(id.btnGetCredit);
btnSendCredit = (Button) v.findViewById(id.btnSendCredit);
btnTransactions = (Button) v.findViewById(id.btnTransactions);
list = (ListView) v.findViewById(id.list);
btnTransactions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.VISIBLE);
}
});
btnGetCredit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.VISIBLE);
layoutTransactions.setVisibility(View.GONE);
}
});
btnAccount.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.VISIBLE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.GONE);
}
});
return v;
}
private void goToCards() {
FragmentCardHolder fragment = new FragmentCardHolder();
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, true);
}
I think the problem is in hidden part of code where you add first fragment to container (FragmentAccountContainer and FragmentPromotionsContainer classes). I suggest you to create abstract method in BaseContainerFragment.class with signature by example
protected abstract Fragment getFirstFragment();
So concrete container class will override this method and return new instance of a first fragment to super class and then in parent class add it to fragment container with using add transaction.
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
addFragment(getFirstFragment(), false);
}
}
Note you should check if savedInstanceState is null before adding fragment to avoid dublicates in case activity recreation by system.
In nested fragments you could use replace like you did it ((BaseContainerFragment) getParentFragment()).replaceFragment(___, true);
Also i have a few suggestions for you code. You couldn't just avoid overriding onBackPressed in activity like #NecipAllef suggests, because of known bug with default back logic and child fragment manager , but you could simplify call to popFragment like
#Override
public void onBackPressed() {
String currentTabTag = mTabHost.getCurrentTabTag();
boolean isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(currentTabTag)).popFragment();
if (!isPopFragment) {
super.onBackPressed();
}
}
And for setting bundles to fragment i suggest use fabric method pattern, like
public class TestFragment extends Fragment {
public static Fragment newInstance(String text){
Fragment fragment = new TestFragment();
Bundle args = new Bundle();
args.putString("text", text);
fragment.setArguments(args);
return fragment;
}
}
Ps: i created for you a simple project with described logic
Why are you keeping track of Fragments and popping them by yourself? You don't need to do that, and you shouldn't override onBackPressed(). Let FragmentManager handle the fragment transactions.
If you have fragments inside an activity, use
FragmentManager fManager = getFragmentManager();
or if you want to support devices prior to Android 3.0, use
FragmentManager fManager = getSupportFragmentManager();
if fragments are inside another fragment, then use
FragmentManager fManager = getChildFragmentManager();
After you have fManager, to show a fragment, use
fManager.beginTransaction().add(R.id.fragment_parent, new FirstTabFragment()).commit();
where fragment_parent is the parent view which you want to place your fragments.
When you want to switch to next fragment, use
fManager.beginTransaction().replace(R.id.fragment_parent, new SecondTabFragment())
.addToBackStack(null)
.commit();
Since you add it to back stack, you will see your first fragment when you press back. That's it.
Moreover, as you can easily realize this will cause your fragments to be created from scratch every time, you can prevent this by initializing them once and reuse them.
HTH

ListView empties after switching a fragment in fragmentHolder

I have a listview in one of my fragments and it empties when I leave that fragment.
Why is it happening?
That fragment:
public class ListActivity extends ListFragment {
public void ToastLoadShout(String msg) {
Toast.makeText(getActivity(), msg.toString(), Toast.LENGTH_LONG).show();
}
private static View View;
HttpClient client;
HttpPost httppost;
HttpGet httpget;
JSONObject json;
List<List<String>> items;
List<item> markers = new ArrayList<item>();
MobileArrayAdapter adapter;
ListView list;
ProgressBar listload;
Button relist;
Preferences pref;
String datadata = "";
String savedlat="0.0";
String savedlon="0.0";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_list, container, false);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
try {
pref = new Preferences(getActivity());
list = (ListView) getView().findViewById(android.R.id.list);
listload = (ProgressBar) getView().findViewById(R.id.listload);
HashMap<String, String> loc = pref.getData();
ToastLoadShout(loc.get(Preferences.LAT) + ","
+ loc.get(Preferences.LON));
if (loc.get(Preferences.LAT) != "0.0" && loc.get(Preferences.LAT) != null)
{
//adapter.deleteList();
//list.destroyDrawingCache();
if (loc.get(Preferences.LAT) != savedlat && loc.get(Preferences.LON)!=savedlon){
new Load().execute();
savedlat=loc.get(Preferences.LAT);
savedlon=loc.get(Preferences.LON);
}
}
else
ToastLoadShout("Get Location First.");
relist = (Button) getView().findViewById(R.id.relist);
relist.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
listload.setVisibility(View.INVISIBLE);
HashMap<String, String> loc = pref.getData();
ToastLoadShout(loc.get(Preferences.LAT) + ","
+ loc.get(Preferences.LON));
if (loc.get(Preferences.LAT) != "0.0" && loc.get(Preferences.LAT) != null){
adapter.deleteList();
list.destroyDrawingCache();
new Load().execute();}
else
ToastLoadShout("Get Location First.");
}});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// get selected items
//String selectedValue = (String) getListAdapter().getItem(position);
String selectedValue = markers.get(position).getTitle();
Toast.makeText(getActivity(), selectedValue, Toast.LENGTH_SHORT).show();
}
}
And the MainActivity which holds the fragments:
public class Fragments extends FragmentActivity {
Fragment newFragment;
Button Add;
public void ToastLoadShout(String msg) {
Toast.makeText(this, msg.toString(), Toast.LENGTH_SHORT).show();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments);
//Set Custom actionBar<
getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getActionBar().setCustomView(R.layout.titlebar);
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
//Set Custom actionBar>
ListActivity fragment = new ListActivity();
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_place, fragment,"Nearby");
transaction.commit();
turnGPSOn();
Add = (Button)findViewById(R.id.add);
Add.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent Intent = new Intent(Fragments.this,Add.class);
Bundle bndlanimation =
ActivityOptions.makeCustomAnimation(getApplicationContext(),
R.anim.animation,R.anim.animation2).toBundle();
startActivity(Intent, bndlanimation);
}
});
/* For putting commas in attractin's checkIns
String number = "1345";
int amount = Integer.parseInt(number);
DecimalFormat formatter = new DecimalFormat("#,###");
ToastLoadShout(formatter.format(amount));*/
}
public void onSelectFragment(View view) {
String fragTag="";
boolean needNew=false;
if (view == findViewById(R.id.map))
{
Fragment f = getSupportFragmentManager().findFragmentByTag("Map");
if (f==null){
newFragment = new MainActivity();
needNew=true;
fragTag="Map";
}
else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_place, f, "Map"); //or whatever other string you want to use
transaction.addToBackStack(null);
transaction.commit();
}
}
else if (view == findViewById(R.id.nearby))
{
Fragment f = getSupportFragmentManager().findFragmentByTag("Nearby");
if (f==null){
newFragment = new ListActivity();
needNew=true;
fragTag="Nearby";
}
else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_place, f, "Nearby"); //or whatever other string you want to use
transaction.addToBackStack(null);
transaction.commit();
}
}
if (needNew) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_place, newFragment, fragTag);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
Already tried loading the list on OnCreate. Doesn't work at all.
Thanks for your assistance.
EDIT:
Load.Class:
class Load extends AsyncTask<String, Integer, Boolean> {
#Override
protected void onPreExecute() {
listload.setVisibility(View.VISIBLE);
}
#Override
protected Boolean doInBackground(String... params) {
try {
items = DownloadList();
if (items != null)
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
#Override
protected void onPostExecute(Boolean res) {
// TODO Auto-generated method stub
if (res) {
ArrangePutMarkers();
adapter=new MobileArrayAdapter(getActivity(), markers);
list.setAdapter(adapter);
} else {
ToastLoadShout("Error");
ToastLoadShout(datadata);
}
listload.setVisibility(View.INVISIBLE);
}
}
As i can see you recreate the loc object every time the fragment is displayed.
It obviously makes you fragment to reload the data.
Keep your adapter in activity as a field. Destroying everything in your fragment when you leave it is a normal thing. setRetainInstance() works only on configuration changes, such as screen rotating. Recreate your list every time you came back in your fragment (in onActivityCreated(), for example, like you do know) and supply it with your stored adapter with saved data in it. You can gain access to your activity inside a fragment, for example, by casting (MainActivity)getActivity(), as an Activity which is passed to this fragment in this case is actually your activity.

How to implement saveFragmentInstanceState?

Could you anyone advice me how to implement saveFragmentInstanceState() or some other methods to retrieving fragment instance when back button is pressed. I use own stack for fragment, viz code bellow:
public class stackA extends ActivityInTab {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("StackA", "onCreate");
if(savedInstanceState == null){
navigateTo(new fragmentA());
}
}
}
Next there is implementation of ActivityInTab class. I think for this class must be implemented methods for saving and retrieving fragment state but still I can't find the way how to do this.
abstract class ActivityInTab extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_tab);
Log.i("ActivityInTab", "onCreate");
}
protected void navigateTo(Fragment newFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
#Override
public void onBackPressed() {
Log.i("ActivityInTab", "onBackPressed");
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 1) {
super.onBackPressed();
} else {
// Otherwise, ask user if he wants to leave :)
//showExitDialog();
super.onBackPressed();
}
}
}
And finally, this is imlementation of Fragments:
public class fragmentA extends Fragment {
private LinearLayout ll;
private FragmentActivity fa;
private String textViewText;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("Fragment A", "onCreateView");
fa = super.getActivity();
ll = (LinearLayout) inflater.inflate(R.layout.fragmenta, container,
false);
Button next = (Button) ll.findViewById(R.id.button1);
Button randomBtn = (Button) ll.findViewById(R.id.random_button);
final TextView randomText = (TextView) ll
.findViewById(R.id.random_textview);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
((ActivityInTab) getActivity()).navigateTo(new
fragmentB());
}
});
randomBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
randomText.setText(String.valueOf(Math.random()));
textViewText = randomText.getText().toString();
}
});
if (savedInstanceState != null) {
randomText.setText(savedInstanceState.getString("TextView"));
}
return ll;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// setRetainInstance(true);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("TextView", textViewText);
}
}
I'm able to save instance state only for orientation changes using onSaveInstanceState(Bundle outState), but no for back button.
I would be very grateful for any advice, thanks.
In the onBackPressed Method you have access to the FragmentManager which can retrieve any fragment for you using the FragmentManager methods "findFragmentById" or "findFragmentByTag".
You can get direct access to any fragment whos state you want to save using either of those two methods depending on how you added the fragments.

Android: fragment restore after screen orientation change

I'm starting with fragments and I'm facing a problem. I wan't to restore my fragment after a screen rotation.
My app looks like this: on landscape I have a button on the left area, which updates the a label on the right area. If on portrait mode, I'm navigating to a new activity. However, I wan't to maintain the fragment state after rotating.
Code looks like this:
Left area fragment:
public class ListFragment extends Fragment implements OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
Button button = (Button) view.findViewById(R.id.button1);
button.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
updateDetail();
break;
}
}
public void updateDetail() {
String newTime = String.valueOf(System.currentTimeMillis());
DetailFragment fragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
if (fragment != null && fragment.isInLayout()) {
fragment.setText(newTime);
} else {
Intent intent = new Intent(getActivity().getApplicationContext(),
DetailActivity.class);
intent.putExtra("value", newTime);
startActivity(intent);
}
}
}
Right area activity:
public class DetailActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
if (savedInstanceState == null) {
setContentView(R.layout.activity_detail);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String s = extras.getString("value");
TextView view = (TextView) findViewById(R.id.detailsText);
view.setText(s);
}
}
}
}
Right area fragment:
public class DetailFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.fragment_detail, container, false);
return view;
}
public void setText(String item) {
TextView view = (TextView) getView().findViewById(R.id.detailsText);
view.setText(item);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
DetailFragment fragment = (DetailFragment)fm.findFragmentById(R.id.detailFragment);
if (fragment == null) {
fragment = new DetailFragment();
fragment.setTargetFragment(this, 0);
fm.beginTransaction().add(R.id.detailFragment, fragment).commit();
}
}
}
What can I possibly be doing wrong?
You have a few options you can use the Bundle in a couple of methods to save and restore the state of the fragments.
Or you can possibly use the setRetainInstance method:
onCreate(Bundle save)
{
super.onCreate(save);
setRetainInstance(true);
}
Keep in mind that the setRetainInstance method doesn't work for fragments in the backstack.
#Override
public void onConfigurationChanged(Configuration newConfig) {
int orientation = getResources().getConfiguration().orientation;
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
getSupportFragmentManager().beginTransaction().replace(R.id.detailFragment, new DetailFragment()).commitAllowingStateLoss();
getSupportFragmentManager().beginTransaction().remove(new DetailFragment()).commitAllowingStateLoss();
break;
case Configuration.ORIENTATION_PORTRAIT:
getSupportFragmentManager().beginTransaction().replace(R.id.detailFragment, new DetailFragment()).commitAllowingStateLoss();
getSupportFragmentManager().beginTransaction().remove(new DetailFragment()).commitAllowingStateLoss();
break;
}
super.onConfigurationChanged(newConfig);
}
You can check on the onConfigurationChange and ensure that your fragment state is not lost by doing a commitAllowingStateLoss

Categories

Resources