Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep an image in an Android activity during an orientation change?

In my app allows to choose an app on user device such as 'Gallery' to select an image. This image is displayed in an imageview window on the activity screen. Unfortunately, when I turn my phone and the orientation changes, the image is lost and they have to go select it again.

How do I save the bitmap image and have it re-displayed after the orientation change?

Here is the code function I use to let the user pick an image app, pick an image, and display the image in the imageview window.

@Override
protected void onActivityResult( int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    
    if (resultCode == RESULT_OK){
        Uri targetUri = data.getData();
        textTargetUri.setText(targetUri.toString());
        Bitmap bitmap;
        try {
            bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(targetUri));
            targetImage.setImageBitmap(bitmap);
        } 
        catch (FileNotFoundException e){
            e.printStackTrace();
        }
    }   
}

The above function is initialised within my onCreate in response to a button click shown in this code:

Button buttonLoadImage = (Button)findViewById(R.id.loadimage);
textTargetUri = (TextView)findViewById(R.id.targeturi);
targetImage = (ImageView)findViewById(R.id.targetimage);
        
buttonLoadImage.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
    Intent   intent = new Intent(Intent.ACTION_PICK,
    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, 0);
}});

Essentially, I just need to put this boolean in my onCreate and have it put the image back in the window. It also displays text showing where the image was located on the device but that is not as important to me as being able to have the image stay on the screen.

if (savedInstanceState != null) {
        
            
} 

But it seems to wipe the data after the orientation change? I have no idea how to save the data and restore it to be used within that boolean. I appreciate any advice, code, or links you can provide!


UPDATE:

Using both of advises in comments, as well as some code I had found from previous research, I was able to compile a solution. However, while it solved the first issue, it spawned another which I will describe after showing the code I used to fix the initial problem.

I found that I had 2 options available for the restoration portion.

Either I could place this in the onCreate:

if (savedInstanceState != null) {
        //if there is a bundle, use the saved image resource (if one is there)
        image = savedInstanceState.getParcelable("BitmapImage");
        targetImage.setImageBitmap(image);
        textTargetUri.setText(savedInstanceState.getString("path_to_picture"));R.drawable.camera_button);
    }   

This allowed me to have both the image as well as the path to it on the device to be re-displayed on my activity.

Or I could have this after the onCreate

@Override 
protected void onRestoreInstanceState(Bundle savedInstanceState){       
        image = savedInstanceState.getParcelable("BitmapImage");
        targetImage.setImageBitmap(image);
        textTargetUri.setText(savedInstanceState.getString("path_to_picture"));
}

and the saving function that I used was

@Override 
public void onSaveInstanceState(Bundle savedInstanceState){
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putParcelable("BitmapImage", bitmap);
    savedInstanceState.putString("path_to_picture", picture_location);
}   

To clarify where picture_location came from, back in my onActivityResult function I added a line and changed another to save the path string

if (resultCode == RESULT_OK){
    Uri targetUri = data.getData();
    picture_location = targetUri.toString();
    textTargetUri.setText(picture_location);

As for variables initialized before the onCreate,

Bitmap image;
Bitmap bitmap;
String picture_location;

Now, I realize this has gotten rather lengthy but I hope it will help others who may have a similar problem. As for the new issue that I mentioned earlier, when I rotate my android device, it does in fact keep the data and re-display it as I was trying to do. However, when I rotate the screen again, the original issue arises; the data seems to be lost and is not displayed on the screen.

I would hope there is a much more simple fix so that the save and restore functions keep working no matter how many times the screen orientation changes?

like image 278
DeadJohnDoe Avatar asked Jan 11 '14 06:01

DeadJohnDoe


4 Answers

Bitmap image;

public void onCreat(..){
    if (savedInstanceState != null) {
        image = (Bitmap) savedInstanceState.getParcelableExtra("BitmapImage");; 
    } else { 
        image = yourBitmapImage;
    } 
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putExtra("BitmapImage", bitmap);
}
like image 183
Vishal Vijay Avatar answered Oct 19 '22 12:10

Vishal Vijay


I think it costs too much if you save a Bitmap in onSaveInstanceState,as onSaveInstanceState will be called before onStop every time,and Bitmap is a big thing.

I think is better to save a URI in onSaveInstanceState

Uri mUri;
ImageView mImageView;

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ...
  if (savedInstanceState != null){
    mUri = savedInstanceState.getParcelable("uri");
    mImageView.setImageURI(mUri);
  }
}


protected void onActivityResult( int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK){
        Uri targetUri = data.getData();
        ...
        mUri = targetUri;
    }   
}
public void onSaveInstanceState(Bundle outState) {
        if (mURI !=null) {
            outState.putParcelable("uri", mUri);
        }
    }
like image 32
footman Avatar answered Oct 19 '22 12:10

footman


We should never use the above way to save our heavy objects such as image or bitmap it is always advisable to use the fragments. Have a look at this an easy implementation of how to retain the fragment https://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject

like image 3
cammando Avatar answered Oct 19 '22 13:10

cammando


according to the developer guide https://developer.android.com/topic/performance/graphics/cache-bitmap.html.

the recommended way is to use setRetainInstance(true) in the fragment to retain images.

refer to section on "Handle Configuration Changes"

Runtime configuration changes, such as a screen orientation change, cause Android to destroy and restart the running activity with the new configuration (For more information about this behavior, see Handling Runtime Changes). You want to avoid having to process all your images again so the user has a smooth and fast experience when a configuration change occurs.

Luckily, you have a nice memory cache of bitmaps that you built in the Use a Memory Cache section. This cache can be passed through to the new activity instance using a Fragment which is preserved by calling setRetainInstance(true)). After the activity has been recreated, this retained Fragment is reattached and you gain access to the existing cache object, allowing images to be quickly fetched and re-populated into the ImageView objects.

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    RetainFragment retainFragment =
            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
    mMemoryCache = retainFragment.mRetainedCache;
    if (mMemoryCache == null) {
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            ... // Initialize cache here as usual
        }
        retainFragment.mRetainedCache = mMemoryCache;
    }
    ...
}

class RetainFragment extends Fragment {
    private static final String TAG = "RetainFragment";
    public LruCache<String, Bitmap> mRetainedCache;

    public RetainFragment() {}

    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
        if (fragment == null) {
            fragment = new RetainFragment();
            fm.beginTransaction().add(fragment, TAG).commit();
        }
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}
like image 1
Angel Koh Avatar answered Oct 19 '22 12:10

Angel Koh