Android 将视图层次结构旋转 90 度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3444764/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 10:14:00  来源:igfitidea点击:

Rotate View Hierarchy 90 degrees

androidandroid-layoutanimationandroid-widget

提问by QRohlf

I am working on a subclass of FrameLayout that is supposed to rotate all of its children by 90 degrees. I am doing this to overcome the landscape-only camera limitation present in android 2.1 and below, by having the activity be in landscape, but placing my camera overlay into this framelayout overlay to cause it to appear as if it was portrait (this is how Layar does it) To accomplish this, I'm adapting Jeff Sharkey's codeto rotate views. My problem is that I can rotate the Framelayout, but I cannot resize it to match the new dimensions. So on my g1, instead of a 320x480 portrait view over a 480x320 camera view in landscape, I get a 320x320 box in the middle showing my portrait view with the sides chopped off.

我正在研究 FrameLayout 的一个子类,它应该将其所有子类旋转 90 度。我这样做是为了克服 android 2.1 及更低版本中存在的仅限横向的相机限制,方法是让活动处于横向状态,但将我的相机叠加层放入此框架布局叠加层中,使其看起来好像是纵向的(这就是方式Layar 做到了)为了实现这一点,我正在调整Jeff Sharkey 的代码来旋转视图。我的问题是我可以旋转 Framelayout,但我无法调整它的大小以匹配新尺寸。因此,在我的 g1 上,不是在横向 480x320 相机视图上显示 320x480 纵向视图,而是在中间得到一个 320x320 框,显示我的纵向视图,侧面被切掉。

Here is my code so far:

到目前为止,这是我的代码:

public class RotateLayout extends FrameLayout {
    private Matrix mForward = new Matrix();
    private Matrix mReverse = new Matrix();
    private float[] mTemp = new float[2];

    public RotateLayout(Context context) {
        super(context);
    }

    public RotateLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

    }


    /* (non-Javadoc)
     * @see android.widget.FrameLayout#onMeasure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //This didn't work:
        //super.onMeasure(heightMeasureSpec, widthMeasureSpec);
    }

    /* (non-Javadoc)
     * @see android.widget.FrameLayout#onSizeChanged(int, int, int, int)
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.rotate(270, getWidth()/2, getHeight()/2);
        //This code will stretch the canvas to accommodate the new screen size. This is not what I want.
        //float scaleX=(float)getHeight()/getWidth();
        //float scaleY=(float)getWidth()/getHeight();
        //canvas.scale(scaleX, scaleY,  getWidth()/2, getHeight()/2);
        mForward = canvas.getMatrix();
        mForward.invert(mReverse);
        canvas.save();
        canvas.setMatrix(mForward); //This is the matrix we need to use for proper positioning of touch events
        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final float[] temp = mTemp;
        temp[0] = event.getX();
        temp[1] = event.getY();

        mReverse.mapPoints(temp);

        event.setLocation(temp[0], temp[1]);
        return super.dispatchTouchEvent(event);
    }
}

I have tried overriding OnMeasureto switch the X and Y dimensions of the View, but have not been able to get that to work. Any help you can provide is greatly appreciated.

我尝试覆盖OnMeasure以切换视图的 X 和 Y 维度,但无法使其正常工作。非常感谢您可以提供的任何帮助。

回答by Georg

I had the same problem and managed to solve it. Instead of rotating each view or the layout by hand, I used a LayoutAnimationController.

我遇到了同样的问题并设法解决了它。我没有手动旋转每个视图或布局,而是使用了 LayoutAnimationController。

First, place a file in /res/anim/ called rotation.xml

首先,在 /res/anim/ 中放置一个名为 rotation.xml 的文件

<?xml version="1.0" encoding="utf-8"?>
<rotate
 xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="-90"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="0" android:fillAfter="true">
</rotate>

Then, in your Activity's onCreate, do

然后,在您的 Activity 的 onCreate 中,执行

  @Override
  public void onCreate(Bundle icicle) {
  super.onCreate(icicle);

     setContentView(R.layout.myscreen);

     Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotation);
     LayoutAnimationController animController = new LayoutAnimationController(rotateAnim, 0);
     FrameLayout layout = (FrameLayout)findViewById(R.id.MyScreen_ContentLayout);
     layout.setLayoutAnimation(animController);
 }

If you want to rotate elements that lie above your camera preview view (SurfaceHolder), simply place a FrameLayout above the SurfaceHolder, place all your elements in that FrameLayout and call the Layout "MyScreen_ContentLayout". Done.

如果要旋转位于相机预览视图 (SurfaceHolder) 上方的元素,只需在 SurfaceHolder 上方放置一个 FrameLayout,将所有元素放置在该 FrameLayout 中,然后将布局命名为“MyScreen_ContentLayout”。完毕。

Hope that helped someone out, took me quite a while to get everything together.

希望能帮助别人,花了我很长时间才把所有东西放在一起。

回答by Alex Ross

Using API level 11 and later you can use the method setRotation(degreesFloat);to change the rotation of a view programmatically, or you can use the XML attribute android:rotation=""to change it in your XML. There are also methods/attributes for changing only the X or Y values of a view's rotation: Android Docs - View (setRotation).

使用 API 级别 11 及更高版本,您可以使用该方法setRotation(degreesFloat);以编程方式更改视图的旋转,也可以使用 XML 属性在 XMLandroid:rotation=""中更改它。还有一些方法/属性可以仅更改视图旋转的 X 或 Y 值:Android Docs - View (setRotation)

So nowadays as long as you're using API level 11 or above, you should be able to apply the rotation to a wrapper layout node. However, you probably will also have to change the dimensions of the top-level layout to match the dimensions you desire after the rotation. I.e. if you have a portrait view w/ dimensions 800x1280, you'll have to change them to 1280x800 in order for it to line up after rotating to landscape.

因此,现在只要您使用 API 级别 11 或更高级别,您就应该能够将旋转应用于包装器布局节点。但是,您可能还必须更改顶级布局的尺寸以匹配旋转后所需的尺寸。即,如果您有一个尺寸为 800x1280 的纵向视图,则必须将它们更改为 1280x800,以便在旋转到横向后对齐。

回答by Dmitry Ryadnenko

Using this library you can rotate whole view hierarchy https://github.com/rongi/rotate-layout

使用此库,您可以旋转整个视图层次结构https://github.com/rongi/rotate-layout

Like this

像这样

enter image description here

在此处输入图片说明

回答by foo

This is what has worked for me in general.

这通常对我有用。

private void init() {
    setRotation(90f);
}

public YourViewOrViewGroup(final Context context) {
    super(context);
    init();
}

... (all the View/ViewGroup constructors) ...

@Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
    super.onLayout(changed, l, t, r, b);

    final int width = getWidth();
    final int height = getHeight();
    final int offset = Math.abs(width - height) / 2;
    setTranslationX(-offset);
    setTranslationY(offset);
}

@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
}

What you want to do is swap the width with height and then put the X & Y offsets so that the view becomes full screen after the rotation.

您想要做的是将宽度与高度交换,然后放置 X 和 Y 偏移量,以便视图在旋转后变为全屏。

The above is a 'landscape'-rotated version. To achieve a landscape inverted just apply 270-deg rotation. You can either modify code within the snippet or apply the rotation outside in a more generic way, i.e

以上是“风景”旋转的版本。要实现景观倒置,只需应用 270 度旋转。您可以修改代码段内的代码,也可以以更通用的方式在外部应用旋转,即

final YourViewOrViewGroup layout = inflater.inflate(...);
if (currentOrientation.isInverted()) {
    layout.setRotation(layout.getRotation + 180f);
}

this way you are able to embed the rotated View/ViewGroup within the xml definition and inflate 'port' and 'land' versions while the screen orientation changes, but this seems out of this topic.

通过这种方式,您可以在 xml 定义中嵌入旋转的 View/ViewGroup 并在屏幕方向更改时膨胀“port”和“land”版本,但这似乎超出了本主题。

Edit:actually it is much better to defer the offset setting until at least one layout pass is over. This is due the fact that in my case after first onMeasure() the view would be drawn (before the offsets were set). Eventually it could be experienced as glitching because the view/layout would not get drawn within the final bounds at first. (updated the snippet)

编辑:实际上最好将偏移设置推迟到至少一个布局传递结束。这是因为在我的情况下,在第一次 onMeasure() 之后将绘制视图(在设置偏移量之前)。最终它可能会被体验为小故障,因为视图/布局最初不会在最终边界内绘制。(更新了片段)

回答by DoDo

Try turning off children clipping of your view root: call setClipChildren(false)on parent of your RotateLayout and in onMeasure method of your RotateLayout put these lines:

尝试关闭视图根的子级剪辑:调用setClipChildren(false)RotateLayout 的父级并在 RotateLayout 的 onMeasure 方法中放置以下几行:

super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());

I'm having basically the same problem as you and I still haven't tested my solution - I'll do it tomorrow and tell if it is working correctly.

我遇到了与您基本相同的问题,但我还没有测试我的解决方案 - 我明天会测试并判断它是否正常工作。

回答by Li_W

I think you forgot one line in your onMeasure.

我认为您在 onMeasure 中忘记了一行。

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     super.onMeasure(heightMeasureSpec, widthMeasureSpec);
     setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}

Taken from the code for a vertical seekbar here: How can I get a working vertical SeekBar in Android?

取自这里的垂直搜索栏的代码: How can I get a working Vertical SeekBar in Android?

You might need to fiddle with the getMeasuredHeight() to make it the correct size of your screen.

您可能需要调整 getMeasuredHeight() 以使其成为屏幕的正确大小。