可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This could easily be a duplicate of another question, Im just struggling to figure out what to search for.
My camera app is locked in landscape mode (in the manifest) like this:
android:screenOrientation="landscape"
However, I want to still rotate some UI elements when the device is rotated into portrait (although android will still think its in landscape, but thats on purpose).
So I've tried this to check the orientation
int rotation = this.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
Log.d("Rotation", "0");
break;
case Surface.ROTATION_90:
Log.d("Rotation", "90");
break;
case Surface.ROTATION_180:
Log.d("Rotation", "180");
break;
case Surface.ROTATION_270:
Log.d("Rotation", "270");
break;
}
And unfortunately it always returns 90, regardless of how I turn the phone. Is there a more robust way to get orientation, regardless of what Android "thinks" the orientation is?
回答1:
So after I thought about it, I realized I could just implement a similar algorithm as what Android itself uses to figure out the orientation. I do it using the onSenseorChanged callback
public static final int UPSIDE_DOWN = 3;
public static final int LANDSCAPE_RIGHT = 4;
public static final int PORTRAIT = 1;
public static final int LANDSCAPE_LEFT = 2;
public int mOrientationDeg; //last rotation in degrees
public int mOrientationRounded; //last orientation int from above
private static final int _DATA_X = 0;
private static final int _DATA_Y = 1;
private static final int _DATA_Z = 2;
private int ORIENTATION_UNKNOWN = -1;
@Override
public void onSensorChanged(SensorEvent event)
{
Log.d(TAG, "Sensor Changed");
float[] values = event.values;
int orientation = ORIENTATION_UNKNOWN;
float X = -values[_DATA_X];
float Y = -values[_DATA_Y];
float Z = -values[_DATA_Z];
float magnitude = X*X + Y*Y;
// Don't trust the angle if the magnitude is small compared to the y value
if (magnitude * 4 >= Z*Z) {
float OneEightyOverPi = 57.29577957855f;
float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
orientation = 90 - (int)Math.round(angle);
// normalize to 0 - 359 range
while (orientation >= 360) {
orientation -= 360;
}
while (orientation < 0) {
orientation += 360;
}
}
//^^ thanks to google for that code
//now we must figure out which orientation based on the degrees
Log.d("Oreination", ""+orientation);
if (orientation != mOrientationDeg)
{
mOrientationDeg = orientation;
//figure out actual orientation
if(orientation == -1){//basically flat
}
else if(orientation <= 45 || orientation > 315){//round to 0
tempOrientRounded = 1;//portrait
}
else if(orientation > 45 && orientation <= 135){//round to 90
tempOrientRounded = 2; //lsleft
}
else if(orientation > 135 && orientation <= 225){//round to 180
tempOrientRounded = 3; //upside down
}
else if(orientation > 225 && orientation <= 315){//round to 270
tempOrientRounded = 4;//lsright
}
}
if(mOrientationRounded != tempOrientRounded){
//Orientation changed, handle the change here
mOrientationRounded = tempOrientRounded;
}
}
It looks more complecated than it is, but just know that it works(I'd say equally well as the system one works). Dont forget to register your sensor change event listener in onResume and onPause for accelerometer
回答2:
For detect orientations I use this for register to sensormanager:
mSensorOrientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(this, mSensorOrientation, SensorManager.SENSOR_DELAY_NORMAL);
And then this for detect orientations changes, in the comments you can put your own method implementations.
Constants:
public static final int LYING = 0;
public static final int LANDSCAPE_RIGHT = 1;
public static final int PORTRAIT = 2;
public static final int LANDSCAPE_LEFT = 3;
public void onSensorChanged(SensorEvent event) {
Sensor sensorEvent = event.sensor;
if ((sensorEvent.getType() == Sensor.TYPE_ORIENTATION)) {
float [] eventValues = event.values;
// current orientation of the phone
float xAxis = eventValues[1];
float yAxis = eventValues[2];
if ((yAxis <= 25) && (yAxis >= -25) && (xAxis >= -160)) {
if (previousOrientation != PORTRAIT){
previousOrientation = PORTRAIT;
// CHANGED TO PORTRAIT
}
} else if ((yAxis < -25) && (xAxis >= -20)) {
if (previousOrientation != LANDSCAPE_RIGHT){
previousOrientation = LANDSCAPE_RIGHT;
// CHANGED TO LANDSCAPE RIGHT
}
} else if ((yAxis > 25) && (xAxis >= -20)){
if (previousOrientation != LANDSCAPE_LEFT){
previousOrientation = LANDSCAPE_LEFT;
// CHANGED TO LANDSCAPE LEFT
}
}
}
}
回答3:
After doing some research and trying some stuff, it work just for me when I set the Sensor as:
mSensorOrientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor.TYPE_ORIENTATION is deprecated and gave me bad results according to some example codes from different people for retrieving orientation. I don´t really know if it's ok or not, but it worked for me.
回答4:
A translation of @panavtec answer to API 23, using this as a reference
class MyActivity extends Activity implements SensorEventListener {
private SensorManager sensorManager;
private float[] lastMagFields = new float[3];;
private float[] lastAccels = new float[3];;
private float[] rotationMatrix = new float[16];
private float[] orientation = new float[4];
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
}
protected void onResume() {
super.onResume();
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
System.arraycopy(event.values, 0, lastAccels, 0, 3);
break;
case Sensor.TYPE_MAGNETIC_FIELD:
System.arraycopy(event.values, 0, lastMagFields, 0, 3);
break;
default:
return;
}
if (SensorManager.getRotationMatrix(rotationMatrix, null, lastAccels, lastMagFields)) {
SensorManager.getOrientation(rotationMatrix, orientation);
float xAxis = (float) Math.toDegrees(orientation[1]);
float yAxis = (float) Math.toDegrees(orientation[2]);
int orientation = Configuration.ORIENTATION_UNDEFINED;
if ((yAxis <= 25) && (yAxis >= -25) && (xAxis >= -160)) {
Log.d(TAG, "Portrait");
orientation = Configuration.ORIENTATION_PORTRAIT;
} else if ((yAxis < -25) && (xAxis >= -20)) {
Log.d(TAG, "Landscape Right");
orientation = Configuration.ORIENTATION_LANDSCAPE;
} else if ((yAxis > 25) && (xAxis >= -20)){
orientation = Configuration.ORIENTATION_LANDSCAPE;
Log.d(TAG, "Landscape Left");
}
}
}
}
回答5:
This is a slightly modified Kotlin version of @Jameo answer. I needed the degrees to calculate the camera rotation in an Activity with the locked orientation. Please upvote him also.
private var rotationDeg: Int = 0
private var rotationRoundedClockwise: Int = 0
override fun onSensorChanged(event: SensorEvent) {
Timber.d("Sensor Changed")
val newRotationDeg = calculateNewRotationDegree(event)
//^^ thanks to google for that code
// now we must figure out which orientation based on the degrees
Timber.d("rotation: $newRotationDeg")
if (newRotationDeg != rotationDeg) {
rotationDeg = newRotationDeg
rotationRoundedClockwise = calculateRoundedRotation(newRotationDeg)
}
Timber.d("rotationRoundedClockwise: $rotationRoundedClockwise")
}
private val X_AXIS_INDEX = 0
private val Y_AXIS_INDEX = 1
private val Z_AXIS_AXIS = 2
private val ORIENTATION_UNKNOWN = -1
private fun calculateRoundedRotation(newRotationDeg: Int): Int {
return if (newRotationDeg <= 45 || newRotationDeg > 315) { // round to 0
0 // portrait
} else if (newRotationDeg in 46..135) { // round to 90
90 // clockwise landscape
} else if (newRotationDeg in 136..225) { // round to 180
180 // upside down portrait
} else if (newRotationDeg in 226..315) { // round to 270
270 // anticlockwise landscape
} else {
0
}
}
private fun calculateNewRotationDegree(event: SensorEvent): Int {
val values = event.values
var newRotationDeg = ORIENTATION_UNKNOWN
val X = -values[X_AXIS_INDEX]
val Y = -values[Y_AXIS_INDEX]
val Z = -values[Z_AXIS_AXIS]
val magnitude = X * X + Y * Y
// Don't trust the angle if the magnitude is small compared to the y value
if (magnitude * 4 >= Z * Z) {
val ONE_EIGHTY_OVER_PI = 57.29577957855f
val angle = Math.atan2((-Y).toDouble(), X.toDouble()).toFloat() * ONE_EIGHTY_OVER_PI
newRotationDeg = 90 - Math.round(angle)
// normalize to 0 - 359 range
while (newRotationDeg >= 360) {
newRotationDeg -= 360
}
while (newRotationDeg < 0) {
newRotationDeg += 360
}
}
return newRotationDeg
}
private fun getCameraRotation(): Int {
return when (rotationRoundedClockwise) {
0 -> 90
90 -> 180
180 -> 270
270 -> 0
else -> 90
}
}
Here is how to listen for events.
override fun onCreate() {
super.onCreate()
(activity?.getSystemService(SENSOR_SERVICE) as? SensorManager)?.let {
sensorManager = it
}
}
override fun onResume() {
super.onResume()
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}