How to save image inside ViewModel to handle orientation change?

Viewed 2089

My activity contains one imageview and a button, when button is clicked the image inside the imageview is changed (works fine).

But when screen orientation changes the image inside imageview is lost (because activity is recreated).

I read in some post that ViewModel survives orientation and it can store large amount of data (bitmap) as compared to onSaveInstanceState.

There are many others options to retreive the same image, but i want to use ViewModel to get that same image without getting affected by orientation change.

So, How can i get the same image using ViewModel?

Thankyou.

2 Answers

Simple example to work with TextView and ImageView using ViewModel to survive orientation changes

Example Explanation:

Layout contains a TextView, ImageView and a Button. Initally text and image is set on TextView and ImageView inside layout. When button is clicked the text(programatically) and image(from drawable) set to the appropriate View.

Suppose Device is rotated and hence the text and Image which is set when button is clicked is lost due to activity recreation and therefore loads the text and image which was set in xml file.

To solve this we are using ViewModel to handle Configuration change.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/my_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Android" />

    <ImageView
        android:id="@+id/my_image"
        android:layout_width="96dp"
        android:layout_height="96dp"
        android:layout_margin="20dp"
        android:src="@drawable/ic_android_black_24dp" />

    <Button
        android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:text="Update text and image" />

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    TextView mText;
    ImageView mImageView;
    Button mButton;

    MyViewModel mViewModel;

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

        mText = findViewById(R.id.my_text);
        mImageView = findViewById(R.id.my_image);
        mButton = findViewById(R.id.my_button);

        mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

        if (mViewModel.getText() != null) {
            mText.setText(mViewModel.getText());
        }

        if (mViewModel.getImage() != null) {
            mImageView.setImageDrawable(mViewModel.getImage());
        }

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mText.setText("Happy Coding...");
                mImageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_face));
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mViewModel.setText(mText.getText().toString());
        mViewModel.setImage(mImageView.getDrawable());

    }
}

MyViewModel.java

public class MyViewModel extends ViewModel {

    private String text;
    private Drawable image;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Drawable getImage() {
        return image;
    }

    public void setImage(Drawable image) {
        this.image = image;
    }
}

Few tips about ViewModel:

  1. The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations.
  2. ViewModels separate UI implementation from your app’s data. The lifecycle of a ViewModel extends from when the associated UI controller is first created, till it is completely destroyed.
  3. Never store a UI controller or Context directly or indirectly in a ViewModel. This includes storing a View in a ViewModel. Direct or indirect references to UI controllers defeat the purpose of separating the UI from the data and can lead to memory leaks.
  4. ViewModel also works very nicely with another Architecture Component like LiveData, Room. The added bonus here of using LiveData is that it’s observable: it can trigger UI updates when the data changes. Use below links to learn more.

Links: https://developer.android.com/topic/libraries/architecture/viewmodel

https://developer.android.com/topic/libraries/architecture/livedata.html

CodeLab Links: https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

I am not a professional developer. Above code works for me. Welcome to any correction or suggestions.

Thankyou ;)

Your ViewModel should have a method changeImage which changes internal state of ViewModel. You have to somehow indicate activity about this changes. For this purpose it is better to use val imageId = MutableLiveData<Int>().

changeImage changes this value. In Activity, you can subscribe for changes and apply new image whenever imageId is changed.

Related