【问题标题】:Android: Detect Orientation ChangedAndroid:检测方向已更改
【发布时间】:2012-01-05 02:30:05
【问题描述】:

我需要检测我的应用程序中的方向变化,但我不希望我的布局从纵向更改为横向。 目前我正在使用OrientationEventListener 但是检测方向角度是不够的。我想检测用户从纵向变为横向或反之亦然,这不仅仅是检测方向角是 90 还是 270。

我想做与 Android 相同的检测来改变活动的方向。我尝试覆盖 onConfigurationChanged 并检查方向是否为横向/纵向,但这仍然会将我的活动布局更改为横向。

有没有办法使用 onConfigurationChanged 但强制布局保持纵向?
是否有另一种方法可以在不使用OrientationEventListener 的情况下检测方向变化。最终我可以实现自己的方向改变算法,对此有什么想法吗?它必须比if(90-THRESHOLD <= orientation <= 90+THRESHOLD) 更复杂,我想检测用户是否进行了完整的移动Portrait->Landscape 或Landscape->Portrait。

感谢您的帮助,
菲利普

【问题讨论】:

  • 哎呀,对不起,脑子里放了你真正需要的东西。据我所知,您不能使用onConfigurationChanged(),除非您实际上允许更改配置(这意味着在这种情况下视图必须旋转)。在事件被分派到窗口系统之前没有办法拦截它。

标签: android android-layout screen-orientation


【解决方案1】:

好的,在尝试使用 Android API 但无法执行我需要的操作后,我实现了自己的算法,实际上并没有那么复杂: 我使用了 OrientationEventListener,并计算了方向是否在 4 个方向点中(在我的代码中,我只检测到 LANDSCAPE_RIGHTPORTRAIT_UP

orientationListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) {
        public void onOrientationChanged(int orientation) {
            if(canShow(orientation)){
                show();
            } else if(canDismiss(orientation)){
                dismiss();
            }
        }
    };

@Override
public void onResume(){
    super.onResume();
    orientationListener.enable();
}

@Override
public void onPause(){
    super.onPause();
    orientationListener.disable();
}

private boolean isLandscape(int orientation){
        return orientation >= (90 - THRESHOLD) && orientation <= (90 + THRESHOLD);
    }

private boolean isPortrait(int orientation){
    return (orientation >= (360 - THRESHOLD) && orientation <= 360) || (orientation >= 0 && orientation <= THRESHOLD);
}

public boolean canShow(int orientation){
    return !visible && isLandscape(orientation);
}

public boolean canDismiss(int orientation){
    return visible && !dismissing && isPortrait(orientation);
}

【讨论】:

  • 好的,现在我有另一个问题。我想在我的活动视图顶部显示的视图必须处于横向模式。如何在不强制应用程序也这样做的情况下强制视图处于横向状态?
  • 我遇到的问题似乎与您的问题很接近。 show() 和dismiss() 方法有什么作用?当我尝试您的示例时,出现运行时异常: ERROR/AndroidRuntime(7868): FATAL EXCEPTION: main android.app.SuperNotCalledException: Activity Orientation did not call through to super.onConfigurationChanged()
  • 但如果我在方法开始时调用 super - 所有方向(包括我不想支持的方向)都会显示
  • 在这种情况下THRESHOLD 的值是什么,为什么我们需要它。我也被困在这种情况下,并尝试使用此代码来获取当前方向。
  • 例如,如果您快速旋转您的设备,您可能会直接从 85º 度转到 92º 度,而不会在 90º 处调度事件。阈值是一个值,用于检测它是否几乎接近可以舍入的特定角度。尝试不同的值,我想我用的是 15 或 30。
【解决方案2】:

嗨,尖叫声是你要找的吗?

// Set background image, rotatable
    View view = getWindow().getDecorView(); 
    int orientation = getResources().getConfiguration().orientation; 
    if (Configuration.ORIENTATION_LANDSCAPE == orientation) { 
        //Do SomeThing; // Landscape
    } else { 
        //Do SomeThing;  // Portrait
    } 

【讨论】:

  • 是的,这行得通。谢谢。我宁愿使用 onConfigurationChanged 方法,因为使用您的解决方案我仍然需要 OrientationEventListener 来检查方向。我会等待 Devunwired 回复,看看他是否有解决方案。否则,我会将您的标记为已接受;)
  • 我的回答不行,所以我拉了。虽然,我担心这会遇到同样的问题。如果您不允许配置实际从纵向更改为横向,则无论设备方向如何,资源都不会报告纵向以外的任何内容。
  • 其实这行不通。如果我使用清单中的属性 screenOrientation="portrait" 将活动锁定为纵向模式,这将不起作用。这只会告诉屏幕方向是什么,而不是检测屏幕方向变化。
  • 好主意,谢谢!这正是我需要的!
【解决方案3】:

我创建了以下类来检测方向变化,保持我的活动原始方向:

public class SensorOrientationChangeNotifier {

    public final String TAG = getClass().getSimpleName();
    private ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>> mListeners = new ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>>(3);

    private int mOrientation = 0;
    private SensorEventListener mSensorEventListener;
    private SensorManager mSensorManager;

    private static SensorOrientationChangeNotifier mInstance;

    public static SensorOrientationChangeNotifier getInstance() {
        if (mInstance == null)
            mInstance = new SensorOrientationChangeNotifier();

        return mInstance;
    }

    private SensorOrientationChangeNotifier() {
        mSensorEventListener = new NotifierSensorEventListener();
        Context applicationContext = GlobalData.getInstance().getContext();
        mSensorManager = (SensorManager) applicationContext.getSystemService(Context.SENSOR_SERVICE);

    }

    /**
     * Call on activity reset()
     */
    private void onResume() {
        mSensorManager.registerListener(mSensorEventListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
    }

    /**
     * Call on activity onPause()
     */
    private void onPause() {
        mSensorManager.unregisterListener(mSensorEventListener);
    }

    private class NotifierSensorEventListener implements SensorEventListener {

        @Override
        public void onSensorChanged(SensorEvent event) {
            float x = event.values[0];
            float y = event.values[1];
            int newOrientation = mOrientation;
            if (x < 5 && x > -5 && y > 5)
                newOrientation = 0;
            else if (x < -5 && y < 5 && y > -5)
                newOrientation = 90;
            else if (x < 5 && x > -5 && y < -5)
                newOrientation = 180;
            else if (x > 5 && y < 5 && y > -5)
                newOrientation = 270;

            //Log.e(TAG,"mOrientation="+mOrientation+"   ["+event.values[0]+","+event.values[1]+","+event.values[2]+"]");
            if (mOrientation != newOrientation){
                mOrientation = newOrientation;
                notifyListeners();
            }

        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }

    }

    public int getOrientation() {
        return mOrientation;
    }

    public interface Listener {
        void onOrientationChange(int orientation);
    }

    public void addListener(SensorOrientationChangeNotifier.Listener listener) {
        if (get(listener) == null) // prevent duplications
            mListeners.add(new WeakReference<SensorOrientationChangeNotifier.Listener>(listener));

        if (mListeners.size() == 1) {
            onResume(); // this is the first client
        }
    }

    public void remove(SensorOrientationChangeNotifier.Listener listener) {
        WeakReference<SensorOrientationChangeNotifier.Listener> listenerWR = get(listener);
        remove(listenerWR);
    }

    private void remove(WeakReference<SensorOrientationChangeNotifier.Listener> listenerWR) {
        if (listenerWR != null)
            mListeners.remove(listenerWR);

        if (mListeners.size() == 0) {
            onPause();
        }

    }

    private WeakReference<SensorOrientationChangeNotifier.Listener> get(SensorOrientationChangeNotifier.Listener listener) {
        for (WeakReference<SensorOrientationChangeNotifier.Listener> existingListener : mListeners)
            if (existingListener.get() == listener)
                return existingListener;
        return null;
    }

    private void notifyListeners() {
        ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>> deadLiksArr = new ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>>();
        for (WeakReference<SensorOrientationChangeNotifier.Listener> wr : mListeners) {
            if (wr.get() == null)
                deadLiksArr.add(wr);
            else
                wr.get().onOrientationChange(mOrientation);
        }

        // remove dead references 
        for (WeakReference<SensorOrientationChangeNotifier.Listener> wr : deadLiksArr) {
            mListeners.remove(wr);
        }
    }

    public boolean isPortrait(){
        return mOrientation == 0 || mOrientation == 180;
    }

    public boolean isLandscape(){
        return !isPortrait();
    }
}

如下使用:

在 AndroidManifest.xml 中 -

    <activity
        ...
        android:screenOrientation="portrait"
        >

在你的活动中:

public class MainActivity extends Activity implements  SensorOrientationChangeNotifier.Listener {

    @Override
    protected void onResume() {
        super.onResume();

        SensorOrientationChangeNotifier.getInstance().addListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        SensorOrientationChangeNotifier.getInstance().remove(this);
    }

    @Override
    public void onOrientationChange(int orientation) {
            if (orientation == 90 || orientation == 270){
                 // Do some landscape stuff
                } else {
                 // Do some portrait stuff
                 }
        }
    }
}

【讨论】:

    【解决方案4】:

    Asaf Pinhassi 答案的 kotlin 版本。

    import android.content.Context
    import android.hardware.Sensor
    import android.hardware.SensorEvent
    import android.hardware.SensorEventListener
    import android.hardware.SensorManager
    import java.lang.ref.WeakReference
    import java.util.*
    
    class SensorOrientationChangeNotifier private constructor(context: Context) {
    val TAG = javaClass.simpleName
    private val mListeners = ArrayList<WeakReference<Listener?>>(3)
    var orientation = 0
        private set
    private val mSensorEventListener: SensorEventListener
    private val mSensorManager: SensorManager
    
    /**
     * Call on activity reset()
     */
    private fun onResume() {
        mSensorManager.registerListener(mSensorEventListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
    }
    
    /**
     * Call on activity onPause()
     */
    private fun onPause() {
        mSensorManager.unregisterListener(mSensorEventListener)
    }
    
    private inner class NotifierSensorEventListener : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            val x = event.values[0]
            val y = event.values[1]
            var newOrientation: Int = orientation
            if (x < 5 && x > -5 && y > 5) newOrientation = 0 else if (x < -5 && y < 5 && y > -5) newOrientation = 90 else if (x < 5 && x > -5 && y < -5) newOrientation = 180 else if (x > 5 && y < 5 && y > -5) newOrientation = 270
    
            //Log.e(TAG,"mOrientation="+mOrientation+"   ["+event.values[0]+","+event.values[1]+","+event.values[2]+"]");
            if (orientation != newOrientation) {
                orientation = newOrientation
                notifyListeners()
            }
        }
    
        override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
    }
    
    interface Listener {
        fun onOrientationChange(orientation: Int)
    }
    
    fun addListener(listener: Listener) {
        if (get(listener) == null) // prevent duplications
            mListeners.add(WeakReference(listener))
        if (mListeners.size == 1) {
            onResume() // this is the first client
        }
    }
    
    fun remove(listener: Listener) {
        val listenerWR = get(listener)
        remove(listenerWR)
    }
    
    private fun remove(listenerWR: WeakReference<Listener?>?) {
        if (listenerWR != null) mListeners.remove(listenerWR)
        if (mListeners.size == 0) {
            onPause()
        }
    }
    
    private operator fun get(listener: Listener): WeakReference<Listener?>? {
        for (existingListener in mListeners) if (existingListener.get() === listener) return existingListener
        return null
    }
    
    private fun notifyListeners() {
        val deadLiksArr = ArrayList<WeakReference<Listener?>>()
        for (wr in mListeners) {
            if (wr.get() == null) deadLiksArr.add(wr) else wr.get()!!.onOrientationChange(orientation)
        }
    
        // remove dead references
        for (wr in deadLiksArr) {
            mListeners.remove(wr)
        }
    }
    
    val isPortrait: Boolean
        get() = orientation == 0 || orientation == 180
    
    val isLandscape: Boolean
        get() = !isPortrait
    
    companion object {
        private var mInstance: SensorOrientationChangeNotifier? = null
        fun getInstance(context: Context): SensorOrientationChangeNotifier? {
            if (mInstance == null) mInstance = SensorOrientationChangeNotifier(context)
            return mInstance
        }
    }
    
    init {
        mSensorEventListener = NotifierSensorEventListener()
        mSensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }
    }
    

    【讨论】:

      【解决方案5】:

      为了检测方向,我们会要求活动提供活动的宽度和高度,所以我们可以找到宽度>高度将是“横向”或相反的高度>宽度是“纵向” - 的以下代码已经过测试和工作。

      @SuppressWarnings("deprecation")
      public void detectScreenOrientation(){
      WindowManager wm = getWindowManager();
      Display d = wm.getDefaultDisplay();
      if (d.getWidth() > d.getHeight()){
          Log.d("Orientation","Landscape mode");
      }
      else {
          Log.d("Orientation", "Portrait mode");
      }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 2017-06-08
        • 1970-01-01
        • 1970-01-01
        • 2012-03-07
        相关资源
        最近更新 更多