如何在不重新创建布局的情况下旋转方向变化的视图?

How to rotate views on orientation change without recreating layout?

这个问题在 here 之前有人问过,但答案不正确。我想在屏幕方向更改时旋转一些视图,但我想保持布局不变。它们应根据当前方向旋转 90、180、270 或 360 度 (SCREEN_ORIENTATION_LANDSCAPESCREEN_ORIENTATION_PORTRAITSCREEN_ORIENTATION_REVERSE_LANDSCAPESCREEN_ORIENTATION_REVERSE_PORTRAIT)。

这就是我想要实现的:

我提到的 link 中的答案指出我应该在 layout-land 中创建一个新的不同布局。显然,这不是我想要的。我不想重新创建 activity 或更改布局方向。我只想旋转一些视图,并在方向更改时保持其他视图不变。

旋转 特定视图与更改或重新创建 整个布局(均在方向上)之间存在巨大差异变化)。

使用这个 link 中的答案,我将能够通过此方法获取当前屏幕方向:

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;
}

关于方向改变,我想做一些简单的事情:

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);
}

另一种改写我的问题的方法是 "Is there any way to detect orientation change in onConfigurationChanged() method without changing the layout?"。问题是如果我已经禁用布局方向更改,它将无法检测到任何方向更改。

谁知道是怎么做到的?我可能完全走错了步骤,我想我将不得不使用加速计传感器或类似的东西来实现我想要的,所以请指导我。

基础实际上要容易得多。看看 Handling Runtime Changes.

要事第一,通过设置

android:configChanges="orientation|keyboardHidden|screenSize"

在您的 activity 标签上的清单中,您可以自己处理方向更改。 (orientation 应该足够了,但有时会出现事件不会单独触发的问题。)

然后您跳过 onCreate,而是调用 onConfigurationChanged。覆盖此方法并在此处应用您的布局更改。无论您是在此处更改您的 linearLayouts 方向还是为不同的屏幕本身设置自定义视图处理布局,都取决于您并取决于您的实现。

动画会有点棘手,如果可能的话。快速搜索显示 it is not.


更新 评论"I only want to rotate some views themselves rather than rotating the layout"

理论上可以创建自己的布局并处理子视图的绘制。我刚刚尝试过,但无法在适当的时间产生任何结果,但您需要做的是:

  • 保留您最后的测量值在视图上使用标签或类似的方法来保留最后的测量值和布局,以便在方向更改后您可以 diff
  • 等待方向改变:触发旋转绘图 - 旋转canvas,用[=37=布置视图]previous 尺寸,并在 before
  • 的位置绘制子视图
  • 开始动画last 插值到 new 值,旋转 canvas从上次布局到新布局

我就是这样做的。

尝试使用OrientationEventListener。您不需要使用 onConfigurationChangedandroid:configChanges="orientation|keyboardHidden|screenSize"

您需要为 AndroidManifest.xml 中的 activity 设置 android:screenOrientation="portrait"。这是我的 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);
        }
    }
}
}

这也可以防止在 orientation 大约为 45、135... 等时旋转过于频繁。 希望对你有帮助。