Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate views on orientation change without recreating layout?

This question has been asked before here but its answer is incorrect. I would like to rotate some views on screen orientation change, but I want to keep the layout unchanged. They should be rotated 90, 180, 270 or 360 degrees according to the current orientation (SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_PORTRAIT, SCREEN_ORIENTATION_REVERSE_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_PORTRAIT).

This is what I want to achieve:

LayoutExample

The answer in the link I mentioned stated that I should create a new different layout in layout-land. Clearly, this is not what I want. I don't want to recreate the activity or change layout orientation. I only want to rotate some views, and keep other views unchanged on orientation change.

There is a huge difference between rotating specific views and changing or recreating the whole layout (both on orientation change).

Using the answer in this link, I will be able to get the current screen orientation with this method:

public static int getScreenOrientation(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    int rotation = windowManager.getDefaultDisplay().getRotation();
    DisplayMetrics dm = new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(dm);
    int width = dm.widthPixels;
    int height = dm.heightPixels;
    int orientation;
    // if the device's natural orientation is portrait:
    if ((rotation == Surface.ROTATION_0
            || rotation == Surface.ROTATION_180) && height > width ||
            (rotation == Surface.ROTATION_90
                    || rotation == Surface.ROTATION_270) && width > height) {
        switch (rotation) {
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_180:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            case Surface.ROTATION_270:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            default:
                Log.e("ScreenOrientation", "Unknown screen orientation. Defaulting to " + "portrait.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
        }
    }
    // if the device's natural orientation is landscape or if the device
    // is square:
    else {
        switch (rotation) {
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_180:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            case Surface.ROTATION_270:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            default:
                Log.e("ScreenOrientation", "Unknown screen orientation. Defaulting to " + "landscape.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
        }
    }

    return orientation;
}

On orientation change, I would like to do something simple like this:

RotateAnimation rotateAnimation = new RotateAnimation(0, getScreenOrientation(getContext()));
rotateAnimation.setDuration(2000);

for (int i = 0; i < 63; i++) {
    Button button = (Button) rootView.findViewById(i);
    button.startAnimation(rotateAnimation);
}

Another way to rephrase my question would be "Is there any way to detect orientation change in onConfigurationChanged() method without changing the layout?". The problem is that it will not be able to detect any orientation change if I already disable layout orientation change.

Anyone knows how it is done? I might have totally gone through wrong steps, and I think I will have to use Accelerometer Sensor or something similar to that to achieve what I want, so please guide me through.

like image 933
Hussein El Feky Avatar asked Jan 22 '16 21:01

Hussein El Feky


People also ask

How do you prevent data from reloading and resetting when the screen is rotated?

Prevent Activity to recreated Most common solution to dealing with orientation changes by setting the android:configChanges flag on your Activity in AndroidManifest. xml. Using this attribute your Activities won't be recreated and all your views and data will still be there after orientation change. Save this answer.

How do I change viewing screen orientation?

Select the Start button, then type settings. Select Settings > System > Display, and choose a screen orientation from the drop-down list next to Display orientation.

Why an activity is destroyed and recreated when you rotate your screen?

When you rotate your device and the screen changes orientation, Android usually destroys your application's existing Activities and Fragments and recreates them . Android does this so that your application can reload resources based on the new configuration.

How retain data on orientation change explain lifecycle during orientation changes?

You can save Primitive data such as String, Boolean, Integers or Parcelable objects in a Bundle during the orientation change and read the same data when Activity recreated. Saving and restoring the data works using two Activity lifecycle methods called onSaveInstanceState() and onRestoreInstanceState().


2 Answers

Try to use OrientationEventListener. You don't need to use onConfigurationChanged and android:configChanges="orientation|keyboardHidden|screenSize".

You need set android:screenOrientation="portrait" for the activity in AndroidManifest.xml. Here is my solution with OrientationEventListener:

public class MyActivity extends Activity{

private ImageButton menuButton;

private Animation toLandAnim, toPortAnim;
private OrientationListener orientationListener;

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

    menuButton=(ImageButton)findViewById(R.id.menu_button);
    toLandAnim= AnimationUtils.loadAnimation(this, R.anim.menubutton_to_landscape);
    toPortAnim= AnimationUtils.loadAnimation(this, R.anim.menubutton_to_portrait);

    orientationListener = new OrientationListener(this);
}

@Override protected void onStart() {
    orientationListener.enable();
    super.onStart();
}

@Override protected void onStop() {
    orientationListener.disable();
    super.onStop();
}

private class OrientationListener extends OrientationEventListener{
    final int ROTATION_O    = 1;
    final int ROTATION_90   = 2;
    final int ROTATION_180  = 3;
    final int ROTATION_270  = 4;

    private int rotation = 0;
    public OrientationListener(Context context) { super(context); }

    @Override public void onOrientationChanged(int orientation) {
        if( (orientation < 35 || orientation > 325) && rotation!= ROTATION_O){ // PORTRAIT
            rotation = ROTATION_O;
            menuButton.startAnimation(toPortAnim);
        }
        else if( orientation > 145 && orientation < 215 && rotation!=ROTATION_180){ // REVERSE PORTRAIT
            rotation = ROTATION_180;
            menuButton.startAnimation(toPortAnim);
        }
        else if(orientation > 55 && orientation < 125 && rotation!=ROTATION_270){ // REVERSE LANDSCAPE
            rotation = ROTATION_270;
            menuButton.startAnimation(toLandAnim);
        }
        else if(orientation > 235 && orientation < 305 && rotation!=ROTATION_90){ //LANDSCAPE
            rotation = ROTATION_90;
            menuButton.startAnimation(toLandAnim);
        }
    }
}
}

This also prevents from too frequent rotations when orientation is about 45, 135... etc. Hope it helps.

like image 79
mirekkomis Avatar answered Nov 03 '22 10:11

mirekkomis


The basics are actually a lot easier. Have a look at Handling Runtime Changes.

First things first, by setting

android:configChanges="orientation|keyboardHidden|screenSize"

in your Manifest on your activity tag you can handle the orientation change yourself. (orientation should be enough, but there are sometimes issues where the event does not fire with that alone.)

You then skip onCreate and instead onConfigurationChanged gets called. Overwrite this method and apply your layout changes here. Whether you change your linearLayouts orientation here or have a custom view handling layout for different screens itself is up to you and depends on your implementation.

Animating will be a bit trickier, if it is even possilbe. A quick search says it is not.


Update for comment "I only want to rotate some views themselves rather than rotating the layout"

In theory it is possible to create your own layout and handle the drawing of your child views. I just tried it but could not produce any results in an appropriate time, but what you would need to do:

  • keep your last measured values use tags on the view or similar approaches to keep the last measurements and layouts, so that after the orientation change you can diff
  • await orientation change: trigger rotated drawing - rotate the canvas, layout the views with the previous dimensions, and draw the child views where they would have been before, and
  • start an animation interpolate from the last to the new values, rotating the canvas from the last to the new layout

This is how I would do it.

like image 1
David Medenjak Avatar answered Nov 03 '22 08:11

David Medenjak