Reputation: 5887
I have PhotosFrgament class hosted by a MainActivity with a tablayout. The PhotoFragment allows a user to select multiple photos from a custom gallery and displays them in a ListView. However on screen rotation, the PhotosFragment is cleared. According to my understanding, I should use on setRetainInstance(true) but it doesnt seem to work, any ideas?
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager viewPager;
private static int TAB_ITEMS=1;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set a Toolbar to replace the ActionBar.
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setTitle(R.string.photos);
tabLayout = (TabLayout)findViewById(R.id.tabs);
viewPager = (ViewPager)findViewById(R.id.viewpager);
/**
*Set an Apater for the View Pager
*/
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
/**
* Now , this is a workaround ,
* The setupWithViewPager dose't works without the runnable .
* Maybe a Support Library Bug .
*/
tabLayout.post(new Runnable() {
@Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
//check for parent activity defined in the manifest before
//displaying caret
if(NavUtils.getParentActivityName(this) != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}//end method onCreate
class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
/**
* Return fragment with respect to Position .
*/
@Override
public Fragment getItem(int position)
{
switch (position){
case 0 :
return new Photos();
}
return null;
}
@Override
public int getCount() {
return TAB_ITEMS;
}
/**
* This method returns the title of the tab according to the position.
*/
@Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0 :
return "Photos";
}
return null;
}
}//end class MyAdapter
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case android.R.id.home:
//if there is a parent activity declared in the manifest
//then navigate to the parent activity
if(NavUtils.getParentActivityName(this) != null)
NavUtils.navigateUpFromSameTask(this);
return true;
default:
return super.onOptionsItemSelected(item);//implement superclass
//implementation
}
}//end method onOptionsItemSelected
}//end class MainActivity
public class PhotosFragment extends ListFragment {
private static final int SELECT_MULTIPLE_PHOTOS = 0;//Set Intent Id
public static final String CustomGalleryIntentKey = "ImageArray";//Set Intent Key Value
private static final String TAG = "PhotosFragment";
private EditText mImagesDescriptionEditText;
private RelativeLayout mImagesDescriptionRelativeLayout;
private ListView mSelectedImagesListView;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);//retain fragment on configuration changes
setHasOptionsMenu(true);//fragment will be implementing menu call backs
//on behalf of activity
}//end method onCreate
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent
,Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment_photos, parent,false);
mSelectedImagesListView = (ListView)v.findViewById(android.R.id.list);
mImagesDescriptionRelativeLayout = (RelativeLayout)
v.findViewById(R.id.photos_descriptionRelativeLayout);
mImagesDescriptionEditText = (EditText)v.findViewById(R.id.photos_descriptionEditText);
mImagesDescriptionRelativeLayout.setVisibility(View.INVISIBLE);
return v;
}//end method onCreateView
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
super.onCreateOptionsMenu(menu,inflater);
inflater.inflate(R.menu.fragment_photos, menu);
}//end method onCreateOptionsMenu
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.menu_item_upload_photo:
//Start Custom Gallery Activity by passing intent id
Intent i = new Intent(getActivity(),CustomGalleryActivity.class);
startActivityForResult(i,SELECT_MULTIPLE_PHOTOS);
return true;
default:
return super.onOptionsItemSelected(item);
}//end switch
}//end onOptionsItemSelected
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode != Activity.RESULT_OK)
return;
if(requestCode == SELECT_MULTIPLE_PHOTOS){
String imagesArray = data.getStringExtra(CustomGalleryIntentKey);//get Intent data
//Convert string array into List by splitting by ',' and substring after '[' and before ']'
List<String> selectedImages = Arrays.asList(imagesArray.substring(1, imagesArray.length() - 1).split(", "));
loadSelectedImagesListView(new ArrayList<String>(selectedImages));
}
}
private void loadSelectedImagesListView(ArrayList<String> imagesArray){
try{
mImagesDescriptionRelativeLayout.setVisibility(View.VISIBLE);
}catch (Throwable e){
e.printStackTrace();
}
SelectedImagesAdapter adapter = new SelectedImagesAdapter(imagesArray);
mSelectedImagesListView.setAdapter(adapter);
}
//custom adapter as inner class
private class SelectedImagesAdapter extends ArrayAdapter<String>{
public SelectedImagesAdapter(ArrayList<String> imagesArray){
super(getActivity(),0,imagesArray);
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
//if we arent given a view inflate one
//if there's no recycled view passed in inflate one
if(convertView == null)
convertView = getActivity()
.getLayoutInflater()
.inflate(R.layout.list_item_selected_image, null);
//configure the view for this image
String imagePath = getItem(position);
ImageView imageView = (ImageView)convertView.findViewById(R.id.selected_imageImageView);
Bitmap bm = BitmapFactory.decodeFile(imagePath);
//get the screen width at run time
int screenWidth = DeviceDimensionsHelper.getDisplayWidth(getActivity());
//load resized bitmap into an ImageView
imageView.setImageBitmap(BitmapScaler.scaleToFitWidth(bm, screenWidth));
EditText caption = (EditText)convertView.findViewById(R.id.selected_imageCaption);
return convertView;//return view object to the list view
}
}//end private class ImagesAdapter
}//end class PhotosFragment
Upvotes: 0
Views: 2420
Reputation: 5887
Solved the problem. I used onSaveInstanceSate, then retrieved the saved bundle in method onCreateView.
@Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putStringArrayList(KEY_IMAGE_LIST, mSelectedImages);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent
,Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment_photos, parent,false);
mSelectedImagesListView = (ListView)v.findViewById(android.R.id.list);
mImagesDescriptionRelativeLayout = (RelativeLayout)
v.findViewById(R.id.photos_descriptionRelativeLayout);
mImagesDescriptionEditText = (EditText)v.findViewById(R.id.photos_descriptionEditText);
mImagesDescriptionRelativeLayout.setVisibility(View.INVISIBLE);
if(savedInstanceState != null){
mSelectedImages = savedInstanceState.getStringArrayList(KEY_IMAGE_LIST);
loadSelectedImagesListView(mSelectedImages);
}
return v;
}//end method onCreateView
Upvotes: 1
Reputation: 2321
Whilst you can store your selected items in a Bundle with onSaveInstanceState
, they have to implement the Parcelable
interface.
If you don't want to be bound by what you retain, consider using a headless fragment.
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
You can find out more here, it explains why the use of headless fragments is preferable to just setting the "configChanges" attribute.
PS. as a general rule of thumb, only use setRetainInstance(true)
for fragments that don't display a view!
Upvotes: 1