2010-08-09 3 views
33

Я работаю над подклассом FrameLayout, который должен вращать все его дети на 90 градусов. Я делаю это, чтобы преодолеть ограниченное по ландшафту ограничение камеры, присутствующее в android 2.1 и ниже, благодаря тому, что активность была в ландшафте, но накладывая наложение этой камеры на этот оверлей рамки, чтобы он выглядел так, как будто это был портрет (this is how Layar does it). сделайте это, я приспосабливаю Jeff Sharkey's code, чтобы повернуть виды. Моя проблема в том, что я могу повернуть Framelayout, но я не могу изменить его размер в соответствии с новыми измерениями. Так что на моем g1, а не на 320x480 портретном представлении над разрешением 480x320 в ландшафте, я получаю окно размером 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 размеры View, но не были в состоянии получить, чтобы работать. Любая помощь, которую вы можете предоставить, очень ценится.

+0

Привет, вы так решили? – fhucho

+0

Nope - окончательное решение заключалось в том, чтобы использовать вышеуказанный код как есть и просто поворачивать кнопки, текстовые изображения и т. Д. Индивидуально, обертывая каждый из них в одном из них. Это не очень элегантно, но это работает. – QRohlf

+2

Я считаю, что внутри 'dispatchTouchEvent()' вы должны использовать 'getRawX()' и 'getRawY()' вместо 'getX()' и 'getY()'. Во всяком случае, это работает лучше для меня. Мой взгляд квадратный, fwiw. –

ответ

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 - на самом дне. После поворота макета расстояние между двумя изображениями останется прежним - вместо того, чтобы расширяться, чтобы быть в верхней/нижней части повернутого вида, они будут где-то посередине. –

+0

благодарит вашу идею/вы могли бы сказать более подробно «просто поместите FrameLayout над SurfaceHolder» Как я могу это сделать? – LTEHUB

+1

В вашем XML-файле макета создайте рамку и поместите ее так, чтобы она лежала над поверхностным держателем (рамная развязка полностью покрывает поверхностный держатель). – Georg

0

Я думаю, вы забыли одну строку в своем onMeasure.

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

Взятые из кода для вертикального SeekBar здесь: 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

Попробуйте отключить ребенок отсечение вашего взгляда корня: вызов setClipChildren(false) на родитель вашего RotateLayout и в методе onMeasure вашего RotateLayout поместить эти строки:

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

Я, имеющие в основном ту же проблема, как вы и Я все еще не проверял свое решение - я сделаю это завтра и скажу, правильно ли он работает.

+0

Появляется 'setClipChildren (false)' не работает. Интересно, почему... – DoDo

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

таким образом, вы способны встраивать повернутый View/ViewGroup в определении XML и раздувать «порт» и " в то время как ориентация экрана меняется, но это кажется из этой темы.

Редактировать: На самом деле гораздо лучше отложить настройку смещения, пока не закончится хотя бы один макет. Это связано с тем, что в моем случае после первого onMeasure() будет отображаться представление (до того, как были установлены смещения). В конце концов, это можно было бы ощутить как сбой, потому что представление/макет сначала не вырисовывались в финальных границах. (обновлен фрагмент)

Смежные вопросы