Infinite loop on settheme() - android

I am trying to change the app theme from the fragment using setTheme() method of an activity. But somehow its looping forever while trying to change. Here is my setTheme() method calls.
//XYZ Fragment class
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
if (mThemeName.equalsIgnoreCase("Mode 1")) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
getActivity().setTheme(R.style.Theme_One);
} else {
getActivity().setTheme(R.style.Theme_Two);
}
super.onViewCreated(view, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//binding object of this fragment
binding = FragmentXYZBinding.inflate(inflater);
binding.switch.setOnCheckedChangeListener((buttonView, isChecked) -> {
if(isChecked){
setTheme("Mode 2");
}else {
setTheme("Mode 1");
}
});
return inflater.inflate(R.layout.fragment_setting, container, false);
}
public void setTheme(String name) {
SharedPreferences preferences = getActivity().getSharedPreferences("app", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("ThemeName", name);
editor.apply();
// If i call this recreate() then it goes into infinite loop
// but without calling this I can't change theme on runtime
//getActivity().recreate();
}
Any idea about what could be wrong in here ?

Related

Saving state of Fragment on View with LiveData

Im saving the state of fragment on a View, it works in the way I expected, the problem is with the LiveData. When I navigate to other fragment and then come back to this fragment, I cant no longer observer for changes. This is the code of my OnCreateView():
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(view == null){
view = inflater.inflate(R.layout.fragment, container, false);
initializeViews(view);
receivedProductViewModel.getProductMutableLiveData().observe(getViewLifecycleOwner(), new Observer<DispatchedProduct>() {
#Override
public void onChanged(DispatchedProduct dispatchedProduct) {
if(dispatchedProduct != null){
int index = dispatchedProducts.indexOf(dispatchedProduct);
dispatchedProductsAdapter.notifyItemChanged(index);
}
}
});
btnSearch.setOnClickListener(btnSearchListener());
btnRegister.setOnClickListener(btnRegisterListener());
initializeRV();
}
return view;
}
I'm initializing the VM in OnCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
receivedProductViewModel = new ViewModelProvider(getActivity()).get(ReceivedProductViewModel.class);
}
Move your observe outside of your if (view == null) block. You always need to create a new Observer for your View every time onCreateView is called - the previous getViewLifecycleOwner() was destroyed when the previous onDestroyView was called, which correctly stops your previous observe callback.
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(view == null){
view = inflater.inflate(R.layout.fragment, container, false);
initializeViews(view);
btnSearch.setOnClickListener(btnSearchListener());
btnRegister.setOnClickListener(btnRegisterListener());
initializeRV();
}
receivedProductViewModel.getProductMutableLiveData().observe(
getViewLifecycleOwner(), new Observer<DispatchedProduct>() {
#Override
public void onChanged(DispatchedProduct dispatchedProduct) {
if(dispatchedProduct != null){
int index = dispatchedProducts.indexOf(dispatchedProduct);
dispatchedProductsAdapter.notifyItemChanged(index);
}
}
});
return view;
}

ClickListener in cardview return null exception

I have some problem when I try to click a cardview, I want it to make toast with its ID. But whenever I click the cardview, it always crashed and get null exception on its listener. already declare the listener but still crashed. Thank You
Listener class
public interface ResetPasswordListener {
void onClickCardview(String userid);
}
Adapter Class
public class ResetPasswordAdapter extends RecyclerView.Adapter<ResetPasswordAdapter.ViewHolder> implements ResetPasswordListener {
private List<ResetPasswordRespModel> resetList = new ArrayList<>();
private ResetPasswordListener resetPasswordListener;
public void setOnClick(ResetPasswordListener listener) {
this.resetPasswordListener = listener;
}
#Override
public void onClickCardview(String userid) {
Log.d("ID VALUE", userid);
resetPasswordListener.onClickCardview(userid);
}
}
This is how I set the listener in fragment class
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
listResetPasswordBinding = DataBindingUtil.inflate(inflater, R.layout.list_reset_password, container, false);
return inflater.inflate(R.layout.fragment_reset_password, container, false);
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
listResetPasswordBinding.setOnClick(new ResetPasswordListener() {
#Override
public void onClickCardview(String userid) {
Toast.makeText(getActivity(), userid, Toast.LENGTH_SHORT).show();
}
});
}
Error Logs
java.lang.NullPointerException: Attempt to invoke interface method 'void example.com.absensiapp.view.listener.ResetPasswordListener.onClickCardview(java.lang.String)' on a null object reference
at example.com.absensiapp.view.adapter.ResetPasswordAdapter.onClickCardview(ResetPasswordAdapter.java:35)
at example.com.absensiapp.databinding.ListResetPasswordBindingImpl._internalCallbackOnClick(ListResetPasswordBindingImpl.java:227)
at example.com.absensiapp.generated.callback.OnClickListener.onClick(OnClickListener.java:11)
The problem seems to be onCreateView you are inflating the layout twice one with DataBindingUtil.inflate adn once with inflater.inflate. Modify it
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
listResetPasswordBinding = DataBindingUtil.inflate(inflater, R.layout.list_reset_password, container, false);
return listResetPasswordBinding.getroot();
}
Also call setOnClick of ResetPasswordAdapter to set listener. Until you set the listener you will keep getting a null pointer.

Null Pointer Debugging [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 3 years ago.
quick summary of what I'm trying to do.
Using shared preferences so that when a user watches a video a green tick will appear beside it to mark it as complete. Currently getting a null pointer error.
Basically there are two pages - main page and video page. When user lands on the main page the if statement should check the sharepreferences to see if they have already visited the video page. If they have it should display the completedTick imageView.
Please see the code and context below.
MAIN PAGE CODE SNIPPET:
/**
* Shared preferences variable which is used to store the userId which is captured at the login screen.
*/
private SharedPreferences loginPref, activityPref;
private ImageView completedTick;
private Boolean activityCompleted;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
loginPref = getContext().getSharedPreferences("loginPref", Context.MODE_PRIVATE);
activityPref = getContext().getSharedPreferences("activityPref", Context.MODE_PRIVATE);
activityCompleted = activityPref.getBoolean("activityCompleted", true);
View view = inflater.inflate(R.layout.fragment_activities, container, false);
initialiseRecyclerView(view);
retrieveActivities();
if(activityCompleted == false || activityCompleted == null ) {
completedTick.setVisibility(view.INVISIBLE);
}else{
completedTick.setVisibility(View.VISIBLE);
}
return view;
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
/**
* Method which is used to initialise the recycler view
*
* #param view
*/
private void initialiseRecyclerView(View view) {
mRecyclerView = view.findViewById(R.id.recyclerViewActivities);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
EmptyAdapter emptyAdapter = new EmptyAdapter();
mRecyclerView.setAdapter(emptyAdapter);
completedTick = view.findViewById(R.id.completedTick);
}
VIDEO PAGE CODE SNIPPET:
private SharedPreferences activityPref;
private Boolean activityCompleted;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.youtube_layout);
Log.d(TAG, "onCreateView: Starting");
activityPref = getSharedPreferences("activityPref", MODE_PRIVATE);
activityCompleted = activityPref.getBoolean("activityCompleted", true);
getExtrasMethod();
initialiseViews();
setTextInViews();
checkCompleteD();
private void checkCompleteD() {
if (activityCompleted) {
SharedPreferences.Editor editor = activityPref.edit();
activityCompleted = false;
editor.putBoolean("activityCompleted", activityCompleted);
editor.apply();
}
}
The error is :
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setVisibility(int)' on a null object reference
at com.example.fit.ActivitiesFragment.onCreateView(ActivitiesFragment.java:78)
UPDATE:
Made the suggedted changes
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loginPref = getContext().getSharedPreferences("loginPref", Context.MODE_PRIVATE);
activityPref = getContext().getSharedPreferences("activityPref", Context.MODE_PRIVATE);
activityCompleted = activityPref.getBoolean("activityCompleted", true);
completedTick = view.findViewById(R.id.completedTickImage);
initialiseRecyclerView(view);
retrieveActivities();
if (activityCompleted == false || activityCompleted == null) {
completedTick.setVisibility(View.INVISIBLE);
} else {
completedTick.setVisibility(View.VISIBLE);
}
}
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setVisibility(int)' on a null object reference
at com.example.feelingfit.ActivitiesFragment.onViewCreated(ActivitiesFragment.java:85)
Which is this line : completedTick.setVisibility(View.INVISIBLE);
First of all, your completedTick is null. Try putting the completedTick = view.findViewById(R.id.completedTick); in your OnViewCreated above this code:
if(activityCompleted == false || activityCompleted == null ) {
completedTick.setVisibility(View.INVISIBLE);
}else{
completedTick.setVisibility(View.VISIBLE);
}
And also, replace the view.INVISIBLE with View.INVISIBLE
Edit + Aditional Information:
If you occur again the NullPointerException please refer to this link.
Edit 2:
Rewrite your code inside the onViewCreated method like this:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_activities, container, false);
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loginPref = getContext().getSharedPreferences("loginPref", Context.MODE_PRIVATE);
activityPref = getContext().getSharedPreferences("activityPref", Context.MODE_PRIVATE);
activityCompleted = activityPref.getBoolean("activityCompleted", true);
initialiseRecyclerView(view);
retrieveActivities();
if(activityCompleted == false || activityCompleted == null ) {
completedTick.setVisibility(View.INVISIBLE);
}else{
completedTick.setVisibility(View.VISIBLE);
}
}
Edit number 3: Since it is crashing again on the same use case I assume your R.id.completedTickImage is not inside your fragment_activities
Do the operation of CompleteTick.setVisibility inside onActivityCreated() method and also replace the view.INVISIBLE to View.INVISIBLE

Fragment's state is not being retained

I am calling fragment's onSaveInstance() method in which I have saved the value of edittext and checbox in bundle. In onCreateView I am setting those values to my edittext and checkbox but when I run the app and write something in edittext and rotate my device it becomes blank! can anyone help?
public class BlankFragment extends Fragment {
public EditText ed;
public CheckBox cb;
public BlankFragment() { }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_blank, container, false);
ed = v.findViewById(R.id.edit);
cb = v.findViewById(R.id.check);
if(savedInstanceState != null){
ed.setText(savedInstanceState.getString("string"));
cb.setChecked(savedInstanceState.getBoolean("bool"));
}
return v;
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putString("string",ed.getText().toString());
outState.putBoolean("bool",cb.isChecked());
super.onSaveInstanceState(outState);
}
}
I have logged those setText and setChecked lines, it prints correct values in log but then why it is not setting anything on views!

Saving and restoring state using fragments

I'm trying to understand the process of saving and restoring state using fragments. I've created sliding navigation menu using it.
In one of the fragments there is this code:
public class FifthFragment extends Fragment {
CheckBox cb;
View view;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fifth_layout, container, false);
cb = (CheckBox) view.findViewById(R.id.checkBox);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore save state
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save state
}
}
For example I want to save the state of the CheckBox before user exits the fragment and restore it when the fragment is created again. How to achieve this?
EDIT:
According to raxellson's answer I've changed my fragment to this:
public class FifthFragment extends Fragment {
private static final String CHECK_BOX_STATE = "string";
CheckBox cb;
View view;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fifth_layout, container, false);
cb = (CheckBox) view.findViewById(R.id.checkBox);
if (savedInstanceState == null) {
Log.i("statenull", "null");
}
if (savedInstanceState != null) {
// Restore last state for checked position.
boolean checked = savedInstanceState.getBoolean(CHECK_BOX_STATE, false);
cb.setChecked(checked);
}
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(CHECK_BOX_STATE, cb.isChecked());
}
}
I got logged I/statenull: null so savedInstanceState was not saved. What am I doing wrong?
You want to save the value of your current checked state in onSaveInstanceState.
Something like this:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(CHECK_BOX_STATE, cb.getChecked());
}
and then when your view is created you want to get the value if it's present. And set your CheckBox state with it.
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fifth_layout, container, false);
cb = (CheckBox) view.findViewById(R.id.checkBox);
if (savedInstanceState != null) {
// Restore last state for checked position.
boolean checked = savedInstanceState.getBoolean(CHECK_BOX_STATE, false);
cb.setChecked(checked);
}
return view;
}
EDIT:
When you add the fragment, make sure to add it with a tag or id so that you can retrieve the same instance.
You could do a helper method to retrieve fragment and set the fragment.
private void setFragment(String tag, Fragment newFragment) {
FragmentManager fm = getSupportFragmentManager();
Fragment savedFragment = fm.getFragmentByTag(tag);
fm.replace(R.id.container, savedFragment != null ? savedFragment : newFragment, tag);
fm.commit();
}
so you your switch you can call the helper method instead.
switch (position) {
case 0:
setFragment("A", new FragmentA());
break;
....
}
Note: This is just an example not best practice since you are creating new fragments every time in your switch case now anyways. But it might point you in the right direction.
After see all the example. Here is the solution for save fragment state:
Two steps for this:
1.
String saveValue;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
saveValue = "";
} else {
saveValue = savedInstanceState.getString("saveInstance");
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//save the values of fragment if destroy on second to back
if (!saveValue.isEmpty())
savedInstanceState.putString("saveInstance", saveValue);
}
In onSaveInstanceState you can save your values. And after destroy fragment you can receive your values through onCreate.

Categories

Resources