Android Basic Training Course: Handling Rotation

    Most of Android devices support screen rotation from portrait to landscape using the accelerometer to determine when user rotate the it. The action result is that, the screen changes from portrait to landscape and vice versa. As the title of this post said, Android has multiple ways to handle screen-rotate so that your application can handle correctly any manipulation relating to the rotation. However, remember that these facilities can only help you detect and manage the process of rotating the screen - you still have to ensure that your layout looks reasonable in each spin.

The philosophy of Activity/View destruction

    By default, when there is a change in the settings of the device, it will be able to influence the choice of resources, Android will destroy and recreate any running or paused activity. This happens for some kind of change various settings, including:
  • Rotating device screen.
  • Change the location or change preferred language.
  • Expand or hide a physical keyboard on the device which has sliding keyboard.
    The key here is the default behavior of Android in destroying and recreating any activity running or paused may be regarded as the best behavior for most of your activity. However, you have some control on this issue and can change how your activity in response to the screen rotation or similar switch configuration.
    Now, considering with this example. Firstly, we have a simple Activity layout with only Button and TextView:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click Me!" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:gravity="center"
        android:text="This is a TextView"
        android:textStyle="bold" />
</LinearLayout>

    And this is programmatically code:
MainActivity.java
package info.devexchanges.handlingrotation;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private View button;

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

        textView = (TextView) findViewById(R.id.text);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                textView.setText("Button clicked!");
            }
        });
    }
}

    When running app, we will have this output screen:
    When click the Button:
    But when you rotate device, it's will reset to it's original state:

Saving your Activity state

    As default, Android will destroy and recreate your activity when rotating the screen, you just need to call the same method onSaveInstanceState() that you want regardless of your activity is destroyed by any reason. That means you must provide sufficient information to Bundle object, it will help you return to the current state of activity (state before destroyed). Then, in the method onCreate() (or methods onRestoreInstanceState() if you want), get data from objects Bundle up and use them to restore your activity as before.
    Rewrite the above Java code like this:
MainActivity.java
package info.devexchanges.handlingrotation;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private View button;

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

        textView = (TextView) findViewById(R.id.text);

        if (savedInstanceState != null) {
            textView.setText(savedInstanceState.getString("message"));
        }

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                textView.setText("Button clicked!");
            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("message", textView.getText().toString());
    }
}

    By this way, the TextView content is preserved when you rotate the device!

Now With More Savings!

    The problem with the method onSaveInstabceState() that you are limited to only one object Bundle. Because this callback method is also used in situations where your all processes is terminated, so the data that needs to be saved must be something might sequentially and not depend on your running processes.
    One way to get past this is to use onRetainNonConfigurationInstance() instead of  onSaveInstanceState() for “light” changes like a rotation. Your activity’s  onRetainNonConfigurationInstance() callback can return an Object, which you can retrieve later via getLastNonConfigurationInstance(). The Object can be just about anything you want - typically, it will be some kind of “context” object holding activity state, such as running threads, open sockets, and the like. Your activity’s Object can call getLastNonConfigurationInstance() - if you get a non-null response, you now have your sockets and threads and whatnot. The biggest limitation is that you do not want to put in the saved context anything that might reference a resource that will get swapped out, such as a Drawable loaded from a resource.
    Rewrite above code with this way:
MainActivity.java
package info.devexchanges.handlingrotation;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

    private TextView textView;
    private View button;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);

        //check if data saved
        final String data = (String) getLastNonConfigurationInstance();
        if (getLastNonConfigurationInstance() != null) {
            textView.setText(data);
        }

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                textView.setText("Button clicked!");
            }
        });
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return textView.getText().toString();
    }
}

    Important Note: In order to use onRetainNonConfigurationsInstance() , your activity must extend Activity class, not FragmentActivity or it's subclass.
    However, even approaches using onRetainNonConfigurationInstance() in handling screen rotation can still cause many problems for your application. Moreover, this method is now is deprecated, So I do not recommend using this way, with the much complicated data, you should use other official solutions in saving data (Preferences, Files or database) and managing the loading process. Another way of handling rotation is use onConfigurationChanged()  method, but Google is not recommended it, too. So, I wouldn't like to present it!

Prevent screen rotation

    Some simple activity is not need to rotate the screen. To prevent Android rotate your activity, all that you need is to add the attribute android:screenOrientation="portrait" (or landscape if you want) in AndroidManifest.xml file as follows:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.devexchanges.handlingrotation">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:screenOrientation="portrait"
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

    Since it only applies to each activity, so you will need to decide what their activity should enable this feature. With settings in AndroidManifest.xml file as above, now your application will always remain way under value you specify (in this example, it will always be portrait) regardless of whatever you can do.

Conclusions

   Handling orientation change in android is bit interesting topic to explore. on change of orientation your Android device's width changes to height and vice versa. It make developer to force some changes in layout for better user experience. With my guide, I hope that you can dealing with this problem better. Thanks for reading!



Share


Previous post
« Prev Post
Next post
Next Post »