如何在Android活动中保留图像在方向更改期间?

7

我的应用程序允许用户选择设备上的应用程序,例如“相册”,以选择图像。该图像在活动屏幕上的ImageView窗口中显示。不幸的是,当我转动手机并且方向改变时,图像会丢失,他们必须再次选择。

如何保存位图图像并在方向更改后重新显示它?

这是我用来让用户选择图像应用程序、选择图像并将图像显示在ImageView窗口中的代码函数。

@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();
        }
    }   
}

上述函数在我的onCreate中被初始化,以响应此代码中显示的按钮点击:
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);
}});

基本上,我只需要在onCreate中放置这个布尔值,并让它将图像放回窗口。它还显示文本,显示图像在设备上的位置,但对我来说,让图像保持在屏幕上更重要。

if (savedInstanceState != null) {
        
            
} 

但是似乎在更改方向后会清除数据? 我不知道如何保存数据并将其还原以在该布尔值内使用。 感谢您提供的任何建议、代码或链接!

更新:

使用评论中的建议以及我之前找到的一些代码,我能够编译出一个解决方案。然而,虽然它解决了第一个问题,但它引发了另一个问题,我将在展示用于解决最初问题的代码后描述这个问题。

我发现恢复部分有2个可用选项。

要么在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);
    }   

这使我能够在应用程序中重新显示图像及其路径。

或者我可以在onCreate之后执行此操作。

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

我使用的保存功能是:
@Override 
public void onSaveInstanceState(Bundle savedInstanceState){
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putParcelable("BitmapImage", bitmap);
    savedInstanceState.putString("path_to_picture", picture_location);
}   

为了澄清picture_location的来源,在我的onActivityResult函数中,我添加了一行代码并更改了另一行代码以保存路径字符串。
if (resultCode == RESULT_OK){
    Uri targetUri = data.getData();
    picture_location = targetUri.toString();
    textTargetUri.setText(picture_location);

关于在onCreate之前初始化的变量,

Bitmap image;
Bitmap bitmap;
String picture_location;

现在,我意识到这已经变得相当冗长了,但我希望它能帮助那些可能有类似问题的人。至于我之前提到的新问题,当我旋转我的安卓设备时,它确实保留了数据并重新显示了它,就像我想要做的那样。然而,当我再次旋转屏幕时,原来的问题出现了;数据似乎丢失了,没有显示在屏幕上。
我希望有一个更简单的解决方法,使保存和恢复功能无论屏幕方向改变多少次都能正常工作。

请使用Android Query进行操作。 - Kishan Dhamat
7个回答

6

我认为如果在onSaveInstanceState中保存Bitmap的话,成本太高了,因为每次onStop之前都会调用onSaveInstanceState,而且Bitmap是一个很大的东西。

我认为最好在onSaveInstanceState中保存一个URI

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);
        }
    }

5
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);
}

如果我没记错的话,eclipse会抱怨putExtra和getParcelableExtra方法对于Bundle类型是未定义的。我可能错误地集成了你的代码才会发生这个错误。使用if{} else{}而不是只使用if(!=)是否有原因?使用if else能解决它只在一个方向变化时有效的问题吗? - DeadJohnDoe

3

1
根据开发者指南https://developer.android.com/topic/performance/graphics/cache-bitmap.html,推荐的方法是在片段中使用setRetainInstance(true)来保留图像。请参阅“处理配置更改”部分。
运行时配置更改(例如屏幕方向更改)会导致Android销毁并重新启动具有新配置的运行中活动(有关此行为的更多信息,请参见处理运行时更改)。您希望避免再次处理所有图像,以便用户在发生配置更改时拥有平稳快速的体验。
幸运的是,在“使用内存缓存”部分中,您构建了一个不错的位图内存缓存。可以通过Fragment将此缓存传递给新的活动实例,并通过调用setRetainInstance(true))来保留它。在活动重新创建后,该保留的Fragment被重新附加,您可以访问现有缓存对象,从而快速获取并重新填充到ImageView对象中。
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);
    }
}

0
使用 onSaveInstanceState 来保存图片。
if (savedInstanceState == null) {
    image = R.drawable.default;
} else {
     // if there is a bundle, use the saved image resource (if one is there)
     image = savedInstanceState.getInt(IMAGE_RESOURCE, R.drawable.default);
}

@Override
public void onSaveInstanceState(Bundle outState) {
    // Make sure you save the current image resource 
    outState.putInt(IMAGE_RESOURCE, image);
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    outState.getInt(IMAGE_RESOURCE,image);
    super.onRestoreInstanceState(state);
}

有没有理由使用if{} else{}而不是我使用的if(!=){}?使用if else能解决只在一个方向更改时有效的问题吗? - DeadJohnDoe

0
如果您正在使用Glide进行图像加载,那么可以使用DiskCacheStrategy来轻松处理。
Glide.with(this)
            .load(imageUrl)
            .transition(withCrossFade())
            .placeholder(placeholderImage)
            .skipMemoryCache(true)
            .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
            .into(image_view)

这里使用了DiskCacheStrategy.RESOURCE来实现。


-1
在 Manifest 文件中为您的 Activity 添加 android:configChanges="keyboardHidden|orientation"

我之前在您的答案中找到了一个“解决方案”,但很遗憾它并不起作用。至少,对于我的程序来说是这样。 - DeadJohnDoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接