onSaveInstanceState null in Fragment everytime - android

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);

Related

How can I finish my fragment of TabLayout?

I have a MainActivity and a HomeTabs with three tabs (A B C), i set a refresh on tab C .
My structure is when i trigger onRefresh on tab C , i will switch to MainActivity
and load the data again to show the HomeTabs.
My problem is when i click back for finish(); , the layout will show tab C.
I try to finish the Fragment use like:
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
or
((MainActivity)getActivity()).removeFragment(getActivity());
Both of them are no working , when i click back , i still can't finish the app immediately.
Some one can teach me what step i miss it , that would be appreciated.
My HomeTabs extends Fragment it use ViewPager and TabLayout add three tabs
MainActivity:
public class MainActivity extends AppCompatActivity {
private FrameLayout frameLayout;
private Toolbar toolBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout = (FrameLayout) findViewById(R.id.frameLayout);
// Load the main Fragment
if (savedInstanceState == null) {
switchFragment(HomeTabs.newInstance());
}
//take the onRefresh dataļ¼Œsend data to HomeTabs and open tab C
if (getIntent().hasExtra("refresh")) {
boolean isRefresh = getIntent().getExtras().getBoolean("refresh");
if (isRefresh) {
Bundle bundle = new Bundle();
bundle.putBoolean("refresh", isRefresh);
HomeTabs homeTabs = new HomeTabs();
homeTabs.setArguments(bundle);
switchFragment(homeTabs);
}
}
}
public void switchFragment(Fragment fragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.frameLayout, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
// I try to finsh my tab C , it's no working
public void removeFragment(Activity activity) {
activity.onBackPressed();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
new DialogHandler(this).quickDialog(
getResources().getString(R.string.quick),
getResources().getString(R.string.confirm),
getResources().getString(R.string.cancel),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// If i onRefrsh three times , i will finsh three times... here is my issue.
finish();
}
}, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
}
return super.onKeyDown(keyCode, event);
}
}
Here is my tab C Fragment refresh call back method:
public class MyLineChart extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my_line_chart, container, false);
//.....................
return view;
}
#Override
public void onRefresh() {
refreshLayout.setRefreshing(false);
// Both of them are no working.
//((MainActivity)getActivity()).removeFragment(getActivity());
//getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
Intent intent = new Intent(getActivity(),MainActivity.class);
intent.putExtra("refresh", true);
startActivity(intent);
}
Finally my HomeTabs Fragment take the date and show tab C:
Bundle bundle = getArguments();
if (bundle != null) {
boolean isRefresh = bundle.getBoolean("refresh");
if (isRefresh) {
//tab C position is 2
tabLayout.getTabAt(2).select();
}
}
Try this solution to add below code when you are starting activity again on Refresh click:-
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
OR
Update your MainActivity.java
1) Add function in MainActivity
public void refreshFragment() {
Bundle bundle = new Bundle();
bundle.putBoolean("refresh", isRefresh);
HomeTabs homeTabs = new HomeTabs();
homeTabs.setArguments(bundle);
switchFragment(homeTabs);
}
Now Call that from Fragment Tab C just replacing startActivity(refresh) code:
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.refreshFragment();
you can add a listener in your fragment that can trigger a function in your parent activity
which means you need to add an interface in your fragmentC code
public class MyLineChart extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private MyLineChartListener fragmentListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my_line_chart, container, false);
//.....................
return view;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MyLineChartListener) {
fragmentListener = (MyLineChartListener) context;
} else {
// throw an error
}
}
#Override
public void onRefresh() {
refreshLayout.setRefreshing(false);
// Both of them are no working.
//((MainActivity)getActivity()).removeFragment(getActivity());
//getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
fragmentListener.onSettingRefresh();
}
public interface MyLineChartListener {
void onSettingRefresh();
}
}
then you need to implement the listener in the MainActivity code as follows
public class MainActivity extends AppCompatActivity implements MyLineChart.MyLineChartListener {
private FrameLayout frameLayout;
private Toolbar toolBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout = (FrameLayout) findViewById(R.id.frameLayout);
// Load the main Fragment
if (savedInstanceState == null) {
switchFragment(HomeTabs.newInstance());
}
}
public void switchFragment(Fragment fragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.frameLayout, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
// I try to finsh my tab C , it's no working
public void removeFragment(Activity activity) {
activity.onBackPressed();
}
// this function will be called when the fragment is refreshed
#Override
public void onSettingRefresh() {
Bundle bundle = new Bundle();
bundle.putBoolean("refresh", true);
HomeTabs homeTabs = new HomeTabs();
homeTabs.setArguments(bundle);
switchFragment(homeTabs);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
new DialogHandler(this).quickDialog(
getResources().getString(R.string.quick),
getResources().getString(R.string.confirm),
getResources().getString(R.string.cancel),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// If i onRefrsh three times , i will finsh three times... here is my issue.
finish();
}
}, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
}
return super.onKeyDown(keyCode, event);
}
}

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

Android: NullPointerException when communicating between fragments

Android newbie here. My app crashes when trying to send text from one fragment to another. It says:
java.lang.NullPointerException
at com.example.hang.camera.Camera.sendResults(Camera.java:57)
at com.example.hang.camera.PageOneFragment.sendMethod(PageOneFragment.java:320)
at com.example.hang.camera.PageOneFragment$1.onClick(PageOneFragment.java:104)
Here is my code:
// Activity that implements the interface from fragment A
Camera.java:
public class Camera extends ActionBarActivity implements PageOneFragment.OnComputeResultListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mViewPager = (CustomViewPager)findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter.addTab(mTabHost.newTabSpec("one").setIndicator("One"), PageOneFragment.class, null);
mTabsAdapter.addTab(mTabHost.newTabSpec("two").setIndicator("Two"), PageTwoFragment.class, null);
if (savedInstanceState != null)
{
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
mViewPager.setPagingEnabled(false);
}
#Override
public void sendResults(String text){
PageTwoFragment fragmentTwo = (PageTwoFragment)getSupportFragmentManager().findFragmentById(R.id.second_fragment);
fragmentTwo.updateText(text);
}
// fragment A:
PageOneFragment.java
OnComputeResultListener mCallback;
public interface OnComputeResultListener{
public void sendResults(String text);
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try{
mCallback = (OnComputeResultListener) activity;
} catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+ " must implement OnComputeResultListener");
}
}
public void sendMethod(){
mCallback.sendResults("the results");
}
// in onCreateView, I have this onclicklistener, and I call "sendMethod" here
lengthButton.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View v){
double length ;
line_x = imageview.x_getter();
line_y = imageview.y_getter();
length = Math.sqrt(Math.pow((line_x[0]-line_x[1]),2) + Math.pow((line_y[0]-line_y[1]),2));
line_length.setText("Length of the line is: " + String.format("%.2f",length) + "dp");
sendMethod();
}
}
);
// fragment B
PageTwoFragment.java
private TextView resultText = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.pagetwo_fragment, container, false);
resultText = (TextView) view.findViewById(R.id.resultText);
return view;
}
public void updateText(String text){
resultText.setText(text);
}
I finally solved the problem. In my camera.java where I implemented "sendResults" method of the interface, I changed the parameter of "findFragmentById" to the id of the viewPager instead of the id of the fragment's xml file.
However, from Android developer doc, for findFragmentById, it does say:
Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction.
So, I don't know why the xml id didn't work.
You can save your Fragment as an object in your Activity class.
In the activity class before doing the fragment transaction, do the following:
PageTwoFragment fragmentTwo = new PageTwoFragment();
// do the transaction:
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragmentTwo);
ft.commit();
then later in code, just call
fragmentTwo.updateText(...);

Maintaining fragments instance state between transactions

From all the searches I have found on SO stating that you should save your instance state in the #Override public void onSaveInstanceState(Bundle outState)
However This is tightly coupled with the activities lifestyle.
How can I save the state of my listview in a fragment that gets swapped out with another fragment.
I have one main activity which all the fragments are loaded into.
I have tried this so far:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save adapter data so that when the fragment is returned to it can be resused.
ArrayList<CategoryMobileDto> categories = new ArrayList<CategoryMobileDto>();
for(int i=0; i < adapter.getCount();i++)
{
categories.add(adapter.getItem(i));
}
String persistData = new Gson().toJson(categories);
outState.putString("Categories", persistData);
}
and then in my OnCreate();
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
however savedInstanceState is always null. But this makes sense as my activity is not being destroyed and recreated.
This is how I transition from one fragment to another:
fragment.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, fragment, "ProductListFragment");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Is there a way i can save the state of my listview when the fragment is removed and then restore it again when the fragment is popped from the back-stack?
Move this code from onCreate() to onActivityCreated() of Fragment
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
If you have any query please let me know.
You can use the Arguments with the Fragment(Only if you have the data to show in fragment before the fragment is loaded means attached). You can setArguments to a fragment which will be persisted when you go to another fragment by fragment transaction and when you come back, load the fragment from the getArguments function.
public void setArguments (Bundle args)
Added in API level 11
Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
public final Bundle getArguments ()
Added in API level 11
Return the arguments supplied when the fragment was instantiated, if any.
Please find the sample code below for passing data between fragments :
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
MainActivity.java
public class MainActivity extends Activity implements IFragContainer {
private static final String FRAG_TAG = "FragTag";
private FragBase mFrag;
private String dataToBePassedBack;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
changeFragment(FragA.class, "Data to Frag A");
}
#Override
public void changeFragment(Class<? extends FragBase> fragClass, String data) {
try {
FragmentTransaction ft = getFragmentManager().beginTransaction();
mFrag = fragClass.newInstance();
Bundle args = new Bundle();
args.putString("DATA", data);
mFrag.setArguments(args);
ft.replace(R.id.flContainer, mFrag, FRAG_TAG);
ft.addToBackStack(mFrag.toString());
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBackPressed() {
dataToBePassedBack = mFrag.getDataToPassBack();
FragmentManager mgr = getFragmentManager();
mgr.executePendingTransactions();
boolean doCheckAndExit = true;
for (int i = mgr.getBackStackEntryCount() - 1; i > 0; i--) {
BackStackEntry entry = mgr.getBackStackEntryAt(i);
if (!TextUtils.isEmpty(entry.getName())) {
mgr.popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
doCheckAndExit = false;
break;
}
}
if (doCheckAndExit) {
finish();
} else {
mFrag = (FragBase) mgr.findFragmentByTag(FRAG_TAG);
}
}
#Override
public String getDataToBePassedBack() {
return dataToBePassedBack;
}
}
IFragContainer.java
public interface IFragContainer {
void changeFragment(Class<? extends FragBase> fragClass, String data);
String getDataToBePassedBack();
}
FragBase.java
public abstract class FragBase extends Fragment {
public String getDataToPassBack(){
return null;
}
}
FragA.java
public class FragA extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
final IFragContainer fragContainer = (IFragContainer) getActivity();
if (TextUtils.isEmpty(fragContainer.getDataToBePassedBack())) {
btn.setText(getArguments().getString("DATA"));
} else {
btn.setText(fragContainer.getDataToBePassedBack());
}
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fragContainer.changeFragment(FragB.class, "Data to Frag B");
}
});
return btn;
}
}
FragB.java
public class FragB extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
btn.setText(getArguments().getString("DATA"));
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
});
return btn;
}
#Override
public String getDataToPassBack() {
return "Data from Frag B to A";
}
}

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.

Categories

Resources