http://www.iteye.com/wiki/blog/812749
在Android的Settings->Sound andDisplay中有Orientation这一设置项。当选中时,反转手机,手机屏幕会随之旋转,一般只可以旋转90度。
这一settings设置是在文件SoundAndDisplaySettings.java中,该项对应的键字符串为:
view plaincopy to clipboardprint?
private static final String KEY_ACCELEROMETER = "accelerometer";
Java代码
private static final String KEY_ACCELEROMETER = "accelerometer";
其默认值保存在xml文件中,默认是Enable。UI程序初始化时会根据其值是否在复选框中打勾(代码在onCreate函数中):
view plaincopy to clipboardprint?
protected void onCreate(Bundle savedInstanceState) {
…
mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
mAccelerometer.setPersistent(false);
…
}
Java代码
protected void onCreate(Bundle savedInstanceState) {
…
mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
mAccelerometer.setPersistent(false);
…
}
当用户改变了该值时,会保存起来:
view plaincopy to clipboardprint?
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
…
} else if (preference == mAccelerometer) {
Settings.System.putInt(getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION,
mAccelerometer.isChecked() ? 1 : 0);
…
}
Java代码
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
…
} else if (preference == mAccelerometer) {
Settings.System.putInt(getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION,
mAccelerometer.isChecked() ? 1 : 0);
…
}
文件frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中的SettingsServer会随时监控其值,对用户设置做出反应:
view plaincopy to clipboardprint?
public void update() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
synchronized (mLock) {
…
int accelerometerDefault = Settings.System.getInt(resolver,
Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);
if (mAccelerometerDefault != accelerometerDefault) {
mAccelerometerDefault = accelerometerDefault;
updateOrientationListenerLp();
}
…
}
Java代码
public void update() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
synchronized (mLock) {
…
int accelerometerDefault = Settings.System.getInt(resolver,
Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);
if (mAccelerometerDefault != accelerometerDefault) {
mAccelerometerDefault = accelerometerDefault;
updateOrientationListenerLp();
}
…
}
上述是设置生效流程。当Orientation设置Enable时,会发生什么呢?
在PhoneWindowManager.java有个Listener,它会根据Sensor判别出的旋转方向,调用WindowManager.setRotation让屏幕进行旋转。另外,当应用程序显示禁止屏幕旋转时则不会旋转,见函数needSensorRunningLp()。
view plaincopy to clipboardprint?
class MyOrientationListener extends WindowOrientationListener {
MyOrientationListener(Context context) {
super(context);
}
@Override
public void onOrientationChanged(int rotation) {
// Send updates based on orientation value
if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation);
try {
mWindowManager.setRotation(rotation, false,
mFancyRotationAnimation);
} catch (RemoteException e) {
// Ignore
}
}
}
MyOrientationListener mOrientationListener;
boolean useSensorForOrientationLp(int appOrientation) {
if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
return true;
}
if (mAccelerometerDefault != 0 && (
appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER ||
appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
return true;
}
return false;
}
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
* screen is switched off.
*/
boolean needSensorRunningLp() {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
// If the application has explicitly requested to follow the
// orientation, then we need to turn the sensor or.
return true;
}
if (mAccelerometerDefault == 0) {
// If the setting for using the sensor by default is enabled, then
// we will always leave it on. Note that the user could go to
// a window that forces an orientation that does not use the
// sensor and in theory we could turn it off... however, when next
// turning it on we won't have a good value for the current
// orientation for a little bit, which can cause orientation
// changes to lag, so we'd like to keep it always on. (It will
// still be turned off when the screen is off.)
return false;
}
return true;
}
Java代码
class MyOrientationListener extends WindowOrientationListener {
MyOrientationListener(Context context) {
super(context);
}
@Override
public void onOrientationChanged(int rotation) {
// Send updates based on orientation value
if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation);
try {
mWindowManager.setRotation(rotation, false,
mFancyRotationAnimation);
} catch (RemoteException e) {
// Ignore
}
}
}
MyOrientationListener mOrientationListener;
boolean useSensorForOrientationLp(int appOrientation) {
if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
return true;
}
if (mAccelerometerDefault != 0 && (
appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER ||
appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
return true;
}
return false;
}
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
* screen is switched off.
*/
boolean needSensorRunningLp() {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
// If the application has explicitly requested to follow the
// orientation, then we need to turn the sensor or.
return true;
}
if (mAccelerometerDefault == 0) {
// If the setting for using the sensor by default is enabled, then
// we will always leave it on. Note that the user could go to
// a window that forces an orientation that does not use the
// sensor and in theory we could turn it off... however, when next
// turning it on we won't have a good value for the current
// orientation for a little bit, which can cause orientation
// changes to lag, so we'd like to keep it always on. (It will
// still be turned off when the screen is off.)
return false;
}
return true;
}
在WindowOrientationListener(见文件javaframeworks/base/core/java/android/view/WindowOrientationListener.java)中会监听Sensor的值,对旋转方向进行判断,然后调用抽象方法onOrientationChanged,因此,只要在子类Listener中重新实现这个函数即可对四个不同方向做出响应(见上面让屏幕旋转即是一例)。
遗憾的是在Donut和Éclair中,对旋转方向的识别只给出了90度旋转,在Froyo中增加了一个270度旋转,不支持180度即倒立的操作。
可以在修改下面代码,判断来自于Gsensor的值,识别出旋转方向:
view plaincopy to clipboardprint?
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
float X = values[_DATA_X];
float Y = values[_DATA_Y];
float Z = values[_DATA_Z];
float OneEightyOverPi = 57.29577957855f;
float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);
float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi;
int rotation = -1;
if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {
// Check orientation only if the phone is flat enough
// Don't trust the angle if the magnitude is small compared to the y value
float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;
int orientation = 90 - (int)Math.round(angle);
// normalize to 0 - 359 range
while (orientation >= 360) {
orientation -= 360;
}
while (orientation < 0) {
orientation += 360;
}
// Orientation values between LANDSCAPE_LOWER and PL_LOWER
// are considered landscape.
// Ignore orientation values between 0 and LANDSCAPE_LOWER
// For orientation values between LP_UPPER and PL_LOWER,
// the threshold gets set linearly around PIVOT.
if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) {
float threshold;
float delta = zyangle - PIVOT;
if (mSensorRotation == Surface.ROTATION_90) {
if (delta < 0) {
// Delta is negative
threshold = LP_LOWER - (LP_LF_LOWER * delta);
} else {
threshold = LP_LOWER + (LP_LF_UPPER * delta);
}
rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90;
} else {
if (delta < 0) {
// Delta is negative
threshold = PL_UPPER+(PL_LF_LOWER * delta);
} else {
threshold = PL_UPPER-(PL_LF_UPPER * delta);
}
rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0;
}
} else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) {
rotation = Surface.ROTATION_90;
} else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) {
rotation = Surface.ROTATION_0;
}
if ((rotation != -1) && (rotation != mSensorRotation)) {
mSensorRotation = rotation;
onOrientationChanged(mSensorRotation);
}
}
}
Java代码
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
float X = values[_DATA_X];
float Y = values[_DATA_Y];
float Z = values[_DATA_Z];
float OneEightyOverPi = 57.29577957855f;
float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);
float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi;
int rotation = -1;
if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {
// Check orientation only if the phone is flat enough
// Don't trust the angle if the magnitude is small compared to the y value
float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;
int orientation = 90 - (int)Math.round(angle);
// normalize to 0 - 359 range
while (orientation >= 360) {
orientation -= 360;
}
while (orientation < 0) {
orientation += 360;
}
// Orientation values between LANDSCAPE_LOWER and PL_LOWER
// are considered landscape.
// Ignore orientation values between 0 and LANDSCAPE_LOWER
// For orientation values between LP_UPPER and PL_LOWER,
// the threshold gets set linearly around PIVOT.
if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) {
float threshold;
float delta = zyangle - PIVOT;
if (mSensorRotation == Surface.ROTATION_90) {
if (delta < 0) {
// Delta is negative
threshold = LP_LOWER - (LP_LF_LOWER * delta);
} else {
threshold = LP_LOWER + (LP_LF_UPPER * delta);
}
rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90;
} else {
if (delta < 0) {
// Delta is negative
threshold = PL_UPPER+(PL_LF_LOWER * delta);
} else {
threshold = PL_UPPER-(PL_LF_UPPER * delta);
}
rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0;
}
} else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) {
rotation = Surface.ROTATION_90;
} else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) {
rotation = Surface.ROTATION_0;
}
if ((rotation != -1) && (rotation != mSensorRotation)) {
mSensorRotation = rotation;
onOrientationChanged(mSensorRotation);
}
}
}
在Froyo中,对上述算法进行了修改,让其报出270度的旋转方向。
Android Sensor 屏幕360度旋转实现
2010-09-17 10:51
修改下面函数 void android.view.WindowOrientationListener.SensorEventListenerImpl.onSensorChanged(android.hardware.SensorEvent event)
float[] values = event.values;
float X = values[_DATA_X];
float Y = values[_DATA_Y];
float Z = values[_DATA_Z];
//For fixing the problem of Sensor change the window orientation error but the sensor game is no problem.
float OneEightyOverPi = 57.29577957855f;
float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);
float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi;
int rotation = -1;
if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {
// Check orientation only if the phone is flat enough
// Don't trust the angle if the magnitude is small compared to the y value
float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;
int orientation = 90 - (int)Math.round(angle);
// normalize to 0 - 359 range
while (orientation >= 360) {
orientation -= 360;
}
while (orientation < 0) {
orientation += 360;
}
Log.i("Tiger","orientation="+orientation);
//确定由角度与屏幕方向的对应范围
if(orientation > 325 || orientation <= 45){
rotation = Surface.ROTATION_0;
}else if(orientation > 45 && orientation <= 135){
rotation = Surface.ROTATION_270;
}else if(orientation > 135 && orientation < 225){
rotation = Surface.ROTATION_180;
}else {
rotation = Surface.ROTATION_90;
}
Log.i("Tiger","mSensorRotation="+mSensorRotation+" , rotation="+rotation);
if ((rotation != -1) && (rotation != mSensorRotation)) {
mSensorRotation = rotation;
onOrientationChanged(mSensorRotation);
}
}
分享到: