Search code examples
androidviewmodel

How to save image inside ViewModel to handle orientation change?


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.


Solution

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