将视图层次结构旋转90度

时间:2010-08-09 22:47:43

标签: android android-layout animation android-widget

我正在研究FrameLayout的子类,它应该将其所有子节点旋转90度。我这样做是为了克服Android 2.1及以下版本中仅限风景的相机限制,让活动处于风景中,但将我的相机叠加放入此framelayout叠加层,使其看起来像是肖像({{3为了实现这一点,我正在调整this is how Layar does it来旋转视图。我的问题是我可以旋转Framelayout,但我不能调整它以匹配新的尺寸。所以在我的g1上,而不是在横向480x320相机视图上的320x480纵向视图,我在中间看到一个320x320的盒子,显示我的纵向视图,两侧被切断。

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

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维度,但无法使其工作。 非常感谢您提供的任何帮助。

6 个答案:

答案 0 :(得分:39)

我遇到了同样的问题,并设法解决了这个问题。 我没有手动旋转每个视图或布局,而是使用了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)上方的元素,只需将FrameLayout放在SurfaceHolder上方,将所有元素放在FrameLayout中,然后调用Layout“MyScreen_ContentLayout”。完成。

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

答案 1 :(得分:7)

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

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

答案 2 :(得分:3)

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

喜欢这个

enter image description here

答案 3 :(得分: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&amp; Y偏移,以便在旋转后视图变为全屏。

以上是'风景'旋转版。要实现倒置景观,只需施加270度旋转即可。您可以修改代码段中的代码,也可以以更通用的方式在外部应用轮换,即

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

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

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

答案 4 :(得分:0)

我想你在onMeasure上忘记了一行。

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

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

您可能需要使用getMeasuredHeight()来调整屏幕大小。

答案 5 :(得分:0)

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

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

我和你的问题基本相同,我还没有测试过我的解决方案 - 明天我会做,告诉它是否正常工作。