Question

I am trying to create an app that uses Jack Wharton's ViewPager library (here) using only images for every page (something like Prixing (here) main screen). Everything works ok, except for the saveInstance in the Fragment.

In Jack Wharton's example, he stores the text in a String variable, named mContent, and restores it in onCreate, but in my case, what should I do? Save/Restore a bitmap?! Any objective answer would be much appreciated!

I'm quite new to this Fragment using applications, cause every example I look at, presents only the basics, and it's getting difficult upon more complex ones.

PS. If is it usefull to know, I'm using CirclePageIndicator.

Current Fragment Code here:

    public final class SpecialOfferFragment extends Fragment {

    private int imageResourceId;

    public static SpecialOfferFragment newInstance(int i) {

        //probably I'll use a bitmap(downloaded) as parameter instead of using static images
        SpecialOfferFragment fragment = new SpecialOfferFragment();

        fragment.imageResourceId = i;

        return fragment;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // if ((savedInstanceState != null) { }

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        ImageView image = new ImageView(getActivity());
        image.setImageResource(imageResourceId);
        image.setScaleType(ScaleType.FIT_XY);

        LinearLayout layout = new LinearLayout(getActivity());
        layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                LayoutParams.FILL_PARENT));

        layout.setGravity(Gravity.CENTER);
        layout.addView(image);

        return layout;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //smth to save here..
    }
}

In this current state of the app, I get the following Exception:

04-12 07:28:17.760: E/AndroidRuntime(31903): FATAL EXCEPTION: main 
04-12 07:28:17.760: E/AndroidRuntime(31903): java.lang.NullPointerException
04-12 07:28:17.760: E/AndroidRuntime(31903): at android.support.v4.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1576)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1617)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:481)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.Activity.performSaveInstanceState(Activity.java:1113)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1188)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:2804)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.ActivityThread.handleStopActivity(ActivityThread.java:2862)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.ActivityThread.access$900(ActivityThread.java:127)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1175)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.os.Handler.dispatchMessage(Handler.java:99)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.os.Looper.loop(Looper.java:137)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at android.app.ActivityThread.main(ActivityThread.java:4511)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at java.lang.reflect.Method.invokeNative(Native Method)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at java.lang.reflect.Method.invoke(Method.java:511)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
04-12 07:28:17.760: E/AndroidRuntime(31903):    at dalvik.system.NativeStart.main(Native Method)

This happens whenever I pause/stop the activity containing this fragment, like pushing the Home button.

**

EDIT with code:

**

 public class MainMenu extends FragmentActivity {

    //private List<CategoriesHolder> categoriesList = new ArrayList<CategoriesHolder>();
    //private CategoriesAdapter categoriesAdapter = null;
    //private GridView gv_mainmenu_categories;

    SpecialOfferFragmentAdapter mAdapter;
    ViewPager mPager;
    PageIndicator mIndicator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_mainmenu);

        linkUI();
        setAction();

        // just for testing
        String[] imagesUrls = null;

        mAdapter = new SpecialOfferFragmentAdapter(getSupportFragmentManager(),
                this, imagesUrls);

        //categoriesAdapter = new CategoriesAdapter(this, categoriesList);

        // used for Categories GridView
        //gv_mainmenu_categories.setAdapter(categoriesAdapter);

        // used for ViewPager
        mPager.setAdapter(mAdapter);
        mIndicator.setViewPager(mPager);

    }

    private void linkUI() {
        mPager = (ViewPager) findViewById(R.id.vp_mainmenu_special_offers);
        mIndicator = (CirclePageIndicator) findViewById(R.id.indicator);

        //gv_mainmenu_categories = (GridView) findViewById(R.id.gv_mainmenu_categories);

    }

    private void setAction() {

    }
} 

    public class SpecialOfferFragmentAdapter extends FragmentPagerAdapter implements
        IconPagerAdapter {

    private int[] mCarouselImages = new int[] { R.drawable.pic1,
            R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5

    };

    private String[] imagesUrls;
    private Context context;

    public static final int[] ICONS = new int[] {
            R.drawable.perm_group_calendar, R.drawable.perm_group_camera,
            R.drawable.perm_group_device_alarms, R.drawable.perm_group_location };

    private int mCount = mCarouselImages.length;

    public SpecialOfferFragmentAdapter(FragmentManager fm, Context context,
            String[] imagesUrls) {
        super(fm);
        this.context = context;
        this.imagesUrls = imagesUrls;
    }

    @Override
    public Fragment getItem(int position) {

        return SpecialOfferFragment.newInstance(mCarouselImages[position]);
    }

    @Override
    public int getCount() {
        return mCount;
    }

    @Override
    public int getIconResId(int index) {
        return ICONS[index % ICONS.length];
    }

    public void setCount(int count) {
        if (count > 0 && count <= 10) {
            mCount = count;
            notifyDataSetChanged();
        }
    }
}
Était-ce utile?

La solution

To achieve the thing which you want, you should use integer value to store the current position in your ViewPager, and after that use this value to set the right position to your ViewPager.

For example do something like this in your FragmentActivity:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  savedInstanceState.putInt("mMyCurrentPosition", mPager.getPosition());
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  mMyCurrentPosition = savedInstanceState.getInt("mMyCurrentPosition");
  // where mMyCurrentPosition should be a public value in your activity.
}

which will saved your ViewPager's last position. And after that in your onResume() for example you can check

if(mMyCurrentPosition != 0){
    mPager.setCurrentItem(mMyCurrentPosition);
}

I think this should work , it's not a good practice to save images / bitmaps in bundle (and I don't think you are able to do it).

Autres conseils

Ok first id like to clear something up that might solve your confusion about savedinstancestate. Savedinstancestate can only handle primitives ( int, double, float, byte..) and the String object. This should be all you need but in some cases you want to save instances of other objects as well. For this you would need some other pattern to accomplish the job, for example a factory pattern wich holds an array of all your bitmaps. Like for instance create a class ImageFactory that holds a static ArrayList of bitmaps. Whenever you rotate your device the oncreateview and onactivitycreated methods are called again and this is the moment to check if you want to retrieve information from your factory or not. for example

if(savedInstanceState != null) { //we have a savedinstancestate so there probably are saved images ImageFactory.getBMPs(); }

hope you can do something with this info. good luck

this worked for me easy and simple inside your main activity

 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        onCreate(savedInstanceState);
    }

and inside your manifest

 <activity
            android:configChanges="orientation|screenSize"/>

I have created My Own Method for Save Fragment Data while Tab is Changed using SharedPreference:

Method like:

  public void saveData() {

    Log.d("msg", "Save Instance");
    SharedPreferences.Editor outState = getActivity().getSharedPreferences("order", Context.MODE_APPEND).edit();

    orderDate = orderDateEditText.getText().toString();
    orderBy = orderByEditText.getText().toString();
    orderMobileNo = orderMobileNoEditText.getText().toString();
    orderTransport = orderTransportEditText.getText().toString();
    orderInvoicePer = orderInvoicePerEditText.getText().toString(); 
    orderCharge = orderChargeEditText.getText().toString();
    orderGoodsDispatch = orderGoodsDispatchEditText.getText().toString();
    orderOthers = orderOthersEditText.getText().toString();
    orderIsDirect = orderIsDirectCheckBox.isChecked() ? 1 : 0;

    outState.putBoolean("saved", true);

    outState.putString("orderDate", orderDate);
    outState.putString("orderBy", orderBy);
    outState.putString("orderMobileNo", orderMobileNo);
    outState.putString("orderTransport", orderTransport);
    outState.putString("orderInvoicePer", orderInvoicePer);
    outState.putString("orderCharge", orderCharge);
    outState.putString("orderGoodsDispatch", orderGoodsDispatch);
    outState.putString("orderOthers", orderOthers);
    outState.putInt("orderIsDirect", orderIsDirect);

    outState.commit();
}

Call this Method when

    @Override
    public void onStop() {
    // TODO Auto-generated method stub
        super.onStop();

        saveData();
}

Replacing Saved Data with EditText:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onActivityCreated(savedInstanceState);

    /***** PUT DATA IN EDITTEXT is ITS AVAILABLE IN BUNDLE *****/
    SharedPreferences orderData = getActivity().getSharedPreferences("order", Context.MODE_APPEND);
    Log.d("msg", ""+orderData.getBoolean("saved", false));

    if(orderData.getBoolean("saved", false))
    {
        orderDate = orderData.getString("orderDate", "");
        orderBy = orderData.getString("orderBy", "");
        orderMobileNo = orderData.getString("orderMobileNo", "");
        orderTransport = orderData.getString("orderTransport", "");
        orderInvoicePer = orderData.getString("orderInvoicePer", ""); 
        orderCharge = orderData.getString("orderCharge", "");
        orderGoodsDispatch = orderData.getString("orderGoodsDispatch", "");
        orderOthers = orderData.getString("orderOthers", "");
        orderIsDirect = orderData.getInt("orderIsDirect", 0);

        orderDateEditText.setText(orderDate);
        orderByEditText.setText(orderBy);
        orderMobileNoEditText.setText(orderMobileNo);
        orderTransportEditText.setText(orderTransport);
        orderInvoicePerEditText.setText(orderInvoicePer);
        orderChargeEditText.setText(orderCharge);
        orderGoodsDispatchEditText.setText(orderGoodsDispatch);
        orderOthersEditText.setText(orderOthers);
        if(orderIsDirect == 0)
            orderIsDirectCheckBox.setChecked(false);
        else
            orderIsDirectCheckBox.setChecked(true);
    }
}

May this code is Helpful to you...

Thanks...

I'm not quite sure if this question is still bothering you, since it has been several months. But I would like to share how I dealt with this. Here is the source code:

int FLAG = 0;
private View rootView;
private LinearLayout parentView;

/**
 * The fragment argument representing the section number for this fragment.
 */
private static final String ARG_SECTION_NUMBER = "section_number";

/**
 * Returns a new instance of this fragment for the given section number.
 */
public static Fragment2 newInstance(Bundle bundle) {
    Fragment2 fragment = new Fragment2();
    Bundle args = bundle;
    fragment.setArguments(args);
    return fragment;
}

public Fragment2() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    Log.e("onCreateView","onCreateView");
    if(FLAG!=12321){
        rootView = inflater.inflate(R.layout.fragment_create_new_album, container, false);
        changeFLAG(12321);
    }       
    parentView=new LinearLayout(getActivity());
    parentView.addView(rootView);

    return parentView;
}

/* (non-Javadoc)
 * @see android.support.v4.app.Fragment#onDestroy()
 */
@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    Log.e("onDestroy","onDestroy");
}

/* (non-Javadoc)
 * @see android.support.v4.app.Fragment#onStart()
 */
@Override
public void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
    Log.e("onstart","onstart");
}

/* (non-Javadoc)
 * @see android.support.v4.app.Fragment#onStop()
 */
@Override
public void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    if(false){
        Bundle savedInstance=getArguments();
        LinearLayout viewParent;

        viewParent= (LinearLayout) rootView.getParent();
        viewParent.removeView(rootView);

    }
    parentView.removeView(rootView);

    Log.e("onStop","onstop");
}
@Override
public void onPause() {
    super.onPause();
    Log.e("onpause","onpause");
}

@Override
public void onResume() {
    super.onResume();
    Log.e("onResume","onResume");
}

And here is the MainActivity:

/**
 * Fragment managing the behaviors, interactions and presentation of the
 * navigation drawer.
 */
private NavigationDrawerFragment mNavigationDrawerFragment;

/**
 * Used to store the last screen title. For use in
 * {@link #restoreActionBar()}.
 */

public static boolean fragment2InstanceExists=false;
public static Fragment2 fragment2=null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    setContentView(R.layout.activity_main);

    mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
            .findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
    switch(position){
    case 0:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, Fragment1.newInstance(position+1)).commit();
        break;
    case 1:

        Bundle bundle=new Bundle();
        bundle.putInt("source_of_create",CommonMethods.CREATE_FROM_ACTIVITY);

        if(!fragment2InstanceExists){
            fragment2=Fragment2.newInstance(bundle);
            fragment2InstanceExists=true;
        }
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, fragment2).commit();

        break;
    case 2:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, FolderExplorerFragment.newInstance(position+1)).commit();
        break;
    default: 
        break;
    }
}

The parentView is the keypoint. Normally, when onCreateView, we just use return rootView. But now, I add rootView to parentView, and then return parentView. To prevent "The specified child already has a parent. You must call removeView() on the ..." error, we need to call "parentView.removeView(rootView)", or the method I supplied is useless. I also would like to share how I found it. Firstly, I set up a boolean to indicate if the instance exists. When the instance exists, the rootView will not be inflated again. But then, logcat gave the child already has a parent thing, so I decided to use another parent as a intermediate Parent View. That's how it works. Hope it's helpful to you.

Let me write my way to handle that problem. I keep an fragment object variable in the Activity, for later interaction. In onCreate() method I check if there is a savedInstanceState (caused by screen rotation or something else).

public class MainActivity extends AppCompatActivity {
PlaceAddFragment mFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if(savedInstanceState == null) {
        mFragment = new PlaceAddFragment();
        mFragment.setProfileImages(incomingPics);

        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.form_fragment_holder, mFragment, PlaceAddFragment.TAG)
                .commit();
    }else{
        mFragment = (PlaceAddFragment) getSupportFragmentManager().findFragmentByTag(PlaceAddFragment.TAG);
    }
}

In my fragment class I override onSaveInstanceState method and in it just add setRetainInstance(true), as shown below:

public class PlaceAddFragment extends Fragment {
public static final String TAG = ".PlaceAddFragment";
private ProfileImageAdapter mAdapter;
private List<ProfileImage> mProfileImages;
private Context mContext;

public PlaceAddFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    mContext = getContext();
    View view = inflater.inflate(R.layout.fragment_place_add, container, false);

    RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.hw_pictures_holder);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
    mAdapter = new ProfileImageAdapter(mContext, mProfileImages);
    mRecyclerView.setAdapter(mAdapter);

    return view;
}

@Override
public void onSaveInstanceState(final Bundle outState) {
    super.onSaveInstanceState(outState);
    setRetainInstance(true);
}

@Override
public void onDestroy() {
    super.onDestroy();
    mAdapter = null;
    mProfileImages = null;
}

public void setProfileImages(List<ProfileImage> incomingPics) {
    this.mProfileImages = incomingPics;
}

public List<ProfileImage> getProfileImages() {
    return this.mProfileImages;
}
}

If you want this code to work you should implement ProfileImageAdapter mAdapter, ProfileImage object and all xml layouts for those.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top