旋转视图层次结构90度

37

我正在开发一个FrameLayout的子类,旨在将其所有子项旋转90度。 我这样做是为了克服Android 2.1及以下版本中仅支持横屏模式相机的限制,使活动处于横向状态,但将我的相机叠加层放置到此FrameLayout中,使其看起来好像是竖向模式(Layar就是这样做的)。 为了实现这一点,我正在使用Jeff Sharkey的代码来旋转视图。 我的问题是我可以旋转Framelayout,但无法调整大小以匹配新的尺寸。 因此,在我的G1上,我得到一个320x320的框中间显示我的纵向视图,两侧被切掉而不是一个320x480的竖直视图覆盖着一个480x320的横向相机视图。

这是我目前的代码:

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

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


嗨,你解决了这个问题吗? - fhucho
不,最终的解决方案是直接使用上述代码,并通过将每个按钮、文本视图等单独包装在其中来逐个旋转它们。这并不是非常优雅,但它能够正常工作。 - QRohlf
2
我认为在dispatchTouchEvent()方法内,你应该使用getRawX()getRawY()而不是getX()getY()。至少对我来说,这样效果更好。顺便提一下,我的视图是正方形。 - Carl Manaster
正如Carl Manaster所说,需要使用getRawX和getRawY。因为这解决了我的问题,所以我给Carl加上+1。谢谢。 - PerracoLabs
6个回答

40

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

首先,在/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>

然后,在你的 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);
 }

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

希望这能对某人有所帮助,花了我相当长的时间才把所有东西整合到一起。


谢谢你的想法。你能详细说一下“只需在SurfaceHolder之上放置一个FrameLayout”吗?我该怎么做呢? - LTEHUB
1
在您的布局xml文件中,创建一个FrameLayout并将其放置在SurfaceHolder上方(FrameLayout完全覆盖SurfaceHolder)。 - Georg
@georg,您所说的“above”是什么意思?哪种布局适合同时容纳FrameLayout和SurfaceLayout? - Jameo
我们能用相对布局实现同样的效果吗?为什么要使用帧布局? - Alok Rajasukumaran
你可以对任何视图都这样做。我已经在RelativeLayout上这样做了。 - yerlilbilgin
显示剩余2条评论

9
使用API级别11及更高版本,您可以使用方法setRotation(degreesFloat);以编程方式更改视图的旋转,或者您可以使用XML属性android:rotation=""在XML中进行更改。还有一些方法/属性可用于仅更改视图旋转的X或Y值:Android文档-视图(setRotation)
因此,现在只要您使用的是API级别11或更高版本,就应该能够将旋转应用于包装器布局节点。但是,您可能还需要更改顶层布局的尺寸以匹配旋转后所需的尺寸。也就是说,如果您有一个纵向视图,其尺寸为800x1280,则必须将它们更改为1280x800,以便在旋转到横向后对齐。

4

1
这通常是对我有效的做法。
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);
}

你需要做的是交换宽度和高度,然后设置X和Y偏移量,以便旋转后视图变成全屏状态。上面是“横向”旋转版本。要实现反向横向旋转,只需应用270度旋转即可。你可以在代码片段中修改代码,也可以在外部以更通用的方式应用旋转。
final YourViewOrViewGroup layout = inflater.inflate(...);
if (currentOrientation.isInverted()) {
    layout.setRotation(layout.getRotation + 180f);
}

这样您就能够在XML定义中嵌入旋转的View/ViewGroup,并在屏幕方向改变时膨胀'port'和'land'版本,但这似乎与此主题无关。
编辑:实际上,最好将偏移设置推迟到至少完成一次布局传递之后。这是因为在我的情况下,在第一次onMeasure()之后,视图会被绘制(在偏移量设置之前)。最终可能会出现故障,因为视图/布局在最初不会在最终边界内绘制。
(更新片段)

0
我觉得你在onMeasure中忘记了一行代码。
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     super.onMeasure(heightMeasureSpec, widthMeasureSpec);
     setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}

从这里获取垂直SeekBar的代码: 如何在Android中获得一个可用的垂直SeekBar? 你可能需要调整getMeasuredHeight()以使其适合你屏幕的正确大小。

0

尝试关闭您视图根的子剪辑:在RotateLayout的父级上调用setClipChildren(false),并在RotateLayout的onMeasure方法中添加以下行:

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

我遇到的问题基本上和你一样,但我还没有测试我的解决方案 - 我明天会测试并告诉你它是否正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接