2010-08-09 153 views
33

我正在研究FrameLayout的子类,它应该将其所有子级旋转90度。我这样做是为了克服android 2.1及其以下版本中的风景相机限制,通过让活动处于横向模式,但将相机覆盖层放入此framelayout覆盖层,使其看起来好像是纵向(this is how Layar does it)。完成这个,我正在修改Jeff Sharkey's code来旋转视图。我的问题是我可以旋转Framelayout,但我无法调整它的大小来匹配新的尺寸。所以在我的g1上,取代了横向480x320相机视图上的320x480纵向视图,在中间显示了一个320x320的盒子,显示了我的肖像视图,并且两边被切掉了。旋转视图层次结构90度

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

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尺寸,但一直没能得到那个工作。 任何帮助,您可以提供非常感谢。

+0

嗨,你有没有解决这个问题? – fhucho 2010-09-05 13:41:55

+0

不 - 最终的解决方案是按原样使用上述代码,并通过将各个按钮包装在其中之一中来单独旋转按钮,文本浏览等。这不是很优雅,但它的作品。 – QRohlf 2010-09-06 22:50:51

+2

我相信'dispatchTouchEvent()'里面应该使用getRawX()和getRawY()来代替getX()和getY()。无论如何,这对我来说效果更好。我的观点是方形的,fww。 – 2011-04-27 23:21:40

回答

39

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

首先将文件/ RES /动画/被叫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> 

然后,在你活动的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中并调用布局“MyScreen_ContentLayout”。完成。

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

+0

这是一个有趣的解决方案。我遇到的唯一问题是旋转的布局不能展开。举例来说,如果您的布局有2张图片 - 其中1张位于屏幕的最上方,1张位于最底部。旋转布局后,两幅图像之间的距离将保持不变 - 而不是扩大到旋转视图的顶部/底部,它们将位于中间的某处。 – 2011-02-01 01:10:12

+0

感谢您的想法/可以说更多的细节“只需在SurfaceHolder上方放置一个FrameLayout”我该怎么做? – LTEHUB 2011-04-11 13:04:38

+1

在您的布局xml文件中,创建一个框架布局并将它放置在水平面之上(框架布局完全覆盖了水边框)。 – Georg 2011-04-14 15:34:36

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()摆弄,使其屏幕的正确尺寸。

6

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

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

0

尝试关闭儿童裁剪视图根:你的RotateLayout的父母和你的RotateLayout的onMeasure方法调用setClipChildren(false)把这几行:

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

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

+0

看起来'setClipChildren(false)'不起作用。我想知道为什么... – DoDo 2013-05-31 09:51:58

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定义中嵌入旋转视图/ ViewGroup中和膨胀“端口”和'陆地'版本,而屏幕方向变化,但这似乎超出了这个话题。

编辑:实际上推迟偏移设置要好得多,直到至少一个布局过程结束。 这是因为,在我的情况下,第一次onMeasure()后,将绘制视图(在设置偏移量之前)。最终它可能会被视为有问题,因为视图/布局一开始就不会被绘制在最终的范围内。 (更新了片段)