web-dev-qa-db-fra.com

Faire pivoter la hiérarchie de vue de 90 degrés

Je travaille sur une sous-classe de FrameLayout qui est censée faire pivoter tous ses enfants de 90 degrés. Je fais cela pour surmonter la limitation de la caméra en mode paysage uniquement présente dans Android 2.1 et ci-dessous, en ayant l'activité en mode paysage, mais en plaçant la superposition de ma caméra dans cette superposition framelayout pour la faire apparaître comme s'il s'agissait d'un portrait ( c'est ainsi que Layar le fait ) Pour ce faire, j'adapte le code de Jeff Sharkey pour faire pivoter les vues. Mon problème est que je peux faire pivoter le Framelayout, mais je ne peux pas le redimensionner pour qu'il corresponde aux nouvelles dimensions. Donc sur mon g1, au lieu d'une vue portrait 320x480 sur une vue caméra 480x320 en paysage, j'obtiens une boîte 320x320 au milieu montrant ma vue portrait avec les côtés coupés.

Voici mon code jusqu'à présent:

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

J'ai essayé de remplacer OnMeasure pour changer les dimensions X et Y de la vue, mais je n'ai pas réussi à le faire fonctionner. Toute aide que vous pouvez fournir est fortement appréciée.

35
QRohlf

J'ai eu le même problème et j'ai réussi à le résoudre. Au lieu de faire tourner chaque vue ou la mise en page à la main, j'ai utilisé un LayoutAnimationController.

Tout d'abord, placez un fichier dans/res/anim/appelé 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>

Ensuite, dans la création de votre activité, faites

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

Si vous souhaitez faire pivoter des éléments situés au-dessus de la vue d'aperçu de votre caméra (SurfaceHolder), placez simplement un FrameLayout au-dessus du SurfaceHolder, placez tous vos éléments dans ce FrameLayout et appelez la mise en page "MyScreen_ContentLayout". Terminé.

J'espère que cela a aidé quelqu'un, m'a pris un bon moment pour tout rassembler.

39
Georg

En utilisant l'API de niveau 11 et versions ultérieures, vous pouvez utiliser la méthode setRotation(degreesFloat); pour modifier la rotation d'une vue par programme, ou vous pouvez utiliser l'attribut XML Android:rotation="" pour le changer dans votre XML. Il existe également des méthodes/attributs pour modifier uniquement les valeurs X ou Y de la rotation d'une vue: Android Docs - View (setRotation) .

Donc, de nos jours, tant que vous utilisez le niveau 11 ou supérieur de l'API, vous devriez pouvoir appliquer la rotation à un nœud de présentation de wrapper. Cependant, vous devrez probablement également modifier les dimensions de la disposition de niveau supérieur pour qu'elles correspondent aux dimensions souhaitées après la rotation. C'est à dire. si vous avez une vue portrait avec des dimensions 800x1280, vous devrez les changer en 1280x800 afin de l'aligner après la rotation en paysage.

7
Alex Ross

En utilisant cette bibliothèque, vous pouvez faire pivoter toute la hiérarchie des vues https://github.com/rongi/rotate-layout

Comme ça

enter image description here

3
Dmitry Ryadnenko

C'est ce qui a fonctionné pour moi en général.

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

Ce que vous voulez faire est d'échanger la largeur avec la hauteur, puis de placer les décalages X et Y de sorte que la vue devienne plein écran après la rotation.

Ce qui précède est une version "paysage" tournée. Pour obtenir un paysage inversé, appliquez simplement une rotation de 270 degrés. Vous pouvez soit modifier le code dans l'extrait de code, soit appliquer la rotation à l'extérieur de manière plus générique, c'est-à-dire

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

de cette façon, vous pouvez intégrer le View/ViewGroup pivoté dans la définition xml et gonfler les versions 'port' et 'land' pendant que l'orientation de l'écran change, mais cela semble hors de ce sujet.

Edit: en fait, il est préférable de différer le paramètre de décalage jusqu'à ce qu'au moins une passe de mise en page soit terminée. Cela est dû au fait que dans mon cas après le premier onMeasure (), la vue serait dessinée (avant que les décalages ne soient définis). Finalement, cela pourrait être ressenti comme un pépin, car la vue/la mise en page ne serait pas dessinée dans les limites finales au début. (mise à jour de l'extrait)

1
foo

Je pense que vous avez oublié une ligne dans votre onMeasure.

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

Tiré du code d'une barre de recherche verticale ici: Comment puis-je obtenir une barre de recherche verticale fonctionnelle dans Android?

Vous devrez peut-être jouer avec getMeasuredHeight () pour lui donner la taille correcte de votre écran.

0
Li_W

Essayez de désactiver l'écrêtage des enfants de votre racine de vue: appelez setClipChildren(false) sur le parent de votre RotateLayout et dans la méthode onMeasure de votre RotateLayout, mettez ces lignes:

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

J'ai essentiellement le même problème que vous et je n'ai toujours pas testé ma solution - je le ferai demain et je dirai si cela fonctionne correctement.

0
DoDo