How do I detect landscape mode while maintaining my layouts in portrait mode?
I have an activity which maintains the portrait mode irrespective of actual mobile orientation. I can do this by adding android:screenOrientation="portrait" to my activity configuration in manifest.
However, I want to detect screenOrientation while maintaining the activity in portrait.
I have tried with following code:
#Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
recordConfigChanges(newConfig); // Performs some variable changes or similar
}
// Maintain activity in portrait mode itself
Configuration customConfig = newConfig;
customConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
super.onConfigurationChanged(customConfig);
}
The above code does not maintain portrait, it rotates the UI to landscape mode.
Re-iterating the original question, how do I detect landscape mode while maintaining my layouts in portrait mode?
You can use CustomOrientationEventListener without affect actual Orientation change
in your activity
...
private CustomOrientationEventListener customOrientationEventListener;
...
customOrientationEventListener = new CustomOrientationEventListener(mContext) {
#Override
public void onSimpleOrientationChanged(int orientation) {
}
};
customOrientationEventListener.enable();
standalone class CustomOrientationEventListener
public abstract class CustomOrientationEventListener extends OrientationEventListener {
private String TAG="CustomOrientation";
private static final int CONFIGURATION_ORIENTATION_UNDEFINED = Configuration.ORIENTATION_UNDEFINED;
private volatile int defaultScreenOrientation = CONFIGURATION_ORIENTATION_UNDEFINED;
private int prevOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
private Context ctx;
private final int ROTATION_O = 1;
private final int ROTATION_90 = 2;
private final int ROTATION_180 = 3;
private final int ROTATION_270 = 4;
private int rotation = 0;
private ReentrantLock lock = new ReentrantLock(true);
public CustomOrientationEventListener(Context context) {
super(context);
ctx = context;
}
public CustomOrientationEventListener(Context context, int rate) {
super(context, rate);
ctx = context;
}
#Override
public void onOrientationChanged(final int orientation) {
//return if auto rotate disabled. should rotate if auto rotate enabled only
if (android.provider.Settings.System.getInt(ctx.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 0) // 0 = Auto Rotate Disabled
return;
int currentOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
if (orientation >= 340 || orientation < 20 && rotation != ROTATION_O) {
currentOrientation = Surface.ROTATION_0;
rotation = ROTATION_O;
} else if (orientation >= 70 && orientation < 110 && rotation != ROTATION_90) {
currentOrientation = Surface.ROTATION_90;
rotation = ROTATION_90;
} else if (orientation >= 160 && orientation < 200 && rotation != ROTATION_180) {
currentOrientation = Surface.ROTATION_180;
rotation = ROTATION_180;
} else if (orientation >= 250 && orientation < 290 && rotation != ROTATION_270) {
currentOrientation = Surface.ROTATION_270;
rotation = ROTATION_270;
}
if (prevOrientation != currentOrientation
&& orientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
prevOrientation = currentOrientation;
if (currentOrientation != OrientationEventListener.ORIENTATION_UNKNOWN)
reportOrientationChanged(currentOrientation);
}
}
private void reportOrientationChanged(final int currentOrientation) {
int defaultOrientation = getDeviceDefaultOrientation();
int orthogonalOrientation = defaultOrientation == Configuration.ORIENTATION_LANDSCAPE ? Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int toReportOrientation;
if (currentOrientation == Surface.ROTATION_0
|| currentOrientation == Surface.ROTATION_180)
toReportOrientation = defaultOrientation;
else
toReportOrientation = orthogonalOrientation;
onSimpleOrientationChanged(toReportOrientation);
}
/**
* Must determine what is default device orientation (some tablets can have
* default landscape). Must be initialized when device orientation is
* defined.
*
* #return value of {#link Configuration#ORIENTATION_LANDSCAPE} or
* {#link Configuration#ORIENTATION_PORTRAIT}
*/
private int getDeviceDefaultOrientation() {
if (defaultScreenOrientation == CONFIGURATION_ORIENTATION_UNDEFINED) {
lock.lock();
defaultScreenOrientation = initDeviceDefaultOrientation(ctx);
lock.unlock();
}
return defaultScreenOrientation;
}
/**
* Provides device default orientation
*
* #return value of {#link Configuration#ORIENTATION_LANDSCAPE} or
* {#link Configuration#ORIENTATION_PORTRAIT}
*/
private int initDeviceDefaultOrientation(Context context) {
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Configuration config = context.getResources().getConfiguration();
int rotation = windowManager.getDefaultDisplay().getRotation();
boolean isLand = config.orientation == Configuration.ORIENTATION_LANDSCAPE;
boolean isDefaultAxis = rotation == Surface.ROTATION_0
|| rotation == Surface.ROTATION_180;
int result = CONFIGURATION_ORIENTATION_UNDEFINED;
if ((isDefaultAxis && isLand) || (!isDefaultAxis && !isLand)) {
result = Configuration.ORIENTATION_LANDSCAPE;
} else {
result = Configuration.ORIENTATION_PORTRAIT;
}
return result;
}
/**
* Fires when orientation changes from landscape to portrait and vice versa.
*
* #param orientation
* value of {#link Configuration#ORIENTATION_LANDSCAPE} or
* {#link Configuration#ORIENTATION_PORTRAIT}
*/
public abstract void onSimpleOrientationChanged(int orientation);
}
Related
Hi I have a floating window in which my floating window serves as the video and it also have a controls inside the video which under the floating window
Now my question is it possible that I can rotate my custom view from floating window only without affecting the orientation of my activity
If someone already tried it please guide me towards it.
Thank you.
I have never done it before. But I have an idea. You can use the below code to get the current orientation of the screen on your device.
OrientationEventListener onrientationEventListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) {
#Override
public void onOrientationChanged(int rotation) {
Logger.e("Orientation: " + rotation);
}
};
And after that, depends on the value of "rotation", you can use rotate animation in Android to rotate your custom view.
#Beginer: Here is the code I implemented it. I used the above code in my custom view in my small camera app. It helps me to know which degree should I rotate the bitmap after taken. The toScreenOrientation() method bellow return a value in degrees (0, 90, 180, 270) you also modify it by yourself(whatever you want).
Using setOrientationChangedListener() method to help the parent(Activity, Fragment, etc.) receives a callback also.
public class TakePhotoView extends ConstraintLayout {
private static final int SCREEN_ORIENTATION_0 = 0;
private static final int SCREEN_ORIENTATION_90 = 90;
private static final int SCREEN_ORIENTATION_180 = 180;
private static final int SCREEN_ORIENTATION_270 = 270;
private OnOrientationChangedListener mOnOrientationChangedListener;
private OrientationEventListener mOrientationEventListener;
private int mScreenRotation;
public TakePhotoView(Context context) {
this(context, null);
}
public TakePhotoView(Context context, AttributeSet attrs) {
super(context, attrs);
//....do something here
mOrientationEventListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) {
#Override
public void onOrientationChanged(int rotation) {
Logger.e("Orientation: " + rotation);
if (rotation == OrientationEventListener.ORIENTATION_UNKNOWN) {
mScreenRotation = DEFAULT_SCREEN_ROTATION;
return;
}
mScreenRotation = rotation;
if(mOnOrientationChangedListener != null){
mOnOrientationChangedListener.onOrientationChanged(rotation);
}
}
};
}
private void takePhoto(Camera camera) {
if (camera != null) {
camera.takePicture(null, null, mPictureCallback);
}
}
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
final int screenRotation = mScreenRotation;
int rotate = toScreenOrientation(screenRotation);
// Rotate/Flip the bitmap depends on the 'rotate' value
}
};
/**
* Converts sensor rotation in degrees to screen orientation constants.
*
* #param rotation sensor rotation angle in degrees
* #return Screen orientation angle in degrees (0, 90, 180, 270).
*/
private int toScreenOrientation(int rotation) {
if (rotation > 290 || rotation <= 70) {
return SCREEN_ORIENTATION_0;
} else if (rotation > 70 && rotation <= 110) {
return SCREEN_ORIENTATION_90;
} else if (rotation > 110 && rotation <= 250) {
return SCREEN_ORIENTATION_180;
} else {
return SCREEN_ORIENTATION_270;
}
}
public void setOrientationChangedListener(OnOrientationChangedListener orientationChangedListener){
this.mOnOrientationChangedListener = orientationChangedListener;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Interfaces
////////////////////////////////////////////////////////////////////////////////////////////////
public interface OnOrientationChangedListener {
void onOrientationChanged(int rotation);
}
}
Can I use this listener even I disable rotation in my manifest?
-> It still works fine.
Hope it helps.
Looks like you do not want your Activity to be recreated on device rotation.
If so, then add configChanges attribute in AndroidManifest:
<activity
...
android:configChanges="orientation" >
This will stop activity recreation on rotation. But in your activity you can check that device has been rotated in onConfigurationChanged() method:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
...
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
...
}
}
Do not forget to read android developer guides. ;)
I have found a way to do this but I used alertDailog. The logic will be the same for all views.
AlertDialog dialog;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.stack_overflow2);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
AlertDialog.Builder alert123 = new AlertDialog.Builder(StackOverflow2.this);
View current_view = getLayoutInflater().inflate(R.layout.password_alert,null);
l2 = current_view.findViewById(R.id.linearView);
// Here l2 is linear layout getting root layout of password_alert
alert123.setView(current_view);
dialog = alert123.create();
dialog.show();
OrientationEventListener onrientationEventListener = new OrientationEventListener(getBaseContext(), SensorManager.SENSOR_DELAY_UI) {
#Override
public void onOrientationChanged(int rotation) {
Log.e("Orientation: " , String.valueOf(rotation));
if(rotation==270 || rotation==90)
{
if(rotation==270)
{
l2.setRotation(90);
}
else
{
l2.setRotation(270);
}
dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
Toast.makeText(StackOverflow2.this, "Change Dialog Rotation", Toast.LENGTH_SHORT).show();
}
else
{
if(rotation==180)
{
l2.setRotation(180);
}
else
{
l2.setRotation(0);
}
Toast.makeText(StackOverflow2.this, "Normal Display to Occur", Toast.LENGTH_SHORT).show();
}
}
};
if (onrientationEventListener.canDetectOrientation()) {
Log.v("ORIE", "Can detect orientation");
onrientationEventListener.enable();
} else {
Log.v("ORIE", "Cannot detect orientation");
onrientationEventListener.disable();
}
}
Here are some pictures :
0 Degree
90 Degree
270 Degree
As you can see the background activity is in potrait mode always. The alert dialog's height and width is a little off but you can change your view's dimensions differently. I hope this code solves your problem.
I want to find the camera screen orientation in locked portrait orientation mode, well I am using camera in my fragment class and I have already set my screen orientation as portrait, but the problem I am facing is, when I turn my camera from portrait to landscape its getting changed and I need to set capture button visible only when the camera is in portrait mode. Can anyone help me to get the orientation changes in portrait mode?
Below is my code:
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
sensorManager.registerListener(new SensorEventListener() {
int orientation=-1;;
#Override
public void onSensorChanged(SensorEvent event) {
if (event.values[1] < 6.5 && event.values[1] > -6.5) {
if (orientation!=1) {
Log.d("Sensor", "Landscape");
}
orientation = 1;
} else {
if (orientation!=0) {
Log.d("Sensor", "Portrait");
}
orientation = 0;
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
if (orientation == 0) {
// capture button visisble
} else {
// invisible
}
You can use OrientationEventListener for this. this is class that customise it.
public abstract class SimpleOrientationListener extends OrientationEventListener {
public static final int CONFIGURATION_ORIENTATION_UNDEFINED = Configuration.ORIENTATION_UNDEFINED;
private volatile int defaultScreenOrientation = CONFIGURATION_ORIENTATION_UNDEFINED;
public int prevOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
private Context ctx;
private ReentrantLock lock = new ReentrantLock(true);
public SimpleOrientationListener(Context context) {
super(context);
ctx = context;
}
public SimpleOrientationListener(Context context, int rate) {
super(context, rate);
ctx = context;
}
#Override
public void onOrientationChanged(final int orientation) {
int currentOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
if (orientation >= 330 || orientation < 30) {
currentOrientation = Surface.ROTATION_0;
} else if (orientation >= 60 && orientation < 120) {
currentOrientation = Surface.ROTATION_90;
} else if (orientation >= 150 && orientation < 210) {
currentOrientation = Surface.ROTATION_180;
} else if (orientation >= 240 && orientation < 300) {
currentOrientation = Surface.ROTATION_270;
}
if (prevOrientation != currentOrientation && orientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
prevOrientation = currentOrientation;
if (currentOrientation != OrientationEventListener.ORIENTATION_UNKNOWN)
reportOrientationChanged(currentOrientation);
}
}
private void reportOrientationChanged(final int currentOrientation) {
int defaultOrientation = getDeviceDefaultOrientation();
int orthogonalOrientation = defaultOrientation == Configuration.ORIENTATION_LANDSCAPE ? Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int toReportOrientation;
if (currentOrientation == Surface.ROTATION_0 || currentOrientation == Surface.ROTATION_180)
toReportOrientation = defaultOrientation;
else
toReportOrientation = orthogonalOrientation;
onSimpleOrientationChanged(toReportOrientation);
}
/**
* Must determine what is default device orientation (some tablets can have default landscape). Must be initialized when device orientation is defined.
*
* #return value of {#link Configuration#ORIENTATION_LANDSCAPE} or {#link Configuration#ORIENTATION_PORTRAIT}
*/
private int getDeviceDefaultOrientation() {
if (defaultScreenOrientation == CONFIGURATION_ORIENTATION_UNDEFINED) {
lock.lock();
defaultScreenOrientation = initDeviceDefaultOrientation(ctx);
lock.unlock();
}
return defaultScreenOrientation;
}
/**
* Provides device default orientation
*
* #return value of {#link Configuration#ORIENTATION_LANDSCAPE} or {#link Configuration#ORIENTATION_PORTRAIT}
*/
private int initDeviceDefaultOrientation(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Configuration config = context.getResources().getConfiguration();
int rotation = windowManager.getDefaultDisplay().getRotation();
boolean isLand = config.orientation == Configuration.ORIENTATION_LANDSCAPE;
boolean isDefaultAxis = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180;
int result = CONFIGURATION_ORIENTATION_UNDEFINED;
if ((isDefaultAxis && isLand) || (!isDefaultAxis && !isLand)) {
result = Configuration.ORIENTATION_LANDSCAPE;
} else {
result = Configuration.ORIENTATION_PORTRAIT;
}
return result;
}
/**
* Fires when orientation changes from landscape to portrait and vice versa.
*
* #param orientation value of {#link Configuration#ORIENTATION_LANDSCAPE} or {#link Configuration#ORIENTATION_PORTRAIT}
*/
public abstract void onSimpleOrientationChanged(int orientation);
}
Then where you want to detect orientation just call
SimpleOrientationListener mOrientationListener = new SimpleOrientationListener(
context) {
#Override
public void onSimpleOrientationChanged(int orientation) {
if(orientation == Configuration.ORIENTATION_LANDSCAPE){
}else if(orientation == Configuration.ORIENTATION_PORTRAIT){
}
}
};
mOrientationListener.enable();
You gonna have to use:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
//do your stuff with the button
}
}
If you want your activity not to be recreated during orientation then use
android:configChanges="orientation|keyboardHidden|screenSize"
If you want your activity to be forced to stay portrait then you gonna have to use
in your manifest file in the activity you would like just try android:screenOrientation="portrait"
Hope it helps!!!
You can achieve the orientation change value, when your activity set to only Portrait or Landscape specific and you want to perform some action when orientation changes.
private OrientationEventListener orientationEventListener;
initialize this variable in your class and implement it's listener in onCreate
orientationEventListener = new OrientationEventListener(this) {
#Override
public void onOrientationChanged(int orientation) {
Log.d("Orientation", orientation + " - " + currentOrientation);
if (orientation >= 330 || orientation < 30) {
currentOrientation = Surface.ROTATION_0;
} else if (orientation >= 60 && orientation < 120) {
currentOrientation = Surface.ROTATION_90;
} else if (orientation >= 150 && orientation < 210) {
currentOrientation = Surface.ROTATION_180;
} else if (orientation >= 240 && orientation < 300) {
currentOrientation = Surface.ROTATION_270;
}
}
};
currentOrentation is integer value for later use in the activity other units.
I have developed a three-pane layout where up to two panes are visible at a time. Only in landscape mode multiple views are supported. Each pane contains arrows to change the visibility state so the panes distribution change.
When the application opens in portrait mode, the views transition works correctly. The same happens when the application is opened in landscape mode. The problem arises when device is rotated and the control should change the distribution of the panels. when device is rotated the state transition fails and panes disappear or are not in the position where they should be.
Due to the requirements of the application the activity host handle the configuration changes itself (android:configChanges="orientation|screensize") and I can not disable this behavior. When onConfigurationChanged method is invoked in the host activity the control is notified to execute the state transition. Depending on the current visibility state and the new orientation the control determines the new state and calls the method responsible for executing the transition (configureWidth method in the control's code). Below the code of the host activity and the control:
MainActivity.java
public class MainActivity extends Activity implements
CategoriesListFragment.OnCategoriesListSizeControlListener,
TasksListFragment.OnTasksListSizeControlListener,
TaskDetailFragment.OnTaskDetailSizeControlListener, OnStateChangeListener {
// Multipanel control that will contain the fragments associated with the
// category list, task list and task detail.
private ThreePaneLayout mMultiPaneControl;
// Fragments
private CategoriesListFragment mCategoriesListFragment;
private TasksListFragment mTasksListFragment;
private TaskDetailFragment mTaskDetailFragment;
private int mScreenOrientation;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the initial orientation of the device
mScreenOrientation = getResources().getConfiguration().orientation;
mMultiPaneControl = (ThreePaneLayout) findViewById(R.id.multiPaneControl);
mCategoriesListFragment = CategoriesListFragment.newInstance();
mTasksListFragment = TasksListFragment.newInstance();
mTaskDetailFragment = TaskDetailFragment.newInstance();
// Add the state change observers of the ThreePaneLayout control.
// The client fragments use the events of the OnstateChangeListener interface to
// update the arrows orientation that user can use to redimension the panels
mMultiPaneControl.addStateObserver(mCategoriesListFragment);
mMultiPaneControl.addStateObserver(mTasksListFragment);
mMultiPaneControl.addStateObserver(mTaskDetailFragment);
mMultiPaneControl.addStateObserver(this);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.categoriesList, mCategoriesListFragment, "tag");
transaction.add(R.id.tasksList, mTasksListFragment);
transaction.add(R.id.taskDetail, mTaskDetailFragment);
transaction.commit();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mScreenOrientation = getResources().getConfiguration().orientation;
// When device orientationcchange notify the ThreePaneLyout control associated with the activity
// to update the distribution of its panels in order to improve usability
mMultiPaneControl.deviceOrientationHasChange();
}
#Override
public void onCategoriesListSizeControlSelected() {
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
VisibilityState visibilityState = mMultiPaneControl.getVisibityState();
if (visibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE ) {
mMultiPaneControl.setVisibilityState(VisibilityState.LEFT_VISIBLE);
} else if ( visibilityState == VisibilityState.LEFT_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.LEFT_AND_MIDDLE_VISIBLE);
}
} else { // Configuration.ORIENTATION_PORTRAIT
// The only possible state if this event is received while the device in
// portrait orientation is MIDDLE_VISIBLE
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_VISIBLE);
}
}
#Override
public void onTasksListSizeControlSelected(boolean leftControl) {
VisibilityState visibilityState = mMultiPaneControl.getVisibityState();
if (leftControl) {
if ( mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE ) {
if (visibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE ) {
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_VISIBLE);
} else if (visibilityState == VisibilityState.MIDDLE_VISIBLE ) {
mMultiPaneControl.setVisibilityState(VisibilityState.LEFT_AND_MIDDLE_VISIBLE);
} else if (visibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE ) {
mMultiPaneControl.setVisibilityState(VisibilityState.LEFT_AND_MIDDLE_VISIBLE);
}
} else { // Configuration.ORIENTATION_PORTRAIT
// The only possible state if this event is received while the device in
// portrait orientation is LEFT_VISIBLE
mMultiPaneControl.setVisibilityState(VisibilityState.LEFT_VISIBLE);
}
} else {
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if (visibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_AND_RIGHT_VISIBLE);
} else if (visibilityState == VisibilityState.MIDDLE_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_AND_RIGHT_VISIBLE);
} else if (visibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_VISIBLE);
}
} else { // Configuration.ORIENTATION_PORTRAIT
// The only possible state if this event is received while the device in
// portrait orientation is RIGHT_VISIBLE
mMultiPaneControl.setVisibilityState(VisibilityState.RIGHT_VISIBLE);
}
}
}
#Override
public void onDetailTaskSizeControlSelected() {
VisibilityState visibilityState = mMultiPaneControl.getVisibityState();
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if ( visibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.RIGHT_VISIBLE);
} else if ( visibilityState == VisibilityState.RIGHT_VISIBLE) {
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_AND_RIGHT_VISIBLE);
}
} else { // Configuration.ORIENTATION_PORTRAIT
// The only possible state if this event is received while the device in
// portrait orientation is MIDDLE_VISIBLE
mMultiPaneControl.setVisibilityState(VisibilityState.MIDDLE_VISIBLE);
}
}
#Override
public void onBeginTransitionState(VisibilityState oldState,
VisibilityState newState) {
}
#Override
public void onNewStateVisible(VisibilityState newState) { /**/ }
}
ThreePaneLayout.java
/**
* <p>Control to display up to three panels with a maximum of two simultaneously visible.</p>
*/
public class ThreePaneLayout extends LinearLayout {
/**
* Time control takes for the state change animation
*/
public static final int ANIMATION_DURATION = 300;
private boolean isScrollingViews;
/**
* Possible control visibility states. The states are exclusive, ie, if the control is
* in state {# link LEFT_AND_MIDDLE_VISIBLE} implies that the right pane is not visible.
*/
public enum VisibilityState {
LEFT_VISIBLE,
LEFT_AND_MIDDLE_VISIBLE,
MIDDLE_VISIBLE,
MIDDLE_AND_RIGHT_VISIBLE,
RIGHT_VISIBLE
}
/**
* Interface to be implemented by clients that require to be notified
* when the visibility state change.
*/
public interface OnStateChangeListener {
/**
* Method invoked by the control just prior to the transition state of visibility
*
* #param oldState Estado actual de visibilidad control
* #param newState Proximo estado de visibilidad del control
*/
void onBeginTransitionState(VisibilityState oldState, VisibilityState newState);
/**
* Method invoked by the control when its visibility status has been updated
*
* #param newState New visibility state of the control panels
*/
void onNewStateVisible(VisibilityState newState);
}
// Reference to the three panels of the control
private View mLeftView;
private View mMiddleView;
private View mRightView;
// Variables that store the minimum and maximum widths that can have different panels
private int mMinPaneWidth = -1;
private int mMaxPaneWidth;
private int mFullScreenWidth;
// Stores the current device orientation
private int mScreenOrientation;
// Reference to the current state of the panels
private VisibilityState mVisibilityState;
/**
* Lista de observadores de cambio de estado del control
*/
private List<OnStateChangeListener> mStateListeners;
private Handler mHandler = new Handler();
private Context mContext;
public ThreePaneLayout(Context context, AttributeSet attrs) {
super(context, attrs);
Log.i("Log", "Llamado el constructor del control");
mContext = context;
mScreenOrientation = getResources().getConfiguration().orientation;
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
setOrientation(HORIZONTAL);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ThreePaneLayout);
String initialState = a.getString(R.styleable.ThreePaneLayout_initialState);
if ( initialState != null ) {
// The initial state has been defined in the XML
if (initialState.equals("left_visible")) {
mVisibilityState = VisibilityState.LEFT_VISIBLE;
} else if (initialState.equals("left_and_middle_visible")) {
mVisibilityState = VisibilityState.LEFT_AND_MIDDLE_VISIBLE;
}
} else {
// The initial state is not defined in the XML, set the default state
mVisibilityState = VisibilityState.LEFT_AND_MIDDLE_VISIBLE;
}
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
if ( getChildCount() != 3 ) {
throw new IllegalStateException("ThreePaneLayout requires defining three daughters views in the XML");
}
// Get a reference to the views that make control
mLeftView = getChildAt(0);
mMiddleView = getChildAt(1);
mRightView = getChildAt(2);
configureWidth();
}
/**
* Set the weights of each of the views of the layout container.
*
* If the method takes no arguments recalculates the weights based on the current visibility
* state of the control. If this method receive a parameter is used as visibility state from
* which will be held on recalculation
*
* #param args
*/
private void configureWidth(VisibilityState ... args) {
LayoutParams leftPaneLayoutParams = (LayoutParams) mLeftView.getLayoutParams();
LayoutParams middlePaneLayoutParams = (LayoutParams) mMiddleView.getLayoutParams();
LayoutParams rightPaneLayoutParams = (LayoutParams) mRightView.getLayoutParams();
VisibilityState visibilityState = args.length == 0 ? mVisibilityState : args[0];
if (args.length > 0) {
leftPaneLayoutParams.width = 0;
middlePaneLayoutParams.width = 0;
rightPaneLayoutParams.width = 0;
}
if ( visibilityState == VisibilityState.LEFT_VISIBLE ) {
leftPaneLayoutParams.weight = 1.0f;
middlePaneLayoutParams.weight = 0.0f;
rightPaneLayoutParams.weight = 0.0f;
} else if ( visibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE ) {
leftPaneLayoutParams.weight = 0.35f;
middlePaneLayoutParams.weight = 0.65f;
rightPaneLayoutParams.weight = 0.0f;
} else if ( visibilityState == VisibilityState.MIDDLE_VISIBLE ) {
leftPaneLayoutParams.weight = 0.0f;
middlePaneLayoutParams.weight = 1.0f;
rightPaneLayoutParams.weight = 0.0f;
} else if ( visibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE ) {
leftPaneLayoutParams.weight = 0.0f;
middlePaneLayoutParams.weight = 0.35f;
rightPaneLayoutParams.weight = 0.65f;
} else if ( visibilityState == VisibilityState.RIGHT_VISIBLE ) {
leftPaneLayoutParams.weight = 0.0f;
middlePaneLayoutParams.weight = 0.0f;
rightPaneLayoutParams.weight = 1.0f;
}
// Refresh the view and compute the size of the view in the screen.
requestLayout();
}
/**
* Method that performs the visibility state transition control (redistribution of the panels)
*
* #param newVisibilityState New visibility state required.
* #param resetDimensions This parameter is optional and should be used only when you are performing a
* state transcion due to a change of device orientation so we can recalculated
* weights and widths of the panels.
*/
public void setVisibilityState(VisibilityState newVisibilityState, boolean ... resetDimensions) {
// Ignore the request if the control is being resized or if the requested state is equal to the current
if ( isScrollingViews || newVisibilityState == mVisibilityState ) {
return;
}
// If requested any state that contains more than one panel and device orientation is portrait,
// ignore the request (this control only supports multiple panels visible in landscape)
if (mScreenOrientation == Configuration.ORIENTATION_PORTRAIT) {
if ( newVisibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE ||
newVisibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE ) {
return;
}
}
if (resetDimensions.length > 0 && resetDimensions[0] == true) {
configureWidth(newVisibilityState);
mMinPaneWidth = -1;
}
// Calculate the maximum and minimum widths of the control panel if the have not been defined
if (mMinPaneWidth == -1) {
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(displayMetrics);
int screenWidth = displayMetrics.widthPixels;
if ( mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE ) {
mMinPaneWidth = (int) (screenWidth * 0.35);
mMaxPaneWidth = screenWidth - mMinPaneWidth;
mFullScreenWidth = screenWidth;
} else { // Configuration.ORIENTATION_PORTRAIT
mMinPaneWidth = mMaxPaneWidth = mFullScreenWidth = screenWidth;
}
resetWidget(mLeftView, mMinPaneWidth);
resetWidget(mMiddleView, mMaxPaneWidth);
resetWidget(mRightView, mMaxPaneWidth);
requestLayout();
}
isScrollingViews = true;
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
isScrollingViews = false;
}
}, ANIMATION_DURATION + 100);
VisibilityState currentVisibilityState = mVisibilityState;
// Notify control observers will produce a state transition
if ( mStateListeners != null ) {
for ( OnStateChangeListener observer : mStateListeners ) {
observer.onBeginTransitionState(currentVisibilityState, newVisibilityState);
}
}
if (resetDimensions.length == 0) {
// Perform the movement of the panels to match the new state required
animateVisibilityStateTransition(currentVisibilityState, newVisibilityState);
}
// Update the reference to the current state
mVisibilityState = newVisibilityState;
// Notify the new control state to the control's observers
if ( mStateListeners != null ) {
for ( OnStateChangeListener observer : mStateListeners ) {
observer.onNewStateVisible(mVisibilityState);
}
}
}
/**
* Moves on the x axis and resize the panels to suit new visibility state required.
*
* #param currentVisibilityState Current state control visibility
* #param requiredVisibilityState New visibility state required
*/
private void animateVisibilityStateTransition(VisibilityState currentVisibilityState,
VisibilityState requiredVisibilityState) {
switch (requiredVisibilityState) {
case LEFT_VISIBLE:
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if (currentVisibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE) {
ObjectAnimator.ofInt(this, "leftWidth", mMinPaneWidth, mFullScreenWidth)
.setDuration(ANIMATION_DURATION).start();
}
} else { // Configuration.ORIENTATION_PORTRAIT
if (currentVisibilityState == VisibilityState.MIDDLE_VISIBLE) {
translateView(mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
} else if (currentVisibilityState == VisibilityState.RIGHT_VISIBLE) {
translateView(2 * mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
}
}
break;
case LEFT_AND_MIDDLE_VISIBLE:
// Este estado solo es posible en orientacion panoramica
if (currentVisibilityState == VisibilityState.MIDDLE_VISIBLE) {
translateView(mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "middleWidth", mFullScreenWidth, mMaxPaneWidth)
.setDuration(ANIMATION_DURATION).start();
} else if (currentVisibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
translateView(mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "middleWidth", mMinPaneWidth, mMaxPaneWidth)
.setDuration(ANIMATION_DURATION).start();
} else if (currentVisibilityState == VisibilityState.LEFT_VISIBLE) {
ObjectAnimator.ofInt(this, "leftWidth", mFullScreenWidth, mMinPaneWidth)
.setDuration(ANIMATION_DURATION).start();
}
break;
case MIDDLE_VISIBLE:
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if (currentVisibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE) {
translateView(-1 * mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "middleWidth", mMaxPaneWidth, mFullScreenWidth)
.setDuration(ANIMATION_DURATION).start();
} else if (currentVisibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
ObjectAnimator.ofInt(this, "middleWidth", mMinPaneWidth, mFullScreenWidth)
.setDuration(ANIMATION_DURATION).start();
}
} else { // Configuration.ORIENTATION_PORTRAIT
if (currentVisibilityState == VisibilityState.LEFT_VISIBLE) {
translateView(-1 * mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
} else if (currentVisibilityState == VisibilityState.RIGHT_VISIBLE) {
translateView(mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
}
}
break;
case MIDDLE_AND_RIGHT_VISIBLE:
// Este estado solo es posible en orientacion panoramica
if (currentVisibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE) {
translateView(-1 * mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "middleWidth", mMaxPaneWidth, mMinPaneWidth)
.setDuration(ANIMATION_DURATION).start();
} else if (currentVisibilityState == VisibilityState.MIDDLE_VISIBLE) {
ObjectAnimator.ofInt(this, "middleWidth", mFullScreenWidth, mMinPaneWidth)
.setDuration(ANIMATION_DURATION).start();
} else if (currentVisibilityState == VisibilityState.RIGHT_VISIBLE) {
translateView(mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "rightWidth", mFullScreenWidth, mMaxPaneWidth)
.setDuration(ANIMATION_DURATION).start();
}
break;
case RIGHT_VISIBLE:
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if (currentVisibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
translateView(-1 * mMinPaneWidth, mLeftView, mMiddleView, mRightView);
ObjectAnimator.ofInt(this, "rightWidth", mMaxPaneWidth, mFullScreenWidth)
.setDuration(ANIMATION_DURATION).start();
}
} else { // Configuration.ORIENTATION_PORTRAIT
if (currentVisibilityState == VisibilityState.LEFT_VISIBLE) {
translateView(-2 * mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
} else if (currentVisibilityState == VisibilityState.MIDDLE_VISIBLE) {
translateView(-1 * mMaxPaneWidth, mLeftView, mMiddleView, mRightView);
}
}
break;
}
}
public VisibilityState getVisibityState() {
return mVisibilityState;
}
#SuppressWarnings("unused")
private void setLeftWidth(int value) {
mLeftView.getLayoutParams().width = value;
requestLayout();
}
#SuppressWarnings("unused")
private void setMiddleWidth(int value) {
mMiddleView.getLayoutParams().width = value;
requestLayout();
}
#SuppressWarnings("unused")
private void setRightWidth(int value) {
mRightView.getLayoutParams().width = value;
requestLayout();
}
/**
* Moves in the X axis the views received as parameter.
*
* #param deltaX Number of pixels that are shifted in the x-axis views
* #param views Views on which it will move
*/
private void translateView(int deltaX, View... views) {
for (final View view : views) {
view.setLayerType(View.LAYER_TYPE_NONE, null);
view.animate().translationXBy(deltaX).setDuration(ANIMATION_DURATION)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
}
}
/**
* Updates the properties of length and weight on the layout of the view passed as parameter
*
* #param view Vista on which perform the update of the properties
* #param width New width
*/
private void resetWidget(View view, int width) {
LinearLayout.LayoutParams p = (LinearLayout.LayoutParams) view.getLayoutParams();
p.width = width;
p.weight = 0;
}
/**
* Adds the component passed as a parameter to the list of control observers. The observers are notified
* every time the control change the panels distribution. Observers also receive a notification just before
* starting the transition state by the control.
*
* #param observer component that implements the {# link OnStateChangeListener} interface
* to recieve notifications when the visibility control state change
*/
public void addStateObserver(OnStateChangeListener observer) {
if ( mStateListeners == null ) {
mStateListeners = new ArrayList<OnStateChangeListener>();
}
mStateListeners.add(observer);
}
/**
* Removes the component passed as parameter from the list of observers of
* state change. Observers are added to the list through the method {# link # addStateObserver}
*
* #param observer component that implements the {# link OnStateChangeListener} interface
* to recieve notifications when the visibility control state change
*/
public void deleteStatetObserver(OnStateChangeListener observer) {
if ( mStateListeners == null ) return;
mStateListeners.remove(observer);
}
/**
* Method invoked by the host activity when the orientation of the device has changed. Based on the
* new orientation the control redistributes panels to match the new orientation and improve usability
*/
public void deviceOrientationHasChange()
{
int newScreenOrientation = getResources().getConfiguration().orientation;
VisibilityState currentVisibilityState = getVisibityState();
VisibilityState newVisibilityState = null;
if (newScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
// When orientation chamge to landscape the only possible states in the previous orientation
// (portrait) can only be those where not coexist multiple views (eg LEFT_VISIBLE,
// MIDDLE_VISIBLE, RIGHT_VISIBLE)
if (currentVisibilityState == VisibilityState.LEFT_VISIBLE) {
newVisibilityState = VisibilityState.LEFT_AND_MIDDLE_VISIBLE;
} else if (currentVisibilityState == VisibilityState.MIDDLE_VISIBLE) {
newVisibilityState = VisibilityState.MIDDLE_VISIBLE;
} else if (currentVisibilityState == VisibilityState.RIGHT_VISIBLE) {
newVisibilityState = VisibilityState.RIGHT_VISIBLE;
}
} else { // Configuration.ORIENTATION_PORTRAIT
if (currentVisibilityState == VisibilityState.LEFT_VISIBLE) {
newVisibilityState = VisibilityState.LEFT_VISIBLE;
} else if ( currentVisibilityState == VisibilityState.LEFT_AND_MIDDLE_VISIBLE ||
currentVisibilityState == VisibilityState.MIDDLE_VISIBLE ) {
newVisibilityState = VisibilityState.MIDDLE_VISIBLE;
} else if (currentVisibilityState == VisibilityState.MIDDLE_AND_RIGHT_VISIBLE) {
newVisibilityState = VisibilityState.RIGHT_VISIBLE;
} else if (currentVisibilityState == VisibilityState.RIGHT_VISIBLE) {
newVisibilityState = VisibilityState.RIGHT_VISIBLE;
}
}
mScreenOrientation = newScreenOrientation;
mMinPaneWidth = -1;
configureWidth(newVisibilityState);
mVisibilityState = newVisibilityState;
}
public View getLeftView() {
return mLeftView;
}
public View getMiddleView() {
return mMiddleView;
}
public View getRightView() {
return mRightView;
}
}
For example when the device is in landscape and the visibility state is MIDDLE AND RIGHT (see Figure 1) when you rotate the device to portrait the state change fails (see Figure 2).
Figure 1
Figure 2
For a more detailed analysis of the application, I posted on github a sample application that uses the control and is available for download at the following link: https://github.com/dfpalomar/ThreePaneLayout
I tried to force the redraw of the views using the methods requestLayout, invalidate and forceLayout from controls's configureWidth method but without the desired results.
Any suggestions on how I could solve the problem is welcome :D
There is no automatic functionality to keep session when changing screen rotation, you need to do this manually yourself since the activity is being reloaded. You can read the details here:
http://developer.android.com/guide/topics/manifest/activity-element.html#config
There are also many similar questions here on StackOverflow, so please also try searching for this to get a suitable answer.
I have an Android application using LinearLayout as main layout with a SurfaceView filled by camera preview.
In this I inflate another LinearLayout with three Buttons and a custom TextView. I would like the camera preview to stay always in Landscape orientation and the overlay layout changing according to the device orientation.
I tried setting android:screenOrientation="landscape" in the manifest for the activity but then (of course) also the inflated layout stays always fixed, while not setting the android:screenOrientation property also the camera preview rotate, slowing down the app and showing strange form factors of the preview. Here the relevant code for the layout:
private void setupLayout()
{
setContentView(R.layout.main);
getWindow().setFormat(PixelFormat.UNKNOWN);
// Release camera if owned by someone else
if (camera != null)
releaseCamera();
surfaceView = (SurfaceView) findViewById(R.id.camerapreview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
controlInflater = LayoutInflater.from(getBaseContext());
View viewControl = controlInflater.inflate(R.layout.control, null);
LayoutParams layoutParamsControl = new LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
this.addContentView(viewControl, layoutParamsControl);
buttonGetCollectingData = (Button) findViewById(R.id.getcolldata);
buttonGetCollectingData.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View arg0)
{
...
}
});
btnBackHome = (Button) findViewById(R.id.btnBackHome);
btnBackHome.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View arg0)
{
...
}
});
autoFitTextViewMainMsg = (AutoFitTextView) findViewById(R.id.autoFitTextViewMainMsg);
buttonTakePicture.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View arg0)
{
...
}
});
}
Any idea on how to accomplish this would be really appreciated!
You can make use of custom orientation event listener to get the orientation and set your UI as according.
First, create a class CustomOrientationEventListener
public abstract class CustomOrientationEventListener extends OrientationEventListener {
private static final String TAG = "CustomOrientationEvent";
private int prevOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
private Context context;
private final int ROTATION_O = 1;
private final int ROTATION_90 = 2;
private final int ROTATION_180 = 3;
private final int ROTATION_270 = 4;
private int rotation = 0;
public CustomOrientationEventListener(Context context) {
super(context);
this.context = context;
}
#Override
public void onOrientationChanged(int orientation) {
if (android.provider.Settings.System.getInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 0) // 0 = Auto Rotate Disabled
return;
int currentOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
if (orientation >= 340 || orientation < 20 && rotation != ROTATION_O) {
currentOrientation = Surface.ROTATION_0;
rotation = ROTATION_O;
} else if (orientation >= 70 && orientation < 110 && rotation != ROTATION_90) {
currentOrientation = Surface.ROTATION_90;
rotation = ROTATION_90;
} else if (orientation >= 160 && orientation < 200 && rotation != ROTATION_180) {
currentOrientation = Surface.ROTATION_180;
rotation = ROTATION_180;
} else if (orientation >= 250 && orientation < 290 && rotation != ROTATION_270) {
currentOrientation = Surface.ROTATION_270;
rotation = ROTATION_270;
}
if (prevOrientation != currentOrientation && orientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
prevOrientation = currentOrientation;
if (currentOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
onSimpleOrientationChanged(rotation);
}
}
}
public abstract void onSimpleOrientationChanged(int orientation);
}
Then add below line to the Android manifest under your activity tag
android:configChanges="orientation|keyboardHidden|screenSize"
Make sure that you do not add android:screenOrientation property in manifest for that activity.
Then use the CustomOrientationEventListener class inside onCreate of your camera activity
public class YourActivity extends AppCompatActivity {
private CustomOrientationEventListener customOrientationEventListener;
final int ROTATION_O = 1;
final int ROTATION_90 = 2;
final int ROTATION_180 = 3;
final int ROTATION_270 = 4;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
.....
customOrientationEventListener = new
CustomOrientationEventListener(getBaseContext()) {
#Override
public void onSimpleOrientationChanged(int orientation) {
switch(orientation){
case ROTATION_O:
//rotate as on portrait
yourButton.animate().rotation(0).setDuration(500).start();
break;
case ROTATION_90:
//rotate as left on top
yourButton.animate().rotation(-90).setDuration(500).start();
break;
case ROTATION_270:
//rotate as right on top
yourButton.animate().rotation(90).setDuration(500).start();
break;
case ROTATION_180:
//rotate as upside down
yourButton.animate().rotation(180).setDuration(500).start();
break;
}
}
};
}
#Override
protected void onResume() {
super.onResume();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
customOrientationEventListener.enable();
}
#Override
protected void onPause() {
super.onPause();
customOrientationEventListener.disable();
}
#Override
protected void onDestroy() {
super.onDestroy();
customOrientationEventListener.disable();
}
}
I have set am activity to handle configuration changes and it works, meaning that onConfigurationChanged() is called when the orientation changes.
The activity has a button to explicitly change the orientation. When clicked, it called setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT).
Then the orientation is irrevocably set and onConfigurationChanged() is not called anymore.
How can I change the orientation when the user clicks the button while not loosing the onConfigurationChanged() callback ?
This is how I solved it. I am aware it is reinventing the wheel but it meets my requirement and I did not find a proper way to handle this with standard sdk tools.
First, create an OrientationManager class that listen to orientation changes
public class OrientationManager extends OrientationEventListener{
private static final String TAG = OrientationManager.class.getName();
private int previousAngle;
private int previousOrientation;
private Context context;
private OrientationChangeListener orientationChangeListener;
private static OrientationManager instance;
private OrientationManager(Context context) {
super(context);
this.context = context;
}
public static OrientationManager getInstance(Context context){
if (instance == null){
instance = new OrientationManager(context);
}
return instance;
}
public int getOrientation(){
return previousOrientation;
}
public void setOrientation(int orientation){
this.previousOrientation = orientation;
}
#Override
public void onOrientationChanged(int orientation) {
if (orientation == -1)
return;
if(previousOrientation == 0){
previousOrientation = context.getResources().getConfiguration().orientation;
if (orientationChangeListener != null){
orientationChangeListener.onOrientationChanged(previousOrientation);
}
}
if (previousOrientation == Configuration.ORIENTATION_LANDSCAPE &&
((previousAngle > 10 && orientation <= 10) ||
(previousAngle < 350 && previousAngle > 270 && orientation >= 350)) ){
if (orientationChangeListener != null){
orientationChangeListener.onOrientationChanged(Configuration.ORIENTATION_PORTRAIT);
}
previousOrientation = Configuration.ORIENTATION_PORTRAIT;
}
if (previousOrientation == Configuration.ORIENTATION_PORTRAIT &&
((previousAngle <90 && orientation >= 90 && orientation <270) ||
(previousAngle > 280 && orientation <= 280 && orientation > 180)) ){
if (orientationChangeListener != null){
orientationChangeListener.onOrientationChanged(Configuration.ORIENTATION_LANDSCAPE);
}
previousOrientation = Configuration.ORIENTATION_LANDSCAPE;
}
previousAngle = orientation;
}
public void setOrientationChangedListener(OrientationChangeListener l){
this.orientationChangeListener = l;
}
public interface OrientationChangeListener{
public void onOrientationChanged(int newOrientation);
}
}
Then in your activity implement OrientationChangeListener and override onOrientationChanged():
public class MyActivity extends Activity implements OrientationChangeListener{
private OrientationManager orientationManager;
#Override
public void onCreate(Bundle b){
orientationManager = OrientationManager.getInstance(this);
orientationManager.setOrientationChangedListener(this);
}
#Override
public void onOrientationChanged(int newOrientation) {
orientation = newOrientation;
if (newOrientation == Configuration.ORIENTATION_LANDSCAPE){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
setLandscapeConfig();
}else{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setPortraitConfig();
}
}
So I don't use onConfigurationChanged anymore but keep the following line in the Manifest:
android:configChanges="orientation|screenSize"
Calling setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); will reactivate onConfigurationChanged. You can set up a timer like this:
// Force orientation to portrait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
// Reactivate sensor orientation after delay
Timer mRestoreOrientation = new Timer();
mRestoreOrientation.schedule(new TimerTask() {
#Override
public void run() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}, 2000);
We can only suppose the user will turn the device by himself to the forced orientation within the delay, and this can lead to bad user experience.
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)
This makes your onConfigurationChanged to stop working. If you want it to work, try adding this to your manifest within that activity:
android:configChanges="orientation|screenSize"
The screenSize attribute is only for API 13+ so if your below that, you don't need it
..after many search this the right code mix that solve. enjoy!
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
OrientationEventListener orientationEventListener = new
OrientationEventListener(getApplicationContext()) {
#Override
public void onOrientationChanged(int orientation) {
boolean isPortrait = isPortrait(orientation);
if (!isPortrait && savedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
savedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
} else if (isPortrait && savedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
savedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
}
}
};
orientationEventListener.enable();
}
private int savedOrientation;
private boolean isPortrait(int orientation)
{
if (orientation < 45 || orientation > 315) {
return true;
}
return false;
}
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE)
{
currentOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
// do what you need in landscape mode....
}
else if(newConfig.orientation==Configuration.ORIENTATION_PORTRAIT)
{
currentOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
// do what you need in portrait mode....
}
}
public void rotateScreenByUIButton() {
if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
else
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
You just need to make the orientation back to SCREEN_ORIENTATION_UNSPECIFIED, the the onConfiguration change will be called again
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
->
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)