Подтвердить что ты не робот

Повернуть вид иерархии на 90 градусов

Я работаю над подклассом FrameLayout, который должен вращать все его дети на 90 градусов. Я делаю это, чтобы преодолеть ограниченное по ландшафту ограничение камеры, присутствующее в Android 2.1 и ниже, благодаря тому, что активность была в ландшафте, но наложение наложения этой камеры на эту овальную рамную рамку, чтобы она выглядела так, как будто это был портрет (это то, как Layar делает это). Для этого я адаптирую код Джеффа Шарки для поворота представлений. Моя проблема в том, что я могу повернуть 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 представления, но не смог заставить это работать. Любая помощь, которую вы можете предоставить, очень ценится.

4b9b3361

Ответ 1

У меня была такая же проблема, и мне удалось ее решить. Вместо поворота каждого вида или макета вручную я использовал LayoutAnimationController.

Сначала поместите файл в /res/anim/called 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". Готово.

Надеюсь, что кто-то помог, мне потребовалось довольно много времени, чтобы собрать все вместе.

Ответ 2

Используя API уровня 11 и более поздние версии, вы можете использовать метод setRotation(degreesFloat);, чтобы изменить вращение представления программным путем, или вы можете использовать атрибут XML android:rotation="", чтобы изменить его в своем XML. Существуют также методы/атрибуты для изменения только значений X или Y вращения представления: Android Docs - View (setRotation).

Итак, до тех пор, пока вы используете API-уровень 11 или выше, вы должны иметь возможность применить поворот к макету оболочки node. Однако вам, вероятно, также придется изменить размеры макета верхнего уровня в соответствии с размерами, которые вы хотите после поворота. То есть если у вас есть портретный вид с размерами 800x1280, вам придется изменить их на 1280x800, чтобы он выстраивался в линию после поворота на пейзаж.

Ответ 3

Используя эту библиотеку, вы можете повернуть всю иерархию представлений https://github.com/rongi/rotate-layout

Подобно этому

enter image description here

Ответ 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 градусов. Вы можете либо модифицировать код внутри фрагмента, либо применить поворот снаружи более общим образом, т.е.

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

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

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

Ответ 5

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

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

Взято из кода для вертикальной стрелки здесь: Как я могу получить рабочую вертикальную SeekBar в Android?

Вам может понадобиться играть с getMeasuredHeight(), чтобы сделать его правильным размером вашего экрана.

Ответ 6

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

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

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