Save state of StreetViewFragment when orientation changes - android

I am trying to save the current position of the StreetViewPanorama in the StreetViewFragment so for example if the user is originally in Portrait and changes the location in the StreetViewPanorama by tapping on the arrows and explore the area and then switches to Landscape I want to set the last location to be restored so the user can continue exploring the area. Currently, the streetViewPanorama is Null when I try to access "streetViewPanorama.getLocation()" but this is not the problem. I think that my way of saving the state is not right and there should be a better way. So I am asking for your suggestions, please!
This is my StreetViewFragment's code:
public class StreetViewFragment extends ViperFragment implements OnStreetViewPanoramaReadyCallback,
PropertyDetailsStreetView {
private static final String KEY_PROPERTY_ID = "property_id_street_view";
private static final String KEY_PROPERTY_LAT = "property_lat";
private static final String KEY_PROPERTY_LON = "property_lon";
private Bundle savedInstances;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
streetViewPanoramaFragment = (StreetViewPanoramaFragment)
getActivity().getFragmentManager().findFragmentById(R.id.streetviewpanorama);
streetViewPanoramaFragment.getStreetViewPanoramaAsync(this);
savedInstances = savedInstanceState;
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putDouble(KEY_PROPERTY_LAT, streetViewPanorama.getLocation().position.latitude);
outState.putDouble(KEY_PROPERTY_LON, streetViewPanorama.getLocation().position.longitude);
super.onSaveInstanceState(outState);
}
#Override
public void onStreetViewPanoramaReady(StreetViewPanorama streetViewPanorama) {
this.streetViewPanorama = streetViewPanorama;
presenter.onStreetViewPanoramaReady(getArguments().getLong(KEY_PROPERTY_ID));
if (savedInstances != null) {
this.streetViewPanorama.setPosition(
new LatLng(savedInstances.getLong(KEY_PROPERTY_LAT),
savedInstances.getLong(KEY_PROPERTY_LON)));
}
}

After giving this a bit more tinkering I came with the following solution wich saves the state of the current position of the street view fragment and restores it. Works good.
public class StreetViewFragment extends ViperFragment implements OnStreetViewPanoramaReadyCallback,
PropertyDetailsStreetView {
private static final String KEY_PROPERTY_ID = "property_id_street_view";
private static final String KEY_PROPERTY_LAT = "property_lat";
private static final String KEY_PROPERTY_LON = "property_lon";
private StreetViewPanoramaFragment streetViewPanoramaFragment;
private StreetViewPanorama streetViewPanorama;
private Bundle localBundle;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
streetViewPanoramaFragment = (StreetViewPanoramaFragment)
getActivity().getFragmentManager().findFragmentById(R.id.streetviewpanorama);
streetViewPanoramaFragment.getStreetViewPanoramaAsync(this);
this.localBundle = savedInstanceState;
}
#Override
public void onSaveInstanceState(Bundle outState) {
if (streetViewPanoramaFragment != null) {
outState.putDouble(KEY_PROPERTY_LAT,
streetViewPanorama.getLocation().position.latitude);
outState.putDouble(KEY_PROPERTY_LON,
streetViewPanorama.getLocation().position.longitude);
}
super.onSaveInstanceState(outState);
}
#Override
public void onStreetViewPanoramaReady(StreetViewPanorama streetViewPanorama) {
this.streetViewPanorama = streetViewPanorama;
presenter.onStreetViewPanoramaReady(getArguments().getLong(KEY_PROPERTY_ID));
if (localBundle != null) {
this.streetViewPanorama.setPosition(
new LatLng(localBundle.getDouble(KEY_PROPERTY_LAT),
localBundle.getDouble(KEY_PROPERTY_LON)));
}
}

If it extends Fragment class, then what about using setRetainInstance method? It allows you to save necessary variable(s) not destoying your fragment when device rotates. You can read about usage of this method from my article:
Simple trick to use and manage Toolbar with Fragments in Android

Related

Why does pressing back from detail activity after landscape-to-portrait-switch show an empty screen?

Below is the MainActivity class that I'm using. The code checks to see if the phone is in landscape or portrait. If it's in portrait, it will show the main fragment in the main activity only (the main fragment is a static fragment in the main_activity.xml file). Then if a "Recipe" is clicked it will open a detail activity with its own fragment. If the phone is in landscape mode, it will show the main fragment and the detail fragment side by side. Everything works perfectly fine however when I follow the procedure below I get a white screen instead of the main activity:
Procedure:
Switch to landscape
Switch back to portrait
Choose an item and wait for the detail activity to open
Press back
Here instead of the main activity window I get a white screen
If I don't switch to landscape and just start with the portrait mode everything is fine. It seems like switching to landscape does something that causes the problem and I can't figure out what. Any tip on what's going on or where to look would be much appreciated.
public class MainActivity extends AppCompatActivity implements RecipesFragment.OnRecipeClickListener {
private String RECIPE_PARCEL_KEY;
private boolean mTwoPane;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RECIPE_PARCEL_KEY = getString(R.string.ParcelKey_RecipeParcel);
if (findViewById(R.id.linearLayoutTwoPane) != null) {
mTwoPane = true;
if (savedInstanceState == null) {
RecipeFragment recipeFragment = new RecipeFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.recipeFrameForTwoPane, recipeFragment)
.commit();
}
} else {
mTwoPane = false;
}
}
#Override
public void OnRecipeClick(Recipe recipe) {
if (mTwoPane) {
RecipeFragment recipeFragment = new RecipeFragment();
recipeFragment.setRecipe(recipe);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.recipeFrameForTwoPane, recipeFragment)
.commit();
} else {
Class destinationClass = DetailActivity.class;
Intent intentToStartDetailActivity = new Intent(this, destinationClass);
intentToStartDetailActivity.putExtra(RECIPE_PARCEL_KEY, recipe);
startActivity(intentToStartDetailActivity);
}
}
}
EDIT:
Adding RecipeFragment's code below:
public class RecipeFragment extends Fragment {
private Recipe mRecipe;
#BindView(R.id.tv_recipeName) TextView recipeNameTextView;
public RecipeFragment(){
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recipe_fragment,container,false);
ButterKnife.bind(this,view);
if(mRecipe!=null) {
recipeNameTextView.setText(mRecipe.getName());
}else{
recipeNameTextView.setText(getString(R.string.messageSelectARecipe));
}
return view;
}
public void setRecipe(Recipe recipe){
mRecipe = recipe;
}
}
EDIT:
I followed #mt0s's advice and created different background colors for the fragments and activities and finally narrowed down the problem to a line in my recyclerview adapter code. My adapter code is below. Inside loadInBackground() on line URL url = new URL(getString(R.string.URL_RecipeJSON)); I get a Fragment RecipesFragment{96e9b6a} not attached to Activity exception. I don't understand why I'm getting this exception and what the best way to resolve this is. Have I placed the right code in the right fragment methods (ie OnCreate vs OnActivityCreated vs OnCreateView vs etc)?
public class RecipesFragment extends Fragment
implements RecipeAdapter.RecipeAdapterOnClickHandler,
LoaderManager.LoaderCallbacks<ArrayList<Recipe>> {
#BindView(R.id.rv_recipes) RecyclerView mRecyclerView;
private RecipeAdapter mRecipeAdapter;
private static final int LOADER_ID = 1000;
private static final String TAG = "RecipesFragment";
private OnRecipeClickListener mOnRecipeClickListener;
public RecipesFragment(){
}
public interface OnRecipeClickListener {
void OnRecipeClick(Recipe recipe);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recipes_fragment, container, false);
ButterKnife.bind(this, view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mRecipeAdapter = new RecipeAdapter(this);
mRecyclerView.setAdapter(mRecipeAdapter);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void OnClick(Recipe recipe) {
mOnRecipeClickListener.OnRecipeClick(recipe);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
mOnRecipeClickListener = (OnRecipeClickListener) context;
} catch (ClassCastException e){
Log.e(TAG, "onAttach: Host activity class must implement OnRecipeClickListener.");
}
}
#Override
public Loader<ArrayList<Recipe>> onCreateLoader(int i, Bundle bundle) {
return new AsyncTaskLoader<ArrayList<Recipe>>(getActivity()) {
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad();
}
#Override
public ArrayList<Recipe> loadInBackground() {
String response;
ArrayList<Recipe> recipes = null;
try {
URL url = new URL(getString(R.string.URL_RecipeJSON)); //***I get an exception here***
response = NetworkUtils.getResponseFromHttpUrl(url, getActivity());
recipes = RecipeJsonUtils.getRecipeFromJson(getActivity(), response);
} catch (Exception e) {
Log.e(TAG, "loadInBackground: " + e.getMessage());
}
return recipes;
}
};
}
#Override
public void onLoadFinished(Loader<ArrayList<Recipe>> loader, ArrayList<Recipe> recipes) {
mRecipeAdapter.setRecipeData(recipes);
}
#Override
public void onLoaderReset(Loader<ArrayList<Recipe>> loader) {
}
}
I finally figured out the problem and the solution. The problem is that onStartLoading() in the AsyncTaskLoader anonymous class in RecipesFragment class gets called every time the fragment is resumed whether the enclosing Loader is called or not. This causes the problem. I need to have control over when onStartLoading() is being called and I only want it to be called if and only if the enclosing Loader is being initialized or restarted. As such, I destroyed the loader in onPause() of the fragment and restarted it in onResume(). Hence, I added the following code to the RecipesFragment class:
#Override
public void onPause() {
super.onPause();
getLoaderManager().destroyLoader(LOADER_ID);
}
#Override
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
I also removed initLoader() from onCreate(). This way, every time the fragment is resumed (or created) onStartLoading() will be called. I tried this and it solves my problem.
When you switch from the landscape to portrait or the opposite the Android OS destroy your activity and recreate it again. this what probably trigger your problem

How can I save my data on phone orientation?

I'm having difficulties with making my app persistent. When I rotate my phone the data on the screen doesnt change. But after I click on a button to retrieve a new fragment I get an error saying "Can not perform this action after onSaveInstanceState". I have googled and seen similiar problems but I still dont know how to approach and solve this.
I have an activity class, a controller class and two fragment classes.
The activity class has a navigationviewer with 2 buttons that triggers a fragmenttransaction. That is, on each button click it will replace the current fragment with the one set in the button listener. My controller class initalizes the system and the fragments are just the UI.
My activity class:
public class LoggedInActivity extends AppCompatActivity {
private final String TAG = "LoggedInActivity: ";
private Controller controller;
private TextView navName;
private NavigationView navigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logged_in);
if(savedInstanceState == null) {
Log.v(TAG, "savedInstanceState == null");
initComponents();
setNavName();
initListener();
initializeSystem();
} else {
Log.v(TAG, "savedInstanceState != null");
initComponents();
setNavName();
initListener();
this.controller = (Controller)savedInstanceState.getSerializable("controller");
}
}
private void initComponents() {
navigationView = (NavigationView) findViewById(R.id.navigation_view);
View headerView = navigationView.getHeaderView(0);
navName = (TextView) headerView.findViewById(R.id.tv_name_surname);
}
private void initListener() {
navigationView.setNavigationItemSelectedListener(new MyNavigationItemListener());
}
private void initializeSystem() {
Log.v(TAG, "new controller");
controller = new Controller(this, null);
}
public void setFragment(Fragment fragment) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container_logged_in, fragment).commit();
}
private class MyNavigationItemListener implements NavigationView.OnNavigationItemSelectedListener {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.drawer_summary:
controller.setFragmentSummary();
break;
case R.id.drawer_income:
controller.setFragmentIncome();
break;
}
return false;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putSerializable("controller", controller);
super.onSaveInstanceState(outState);
Log.v(TAG, "onSaveInstanceState, saving the controller");
}
}
My controller class:
public class Controller implements Serializable {
private final String TAG = "Controller: ";
/********************** Fragments ***********************/
private Fragment_Income fragment_income;
private Fragment_Summary fragment_summary;
/********************************************************/
/********************** Activities **********************/
private LoggedInActivity logged_in_activity;
/********************************************************/
public Controller(LoggedInActivity logged_in_activity) {
this.logged_in_activity = logged_in_activity;
initLoggedInFragments();
setFragmentSummary();
}
}
/* Initializes fragments that are connected to LoggedInActivity */
private void initLoggedInFragments() {
fragment_income = new Fragment_Income();
fragment_income.setController(this);
fragment_summary = new Fragment_Summary();
fragment_summary.setController(this);
}
/* use to replace current fragment with the given one */
private void replaceFragmentWith(Fragment fragment) {
logged_in_activity.setFragment(fragment);
}
/***********************************************************
* METHODS REGARDING FRAGMENT INCOME *
**********************************************************/
public void setFragmentIncome() {
replaceFragmentWith(fragment_income);
}
/* Summary fragment is started at first */
public void setFragmentSummary() {
replaceFragmentWith(fragment_summary);
}
}
Fragment_Income:
public class Fragment_Income extends Fragment implements Serializable{
private final String TAG = "Fragment_Income: ";
private Controller controller;
private FloatingActionButton fab_income;
private ListView lv_income;
private ArrayList<LvData> incomeData;
private LvAdapterIncome lvAdapterIncome;
public Fragment_Income() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.v(TAG, "onCreateView");
View view = inflater.inflate(R.layout.fragment_income, container, false); // Inflate the layout for this fragment
if(savedInstanceState != null) {
this.controller = (Controller) savedInstanceState.getSerializable("controller");
}
initComponents(view);
initListener();
setupListView();
return view;
}
private void initComponents(View view) {
fab_income = (FloatingActionButton) view.findViewById(R.id.fab_income);
lv_income = (ListView) view.findViewById(R.id.lv_income);
}
private void initListener() {
ButtonListener buttonListener = new ButtonListener();
fab_income.setOnClickListener(buttonListener);
}
private void setupListView() {
if (incomeData == null) { // checks if incomeData have been initalized before, if so do not change array to defualt
incomeData = new ArrayList<>();
lvAdapterIncome = new LvAdapterIncome(getContext(), incomeData);
}
lv_income.setAdapter(lvAdapterIncome);
}
public void setController(Controller controller) {
this.controller = controller;
}
#Override
public void onSaveInstanceState(Bundle outState) {
Log.v(TAG, "onSaveInstanceState, saving the controller");
outState.putSerializable("controller", this.controller);
super.onSaveInstanceState(outState);
}
}
Fragment_Summary:
public class Fragment_Summary extends Fragment implements Serializable {
private static final String TAG = "Fragment_Summary: ";
private Controller controller;
private TextView tv_user;
private TextView tv_total_revenue;
private TextView tv_total_expenditure;
private TextView tv_balance;
private float totalRevenue;
private float totalExpenditure;
private float balance;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_summary, container, false);// Inflate the layout for this fragment
initComponents(view);
setUserName();
if(savedInstanceState == null) {
//DO SOMETHING
}
return view;
}
private void addData() {
totalRevenue = controller.getTotalRevenue();
totalExpenditure = controller.getTotalExpenditure();
balance = totalRevenue - totalExpenditure;
tv_total_revenue.setText(String.valueOf(totalRevenue));
tv_total_expenditure.setText(String.valueOf(totalExpenditure));
tv_balance.setText(String.valueOf(balance));
}
private void initComponents(View view) {
tv_user = (TextView)view.findViewById(R.id.tv_user);
tv_total_revenue = (TextView)view.findViewById(R.id.tv_revenue);
tv_total_expenditure = (TextView)view.findViewById(R.id.tv_sum_exp);
tv_balance = (TextView)view.findViewById(R.id.tv_balance);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("revenue", String.valueOf(balance));
outState.putString("totalExpenditure", String.valueOf(balance));
outState.putString("balance", String.valueOf(balance));
super.onSaveInstanceState(outState);
}
public void setController(Controller controller) {
this.controller = controller;
}
}
I have removed all the header files and some methods from my classes becuase I tought they were not relevant for this problem.
Here is the error log:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1434)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1452)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:708)
at android.app.BackStackRecord.commit(BackStackRecord.java:672)
at com.example.user.my_app.LoggedInActivity.setFragment(LoggedInActivity.java:85)
at com.example.user.my_app.Controller.replaceFragmentWith(Controller.java:89)
at com.example.user.my_app.Controller.setFragmentIncome(Controller.java:99)
at com.example.user.my_app.LoggedInActivity$MyNavigationItemListener.onNavigationItemSelected(LoggedInActivity.java:127)
at android.support.design.widget.NavigationView$1.onMenuItemSelected(NavigationView.java:156)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:156)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:969)
at android.support.design.internal.NavigationMenuPresenter$1.onClick(NavigationMenuPresenter.java:342)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
This looks like an activity state loss. See this excellent article by Alex Lockwood entitled "Fragment Transactions & Activity State Loss". I refer to it time and again.
To quote the intro to the posting:
The following stack trace and exception message has plagued StackOverflow ever since Honeycomb’s initial release:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
This post will explain why and when this exception is thrown, and will conclude with several suggestions that will help ensure it never crashes your application again.

send intent from main activity to two fragment

I have 2 Fragment and I have to send some id to the Fragment. I use this:
public void onItemLongClick(View view, int position) {
FragmentManager fm = getSupportFragmentManager();
actionOption actionOption = new actionOption();
actionOption.show(fm,"fragment_edit_name");
ToDoModule movie = dbList.get(position);
int y= movie.getId();
Bundle args = new Bundle();
args.putInt("exampleInt", y);
actionOption.setArguments(args);
EditOption editOption = new EditOption();
ToDoModule bl = dbList.get(position);
int z= movie.getId();
Bundle zs = new Bundle();
zs.putInt("int", y);
editOption.setArguments(zs);
}
First Fragment is working, but the second is not sent. Cannot send value to EditOption?
How to solve it?
Its very unusual that, you're trying to pass some data to two Fragment at the same time. It would be great if you could write the situation you have there in brief in your question.
Anyway, #PrerakSola came up with a solution for saving the data you want to pass in a SharedPreference and I do think it should work in your case.
You're trying to pass a movie id to actionOption as well as to editOption. You might try to store the id first in a SharedPreference like this.
From your Activity
public void onItemLongClick(View view, int position) {
// ... Your code
// Save the movie id
SharedPreferences pref = getSharedPreferences("MY_APPLICATION", MODE_PRIVATE);
pref.edit().putInt("MOVIE_ID", movie.getId()).commit();
// Do not pass any bundle to the Fragment. Just transact the Fragment here
}
Now from your Fragment's onCreateView fetch the value from preference.
SharedPreferences pref = getActivity().getSharedPreferences("MY_APPLICATION", MODE_PRIVATE);
String movieID = pref.getInt("MOVIE_ID", 0);
Another way you might try to have a public static int variable which might contain the movie id and you can access it from anywhere from your code.
Hope that helps!
Something like this , you can do it
public interface SetData {
public void data(String id);
}
From your activity class or on item click listner
SetData setData;
setData.setDrawerEnabled("anydata");
Infragment , YourFragment extends Fragment implements SetData
hi yesterday i have done same thing and how it work, i'll give you idea.
It already answered but just i want to share my experiance.This way is perfect.
First of all create two interfaces in your activity,
public interface TaskListener1 {
public void onResultAvailable(String result);
}
public interface TaskListener2 {
public void onResultAvailable(String result);
}
Now come to your activity then call like this where you want to send data to fragment.I'm just giving you example.You can make it as you want.
class TestAsyncTask extends AsyncTask<Void, String, Void> {
String response_result;
public TaskListener1 taskListener1 = null;
public TaskListener2 taskListener2 = null;
public TestAsyncTask(TaskListener1 taskListener1, TaskListener2 taskListener2) {
this.taskListener1 = taskListener1;
this.taskListener2 = taskListener2;
}
#Override
protected Void doInBackground(Void... unused) {
response_result = "Test data what you want to send";
return null;
}
#Override
protected void onPostExecute(Void unused) {
taskListener1.onResultAvailable(response_result);
taskListener2.onResultAvailable(response_result);
}
}
Call like this,
new TestAsyncTask(new Fragment1), new Fragment2)).execute();
And how to get data in fragment,
First fragment,
public class Fragment1 extends Fragment implements YourActivity.TaskListener1 {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment1, container, false);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onResultAvailable(String result) {
Logs.d("TAG", "Fragment result1:" + result);
}
}
Second fragment,
public class Fragment2 extends Fragment implements YourActivity.TaskListener2 {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment2, container, false);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onResultAvailable(String result) {
Logs.d("TAG", "Fragment result2:" + result);
}
}
Thanks hope this will help somebody.

Parcelable from activity to fragment

I am new to parcelable and I am trying to pass data from an Activity (MainActivity) to a fragment (MainFragment) but I`m struggle to get this right.
I made a class (InfoBean) with all the (parcelable) data. When I send the data from the MainActivity, the data from bean.newTheme (2131296447) is there but as soon as I try to retrieve in the Fragment, the value is 0!
Could someone pls have a look, what I`m doing wrong? Thank you for your help.
Send data (MainActivity):
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
InfoBean bean = new InfoBean();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SecureSharedPreferences theme = SecureSharedPreferences.getInstance(this, "MyPrefsFile");
int newTheme = theme.getInt("themeCustom", 0);
bean.newTheme = newTheme;
Bundle bundle = new Bundle();
bundle.putInt("theme", bean.newTheme); // debug shows value 2131296447
MainFragment mf = new MainFragment();
mf.setArguments(bundle);
//
}
}
Retrieve data (MainFragment):
public class MainFragment extends Fragment {
InfoBean bean = new InfoBean();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//
Bundle bundle = this.getArguments(); // Debugging shows 0!
if (bundle != null) {
bean.newTheme = bundle.getInt("theme");
}
if (bean.newTheme == 2131296447) { // White Theme
mCardView1.setBackgroundColor(Color.parseColor("#E8EBED"));
} else { // Dark Theme
mCardView1.setBackgroundColor(Color.parseColor("#282929"));
relLay.setBackgroundColor(Color.parseColor("#1B1C1C"));
}
return rootView;
}
}
InfoBean.class:
public class InfoBean implements Parcelable {
public int newTheme;
public int THEME_DARK = R.style.DarkTheme;
public int THEME_LIGHT = R.style.LightTheme;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.newTheme);
dest.writeInt(this.THEME_DARK);
dest.writeInt(this.THEME_LIGHT);
}
public InfoBean() {
}
protected InfoBean(Parcel in) {
this.newTheme = in.readInt();
this.THEME_DARK = in.readInt();
this.THEME_LIGHT = in.readInt();
}
public static final Parcelable.Creator<InfoBean> CREATOR = new Parcelable.Creator<InfoBean>() {
#Override
public InfoBean createFromParcel(Parcel source) {
return new InfoBean(source);
}
#Override
public InfoBean[] newArray(int size) {
return new InfoBean[size];
}
};
}
If you have embedded fragment in your XML you can't use the setArguments() like that in your program. It is better to use dynamic fragment creation.
There is a brief example in android developer website which can guide you: http://developer.android.com/reference/android/app/Fragment.html there is also another implementation when you have embedded fragments and how to process arguments with that.
There is also another resource here which may help you:
Set arguments of fragment from activity
In your activity, you are using .putInt("theme" .....) but in the fragment you call .getParcelable("theme"). You're getting 0 because you're attempting to get two different data types.
Update
Since you have your fragment embedded in xml, you can't pass values to fragment class. To that you need to make it excute through java code and remove that xml. Make fragment Transaction then it will work
Update
You should try retrieving values in onCreate method of fragment.
#overide
protect void onCreate(bundle onSavedInstance){
if (savedInstance != null) {
bean.newTheme = bundle.getInt("theme");
}
}
Try this
if (bundle != null) {
bean.newTheme = bundle.getInt("theme");
}
instead of
if (bundle != null) {
bean.newTheme = bundle.getParcelable("theme");
}

onSharedPreferenceChange Listener -> In Fragment

Hey I'm trying to add an onSharedPreference Listener into n Fragment. It should regulate if a change is done (switch in the settings) the value of the NumberPicker inside the Fragment changes(0 or 1) too.
the problem with the listener: it only works the first 5-10 times then it doesnt get called anymore (i suppose)? -> no changes done on NumberPicker
following the importants code of my fragment:
public class FragmentTwo extends Fragment {
private EditText mEnterWeight;
private NumberPicker mUnitPicker;
private TextView mConverted;
private int pick;
private String convertedWeightMessage;
private Double enteredWeight;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_two, container, false);
mEnterWeight = (EditText) view.findViewById(R.id.WCenterWeight);
mUnitPicker = (NumberPicker) view.findViewById(R.id.WCunitPicker);
mConverted = (TextView) view.findViewById(R.id.WCconverted);
initialiseUnitPicker();
//some stuff
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// listener implementation
if(key.equals("SwitchMainUnit")) {
Boolean kg = prefs.getBoolean("SwitchMainUnit", true);
if(kg)
mUnitPicker.setValue(0);
else mUnitPicker.setValue(1);
}
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
return view;
}
The docs say that i need to change the onResume() and onPause() to:
#Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
But where do i need to add these? Got errors when i tried to add it in the Fragment.
Thanks for helping; didnt come to any solution myself.. :/
The documentation says
Caution: When you call registerOnSharedPreferenceChangeListener(), the
preference manager does not currently store a strong reference to the
listener
I recommend that you store your instance of SharedPreferences.OnSharedPreferenceChangeListener listener as a field outside of onCreateView.
Example code:
public class FragmentTwo extends Fragment {
private EditText mEnterWeight;
private NumberPicker mUnitPicker;
private TextView mConverted;
private int pick;
private String convertedWeightMessage;
private Double enteredWeight;
SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// listener implementation
if (key.equals("SwitchMainUnit")) {
Boolean kg = prefs.getBoolean("SwitchMainUnit", true);
if (kg) mUnitPicker.setValue(0);
else mUnitPicker.setValue(1);
}
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_two, container, false);
mEnterWeight = (EditText) view.findViewById(R.id.WCenterWeight);
mUnitPicker = (NumberPicker) view.findViewById(R.id.WCunitPicker);
mConverted = (TextView) view.findViewById(R.id.WCconverted);
initialiseUnitPicker();
//some stuff
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefs.registerOnSharedPreferenceChangeListener(listener);
return view;
}

Categories

Resources