将视图层次结构旋转 90 度
Posted
技术标签:
【中文标题】将视图层次结构旋转 90 度【英文标题】:Rotate View Hierarchy 90 degrees 【发布时间】:2011-03-27 13:31:44 【问题描述】:我正在研究 FrameLayout 的子类,它应该将其所有子类旋转 90 度。我这样做是为了克服 android 2.1 及更低版本中存在的仅横向相机限制,方法是让活动处于横向状态,但将我的相机叠加层放置在此框架布局叠加层中以使其看起来好像是纵向的 (this is how Layar does it ) 为了实现这一点,我正在调整 Jeff Sharkey's code 来旋转视图。我的问题是我可以旋转 Framelayout,但我无法调整它的大小以匹配新尺寸。所以在我的 g1 上,不是 320x480 纵向视图而不是横向 480x320 相机视图,而是在中间有一个 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 尺寸,但无法使其正常工作。
非常感谢您提供的任何帮助。
【问题讨论】:
嗨,你解决了这个问题吗? 不——最终的解决方案是按原样使用上面的代码,只需将按钮、文本视图等分别包装在其中一个中即可。它不是很优雅,但很有效。 我相信在dispatchTouchEvent()
内部你应该使用getRawX()
和getRawY()
而不是getX()
和getY()
。无论如何,这对我来说效果更好。我的观点是方形的,fwiw。
正如 Carl Manaster 所说,它必须是 getRawX 和 getRawY。给卡尔+1,因为它解决了我的问题。谢谢。
【参考方案1】:
我遇到了同样的问题并设法解决了。 我没有手动旋转每个视图或布局,而是使用了 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”。完成。
希望对某人有所帮助,我花了很长时间才把所有东西都整理好。
【讨论】:
这是一个有趣的解决方案。我遇到的唯一问题是旋转布局不会扩展。例如,如果您有一个包含 2 个图像的布局 - 1 个位于屏幕的最顶部,1 个位于屏幕的最底部。旋转布局后,两个图像之间的距离将保持不变 - 而不是扩大到旋转视图的顶部/底部,它们将位于中间的某个位置。 谢谢你的想法/你能说更多细节“只需在 SurfaceHolder 上方放置一个 FrameLayout”我该怎么做? 在您的布局 xml 文件中,创建一个框架布局并将其放置在表面支架上方(框架布局完全覆盖表面支架)。 @georg 你上面的意思是什么?什么样的布局可以同时容纳框架布局和表面布局? 如何旋转视图,以及“点击区域”?【参考方案2】:使用 API 级别 11 及更高版本,您可以使用方法 setRotation(degreesFloat);
以编程方式更改视图的旋转,或者您可以使用 XML 属性 android:rotation=""
在 XML 中更改它。还有一些方法/属性仅用于更改视图旋转的 X 或 Y 值:Android Docs - View (setRotation)。
所以现在只要您使用 API 级别 11 或更高级别,您就应该能够将旋转应用到包装器布局节点。但是,您可能还必须更改***布局的尺寸以匹配旋转后所需的尺寸。 IE。如果您有一个尺寸为 800x1280 的纵向视图,则必须将它们更改为 1280x800 才能使其在旋转到横向后对齐。
【讨论】:
【参考方案3】:使用这个库,您可以旋转整个视图层次结构https://github.com/rongi/rotate-layout
像这样
【讨论】:
【参考方案4】:一般来说,这对我有用。
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 度旋转。您可以修改 sn-p 中的代码或以更通用的方式在外部应用旋转,即
final YourViewOrViewGroup layout = inflater.inflate(...);
if (currentOrientation.isInverted())
layout.setRotation(layout.getRotation + 180f);
通过这种方式,您可以在 xml 定义中嵌入旋转的视图/视图组,并在屏幕方向发生变化时膨胀“端口”和“土地”版本,但这似乎超出了本主题。
编辑: 实际上,最好将偏移设置推迟到至少一个布局通道结束。 这是因为在我的情况下,在第一次 onMeasure() 之后将绘制视图(在设置偏移量之前)。最终它可能会被体验为故障,因为视图/布局一开始不会在最终范围内绘制。 (更新了sn-p)
【讨论】:
【参考方案5】:我认为您在 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】:尝试关闭视图根的子剪辑:在 RotateLayout 的父级上调用 setClipChildren(false)
并在 RotateLayout 的 onMeasure 方法中放置这些行:
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
我遇到了和你基本相同的问题,我还没有测试我的解决方案 - 我明天会做,看看它是否工作正常。
【讨论】:
以上是关于将视图层次结构旋转 90 度的主要内容,如果未能解决你的问题,请参考以下文章