So I've spent hours pouring over posts and trying all kinds of solutions here, but I really can't figure out why the app crashes whenever I try to change the background of the QuotesFragment. Basically, in SettingsFragment, the user selects a theme from a spinner, an internal class gets the value and uses an interface to pass the value to the QuotesFragment, which would change its background.
I originally wanted to change it from the SettingsFragment but that seemed to be an even bigger headache. I really only want the background to change for the specific fragment, not every fragment. I initially had the one relative layout for QuotesFragment and tried to change that, but a suggestion I saw online was to nest a RelativeLayout for the background.
Pretty much whenever I click the "Save" button in the SettingsFragment, the app crashes. Everything was working before trying to implement the background changes, so I feel like it has something to do with that. Any help would be greatly appreciated. Also, I don't have much experience posting here so sorry if this isn't quite the way it should be.
public class SettingsFragment extends Fragment implements AdapterView.OnItemSelectedListener {
private Switch notificationSwitch;
private TextView notificationsTV;
private TextView perDayTV;
private Button saveButton;
private Button clearButton;
ArrayList<Calendar> calList;
Spinner quotesSpinner;
Spinner themesSpinner;
private int QUOTES_SPINNER_VALUE;
private String THEMES_SPINNER_VALUE;
public interface BackgroundThemeListener{
void themeSelected(String theme);
}//interface
private BackgroundThemeListener bgListener;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.settings_fragment, container, false);
getActivity().setTitle("Settings");
THEMES_SPINNER_VALUE = "";
QUOTES_SPINNER_VALUE = 0;
notificationSwitch = view.findViewById(R.id.notification_switch);
notificationsTV = view.findViewById(R.id.notifications_onoff_textview);
perDayTV = view.findViewById(R.id.per_day_textview);
saveButton = view.findViewById(R.id.save_button);
clearButton = view.findViewById(R.id.clear_button);
calList = new ArrayList<>();
quotesSpinner = view.findViewById(R.id.per_day_spinner);
ArrayAdapter<CharSequence> quotesAdapter = ArrayAdapter.createFromResource(getContext(), R.array.quotes_per_day_numbers, android.R.layout.simple_spinner_item);
quotesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
quotesSpinner.setAdapter(quotesAdapter);
quotesSpinner.setOnItemSelectedListener(this);
themesSpinner = view.findViewById(R.id.themes_spinner);
ArrayAdapter<CharSequence> themeAdapter = ArrayAdapter.createFromResource(getContext(), R.array.bg_themes, android.R.layout.simple_spinner_item);
themeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
themesSpinner.setAdapter(themeAdapter);
themesSpinner.setOnItemSelectedListener(new ThemesSpinnerClass());
notificationSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
perDayTV.setVisibility(View.VISIBLE);
quotesSpinner.setVisibility(View.VISIBLE);
saveButton.setVisibility(View.VISIBLE);
clearButton.setVisibility(View.VISIBLE);
Toast.makeText(getActivity(), "Notifications On", Toast.LENGTH_SHORT);
} else{
perDayTV.setVisibility(View.INVISIBLE);
quotesSpinner.setVisibility(View.INVISIBLE);
saveButton.setVisibility(View.INVISIBLE);
clearButton.setVisibility(View.INVISIBLE);
Toast.makeText(getActivity(), "Notifications Cleared", Toast.LENGTH_SHORT);
}//if/else
}//onCheckChanged
});
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bgListener.themeSelected(THEMES_SPINNER_VALUE);
int hour;
int minute;
//for loop adding calendars to the list
for (int i = 0; i < QUOTES_SPINNER_VALUE; i++){
Calendar cal = Calendar.getInstance();
hour = (int) (Math.random() % 24) + 1;
minute = (int) (Math.random() % 60) + 1;
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
calList.add(cal);
}//for
Toast.makeText(getActivity(), "Times Set Randomly", Toast.LENGTH_SHORT).show();
startAlarm();
bgListener.themeSelected(THEMES_SPINNER_VALUE);
}//onClick
});
clearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (Calendar cal : calList) {
cancelAlarm();
}//for
Toast.makeText(getActivity(), "Notifications Cleared", Toast.LENGTH_SHORT).show();
}
});
return view;
}//onCreateView method
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
QUOTES_SPINNER_VALUE = Integer.parseInt(quotesSpinner.getSelectedItem().toString());
}//onItemSelected method
@Override
public void onNothingSelected(AdapterView<?> parent) {
}//onNothingSelected method
private void startAlarm(){
for (Calendar cal : calList) {
AlarmManager alarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), AlertReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1, intent, 0);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), alarmManager.INTERVAL_DAY, pendingIntent);
}//for
}//startAlarm method
private void cancelAlarm(){
AlarmManager alarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), AlertReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1, intent, 0);
alarmManager.cancel(pendingIntent);
}//cancelAlarm method
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof BackgroundThemeListener){
bgListener = (BackgroundThemeListener) context;
}//if
else{
throw new RuntimeException(context.toString() + " must implement BackgroundThemeListener");
}//else
}//onAttach method
@Override
public void onDetach() {
super.onDetach();
bgListener = null;
}//onDetach method
class ThemesSpinnerClass implements AdapterView.OnItemSelectedListener{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
THEMES_SPINNER_VALUE = themesSpinner.getItemAtPosition(position).toString();
//bgListener.themeSelected(THEMES_SPINNER_VALUE);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}//ThemesSpinnerClass
}//SettingsFragment class
import static com.abc.danielharrington.betterdays.BetterDays.CHANNEL_1_ID;
import static com.abc.danielharrington.betterdays.BetterDays.CHANNEL_2_ID;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, SettingsFragment.BackgroundThemeListener {
private DrawerLayout drawer;
private NotificationManagerCompat notificationManager;
private QuotesFragment quotesFragment;
private AboutFragment aboutFragment;
private SettingsFragment settingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
quotesFragment = new QuotesFragment();
aboutFragment = new AboutFragment();
settingsFragment = new SettingsFragment();
notificationManager = NotificationManagerCompat.from(this);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
drawer = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navView = findViewById(R.id.nav_view);
navView.setNavigationItemSelectedListener(this);
if(savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new QuotesFragment()).commit();
navView.setCheckedItem(R.id.nav_quotes);
}//if
}//onCreate method
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.nav_quotes:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, quotesFragment).commit();
break;
case R.id.nav_about:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, aboutFragment).commit();
break;
case R.id.nav_settings:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, settingsFragment).commit();
break;
case R.id.nav_share:
break;
}//switch
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public void onBackPressed(){
if(drawer.isDrawerOpen(GravityCompat.START)){
drawer.closeDrawer(GravityCompat.START);
}else {
super.onBackPressed();
}
}//onBackPressed
public void sendOnChannel1(View view){
String title = "Better Days";
String message = "New Quote Available";
Notification notification = new NotificationCompat.Builder(this, CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_quotes)
.setContentTitle(title).setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.build();
notificationManager.notify(1, notification);
}//sendOnChannel1 method
public void sendOnChannel2(View view){
String title = "Better Days";
String message = "New Quote Available";
Notification notification = new NotificationCompat.Builder(this, CHANNEL_2_ID)
.setSmallIcon(R.drawable.ic_quotes)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.build();
notificationManager.notify(2, notification);
}//sendOnChannel2 method
@Override
public void themeSelected(String theme) {
quotesFragment.updateTheme(theme);
}//themeSelected
}//MainActivity class
public class QuotesFragment extends Fragment {
private DrawerLayout drawer;
private RelativeLayout quotesLayout;
private String BG_THEME;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.quotes_fragment, container, false);
getActivity().setTitle("Quotes");
quotesLayout = rootView.findViewById(R.id.quotes_bg_layout);
//quotesLayout.setBackgroundResource(R.drawable.img_forest);
return rootView;
}//onCreateView method
public void updateTheme(String theme){
BG_THEME = theme;
if (theme.equals("Forest")){
quotesLayout.setBackgroundResource(R.drawable.img_forest);
}
//setQuotesBackground();
}//updateTheme method
//method to change the quotes fragment background based on user preference
private void setQuotesBackground(){
if(BG_THEME.equals("Forest")){
quotesLayout.setBackgroundResource(R.drawable.img_forest);
}
/*
switch(BG_THEME){
case "Default":
quotesLayout.setBackgroundColor(getActivity().getResources().getColor(R.color.signBlue));
break;
case "Forest":
quotesLayout.setBackgroundResource(R.drawable.img_forest);
quotesLayout.invalidate();
break;
case "Space":
quotesLayout.setBackground(getActivity().getResources().getDrawable(R.drawable.img_space));
break;
case "Mountain":
quotesLayout.setBackground(getActivity().getResources().getDrawable(R.drawable.img_mountain));
break;
case "Beach":
quotesLayout.setBackground(getActivity().getResources().getDrawable(R.drawable.img_beach));
break;
default:
quotesLayout.setBackgroundColor(getActivity().getResources().getColor(R.color.signBlue));
break;
}//switch
*/
}//setQuotesBackground method
}//QuotesFragment class
This is what the error says:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.abc.danielharrington.betterdays, PID: 4744 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.RelativeLayout.setBackgroundResource(int)' on a null object reference at com.abc.danielharrington.betterdays.QuotesFragment.updateTheme(QuotesFragment.java:45) at com.abc.danielharrington.betterdays.MainActivity.themeSelected(MainActivity.java:131) at com.abc.danielharrington.betterdays.SettingsFragment$2.onClick(SettingsFragment.java:101) at android.view.View.performClick(View.java:6597) at android.view.View.performClickInternal(View.java:6574) at android.view.View.access$3100(View.java:778) at android.view.View$PerformClick.run(View.java:25885) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)