0

I am trying to get my spinner to save its position when the screen turns. I have tried following the solution here, however it does not work and gives me a nullpointerexception. Any ideas how to fix it?

package tk.talcharnes.popularmovies;

import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.Spinner;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * A placeholder fragment containing a simple view.
 */
public class PostersFragment extends Fragment {
    private static List<MovieModel> movieModelList;
    private static int movieModelListLength;
    GridView gridView;
    ImageAdapter adapter;
    Spinner spinner;
    private String sort_method;
    public PostersFragment() {
        //sort_method = "popularity.desc";
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main, container, false);

            updatePosters();
        // should find gridview on the view which you are creating
        gridView = (GridView) view.findViewById(R.id.gridview);



         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int width = (int) (displayMetrics.widthPixels / displayMetrics.density);
//
//        //For Tabs
////        boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
////        width = isLandscape ? (width / 2) : width;
//        int numcolumns =(int) (width/(185));
////
////        float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
////        float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
////        int numcolumns = (int)((185*dp)/dpWidth);
//        gridView.setNumColumns(numcolumns);
        if (getResources().getConfiguration().orientation
                == 1) {
            gridView.setNumColumns(2);


        } else if ( getResources().getConfiguration().orientation
                == 2) {
            gridView.setNumColumns(3);

        }

        adapter = new ImageAdapter(getContext());
        gridView.setAdapter(adapter);
        updatePosters();
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View v,
                                    int position, long id) {
                Toast.makeText(getContext(), "You clicked " + movieModelList.get(position).getTitle(),
                        Toast.LENGTH_SHORT).show();
                MovieModel movieModel = movieModelList.get(position);
                Intent intent = new Intent(getActivity(), MovieDetails.class);
                intent.putExtra("Movie_number", position);
                startActivity(intent);

            }
        });
        return view;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("spinner", spinner.getSelectedItemPosition());
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

        if(savedInstanceState != null) {
            spinner.setSelection(savedInstanceState.getInt("spinner", 0));
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        //commented out until a settings menu is implemented
      //  super.onCreateOptionsMenu(menu, inflater);
      //  getActivity().getMenuInflater().inflate(R.menu.menu_main, menu);
        inflater.inflate(R.menu.menu_refresh, menu);


        MenuItem item = menu.findItem(R.id.spinnerr);
         spinner = (Spinner) MenuItemCompat.getActionView(item);


        String[] sortingCriteria = {"Popular", "Highest Rated"};
        ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(getContext(), R.layout.spinner, sortingCriteria);
        spinner.setAdapter(spinnerAdapter);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
                if(position == 0){
                    sort_method = "popularity.desc";
                    updatePosters();
                }
                else if (position == 1){
                    sort_method = "vote_average.desc";
                    updatePosters();
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parentView) {
                // does nothing
            }

        });

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        //noinspection SimplifiableIfStatement

        if (id == R.id.action_refresh) {
            Toast.makeText(getActivity(), "Refreshing",
                    Toast.LENGTH_SHORT).show();

           updatePosters();
            gridView.setAdapter(adapter);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    public void updatePosters(){
        FetchPostersTask updatePosters = new FetchPostersTask();
        updatePosters.execute();
    }









    //Get movie posters and data
    public class FetchPostersTask extends AsyncTask<Void,Void,Void> {
        private final String LOG_TAG = FetchPostersTask.class.getSimpleName();
        //will contain raw Json data
        String posterJsonString = null;

        public Void parseMovieJson()
                throws JSONException{
            JSONObject jsonParentObject = new JSONObject(posterJsonString);
            JSONArray movieJSonArray = jsonParentObject.getJSONArray("results");

            movieModelList = new ArrayList<>();
            for(int i = 0; i < movieJSonArray.length(); i++){
                JSONObject movieJsonObject = movieJSonArray.getJSONObject(i);
                MovieModel movieModel = new MovieModel();
                movieModel.setTitle(movieJsonObject.getString("title"));
                movieModel.setOverview(movieJsonObject.getString("overview"));
                movieModel.setPoster_path(movieJsonObject.getString("poster_path"));
                movieModel.setRelease_date(movieJsonObject.getString("release_date"));
                movieModel.setVote_average(movieJsonObject.getString("vote_average"));
                movieModelListLength++;

                movieModelList.add(movieModel);
            }
            return null;
        }

        @Override
        protected Void doInBackground(Void ...params) {

            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;

            //will contain raw Json data
            try{

                //open connection to api

                final String BASE_URL = "https://api.themoviedb.org/3/discover/movie?";
                final String SORT_PARAM ="sort_by";



                Uri builtUri = Uri.parse(BASE_URL).buildUpon()
                        .appendQueryParameter(SORT_PARAM, sort_method)
                        .appendQueryParameter("api_key", BuildConfig.MOVIE_DB_API_KEY).build();

                URL url = new URL(builtUri.toString());
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();

                //read input into string
                InputStream inputStream = urlConnection.getInputStream();
                StringBuffer buffer = new StringBuffer();
                if(inputStream == null){
                    //nothing else to do in this case
                    return null;
                }
                reader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while((line = reader.readLine())!= null){
                    buffer.append(line + "\n");
                }

                if(buffer.length()==0){
                    //nothing here, don't parse
                    return null;
                }

                posterJsonString = buffer.toString();
            }
            catch(MalformedURLException e){
                e.printStackTrace();
            }
            catch(IOException e){
                Log.e(LOG_TAG, "Error", e);
                return null;
            }
            finally {
                if(urlConnection != null){
                    urlConnection.disconnect();
                }
                if(reader != null){
                    try{
                        reader.close();

                    }
                    catch (final IOException e){
                        Log.e(LOG_TAG,"Error closing stream", e);
                    }
                }
            }
            try{
                parseMovieJson();;
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            String[] asc = new String[movieModelList.size()];
            for(int i = 0; i < asc.length; i++){
                asc[i]=(getMovieModelList().get(i).getPoster_path());

            }
            adapter.setImageArray(asc);
            adapter.notifyDataSetChanged();
        }
    }
    public static List<MovieModel> getMovieModelList(){
        return movieModelList;
    }
    public static int getMovieModelListLength(){
        return movieModelListLength;
    }

}

here is error code:

FATAL EXCEPTION: main
                                                                             Process: tk.talcharnes.popularmovies, PID: 16847
                                                                             java.lang.RuntimeException: Unable to start activity ComponentInfo{tk.talcharnes.popularmovies/tk.talcharnes.popularmovies.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Spinner.setSelection(int)' on a null object reference
                                                                                 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2306)
                                                                                 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2366)
                                                                                 at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3945)
                                                                                 at android.app.ActivityThread.access$900(ActivityThread.java:149)
                                                                                 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1290)
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                 at android.os.Looper.loop(Looper.java:135)
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5290)
                                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                                 at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
                                                                              Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Spinner.setSelection(int)' on a null object reference
                                                                                 at tk.talcharnes.popularmovies.PostersFragment.onCreate(PostersFragment.java:115)
                                                                                 at android.support.v4.app.Fragment.performCreate(Fragment.java:1939)
                                                                                 at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1029)
                                                                                 at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
                                                                                 at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1230)
                                                                                 at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2037)
                                                                                 at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:154)
                                                                                 at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:289)
                                                                                 at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:61)
                                                                                 at tk.talcharnes.popularmovies.MainActivity.onCreate(MainActivity.java:13)
                                                                                 at android.app.Activity.performCreate(Activity.java:6020)
                                                                                 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
                                                                                 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2259)
                                                                                 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2366) 
                                                                                 at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3945) 
                                                                                 at android.app.ActivityThread.access$900(ActivityThread.java:149) 
                                                                                 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1290) 
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                                 at android.os.Looper.loop(Looper.java:135) 
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5290) 
                                                                                 at java.lang.reflect.Method.invoke(Native Method) 
                                                                                 at java.lang.reflect.Method.invoke(Method.java:372) 
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908) 
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703) 
Community
  • 1
  • 1
Tal C
  • 547
  • 1
  • 8
  • 17
  • When device change its orientation activity is recreating thats why fragment lost it state. use these flags in activity in which fragment loaded. android:configChanges="keyboardHidden|orientation|screenSize" – Khizar Hayat Mar 03 '16 at 08:55

4 Answers4

2

Whenever your orientation changes so does your screen size, add the following line in your AndroidManifest.xml file inside the activity you want not to recreate on rotation or size change

android:configChanges="orientation|screenSize"

This way your Activity wont be restarted automatically. See the documentation for more infos

android:configChanges

Lists configuration changes that the activity will handle itself. When a configuration change occurs at runtime, the activity is shut down and restarted by default, but declaring a configuration with this attribute will prevent the activity from being restarted. Instead, the activity remains running and its onConfigurationChanged() method is called.

AL̲̳I
  • 2,441
  • 4
  • 29
  • 50
1

As you know the whole Activity is recreated after orientation changes. You have written the code in onCreate method but at that time the spinner was not initialized.

So what you can do now is

Create a variable with Bundle datatype and make that global, and save savedInstanceState to that variable

if(savedInstanceState != null) {
            this.myBundle = savedInstanceState;
    }

Then inside onCreateOptionsMenu check if this.myBundle is empty or not before calling spinner.setSelection. Your code looks like this and write this lines of code after the statement spinner.setAdapter(spinnerAdapter);.

 MenuItem item = menu.findItem(R.id.spinnerr);
 spinner = (Spinner) MenuItemCompat.getActionView(item);

 //rest of your codes

spinner.setAdapter(spinnerAdapter);

if(this.myBundle != null){
 spinner.setSelection(myBundle.getInt("spinner", 0));
}
Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
  • Thanks! This helped with rotating! Although I just noticed when I press up button in action bar it does same issue where it doesn't save. When I press the (physical) back button on phone though it does save spinner position. Any ideas? – Tal C Mar 03 '16 at 11:29
  • savedInstanceState is called everytime it's normal. If you want to prevent this you can override onpause and clear the bundle. – Shree Krishna Mar 03 '16 at 11:56
  • I do want it to save. What I'm saying is I want it to also save my position and go to the position when I press the back button on menubar. – Tal C Mar 03 '16 at 12:04
  • 1
    Then do the same override onpause and save the int to myBundle... if something raised then leave comment... currently i am in leave and i will catch up this tomorrow... – Shree Krishna Mar 03 '16 at 12:37
1

Remember selected position from saved instance bundle on create

if(savedInstanceState != null) {
    savedSelection = savedInstanceState.getInt("spinner", 0);
}

and set it after

MenuItem item = menu.findItem(R.id.spinnerr);
spinner = (Spinner) MenuItemCompat.getActionView(item);
spinner.setSelection(savedSelection);
1

With the latest api as of now, in an activity, it is already persisting the selected item of the spinner on rotation, but not in fragments. Here is an example for persisting the selected item of the spinner on a fragment when the screen rotates.

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        if (savedInstanceState == null) {
            val frag = SpinnerFragment()
            supportFragmentManager
                    .beginTransaction()
                    .replace(R.id.fl_container, frag)
                    .commit()
        }

    }

}

SpinnerFragment.kt

class SpinnerFragment : Fragment(), AdapterView.OnItemSelectedListener {

        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
            return inflater.inflate(R.layout.fragment_spinner, container, false)
        }

        override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)


            val adapter = ArrayAdapter.createFromResource(activity, R.array.planets_array, android.R.layout.simple_spinner_item)
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
            planets_spinner.adapter = adapter
            planets_spinner.onItemSelectedListener = this

            var selectedIndex = 0
            if (savedInstanceState != null) {
                selectedIndex = savedInstanceState.getInt("planets_spinner", 0)
            }
            planets_spinner.setSelection(selectedIndex)
        }

        override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
            // An item was selected. You can retrieve the selected item using
            // parent.getItemAtPosition(pos)
        }

        override fun onNothingSelected(parent: AdapterView<*>?) {
            // Another interface callback
        }


        override fun onSaveInstanceState(outState: Bundle?) {
            outState?.putInt("planets_spinner", planets_spinner.getSelectedItemPosition());
            super.onSaveInstanceState(outState)

        }
    }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.myapplication.MainActivity">
    <FrameLayout
        android:id="@+id/fl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>

fragment_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.myapplication.MainActivity">

    <Spinner
        android:id="@+id/planets_spinner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


</android.support.constraint.ConstraintLayout>

strings.xml

<resources>
    <string name="app_name">My Application</string>


    <string-array name="planets_array">
        <item>Mercury</item>
        <item>Venus</item>
        <item>Earth</item>
        <item>Mars</item>
        <item>Jupiter</item>
        <item>Saturn</item>
        <item>Uranus</item>
        <item>Neptune</item>
    </string-array>
</resources>
s-hunter
  • 24,172
  • 16
  • 88
  • 130
  • I was looking why the position of a spinner is retained in an activity but not in a fragment, do you have a reference to the documentation? – Gainder Feb 29 '20 at 19:21