0

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)

Markus Kauppinen
  • 3,025
  • 4
  • 20
  • 30
daniel-eh
  • 344
  • 3
  • 13
  • Just move your code to onViewCreated() of fragment lifeCycle method – Akash Dubey May 16 '19 at 03:38
  • Someone else suggested that as well but it hasn't fixed the issue. Clicking the "Save" button still crashes it. Maybe it has to do with the interface? I added the error to the bottom of the original post. – daniel-eh May 16 '19 at 05:43
  • move the setBackground code only to onViewCreated() – Akash Dubey May 16 '19 at 05:46
  • Yeah I mean, that works for setting the background right away. But the settings fragment has a spinner with five choices for the background and I'm looking for the user to choose one, press the save button, and then the background will change. Maybe I'm missing something, but I can't figure out changing it on the spot like that. – daniel-eh May 16 '19 at 05:58

0 Answers0