web-dev-qa-db-fra.com

Changement d'orientation dans Android VideoView avec vidéo en mémoire tampon

J'essaie de reproduire les fonctionnalités de la dernière application YouTube sur le marché Android. Lorsque vous regardez une vidéo, vous disposez de deux mises en page distinctes, une en mode portrait qui fournit des informations supplémentaires et une en mode paysage qui fournit une vue plein écran de la vidéo.

YouTube app portrait layout
L'application YouTupe en mode portrait  

YouTube app landscape layout
Application YouTube en mode paysage

(Désolé pour le hasard des photos, mais ce sont les premières photos que j'ai pu trouver de la mise en page réelle)

C'est assez facile à faire normalement - il suffit de spécifier une mise en page alternative dans layoutland et tout ira bien. Ce que l’application YouTube fait vraiment bien (et ce que j’essaie de reproduire), c’est que lors du changement d’orientation, la vidéo continue de jouer et n’a pas besoin de re-tamponner dès le début.

J'ai découvert que le fait de remplacer onConfigurationChange () et de définir de nouveaux LayoutParameters me permettrait de redimensionner la vidéo sans forcer le rebuffeur. Toutefois, la vidéo sera redimensionnée de manière aléatoire en différentes largeurs/hauteurs lorsque vous faites pivoter l'écran plusieurs fois. J'ai essayé de faire toutes sortes d'appels invalidate () sur VideoView, d'appeler RequestLayout () sur le conteneur parent RelativeLayout et d'essayer autant de choses différentes que possible, mais je n'arrive pas à le faire fonctionner correctement. Tout avis serait grandement apprécié!

Voici mon code:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        questionText.setVisibility(View.GONE);
        respond.setVisibility(View.GONE);
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    } else {
        questionText.setVisibility(View.VISIBLE);
        respond.setVisibility(View.VISIBLE);
        Resources r = getResources();
        int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150.0f, r.getDisplayMetrics());
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, height));
    }
}

EDIT: J'ai découvert dans logcat quelques sorties intéressantes apparaissant lors de la rotation de ma vidéo, ce qui semble être le coupable - bien que je ne sache pas comment résoudre ce problème:

La sortie Logcat lors du redimensionnement correct (occupe toute la fenêtre)  

remarquez le h = 726

12-13 15:37:35.468  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=210}
12-13 15:37:35.561  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Adjusted Position/X1/Y0/W403/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Rotation/90
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=402 h=726
12-13 15:37:35.561  1262  1268 I Overlay : dumping driver state:
12-13 15:37:35.561  1262  1268 I Overlay : output pixfmt:
12-13 15:37:35.561  1262  1268 I Overlay : w: 432
12-13 15:37:35.561  1262  1268 I Overlay : h: 240
12-13 15:37:35.561  1262  1268 I Overlay : color: 7
12-13 15:37:35.561  1262  1268 I Overlay : UYVY
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:37:35.561  1262  1268 I Overlay : window l: 1 
12-13 15:37:35.561  1262  1268 I Overlay : window t: 0 
12-13 15:37:35.561  1262  1268 I Overlay : window w: 402 
12-13 15:37:35.561  1262  1268 I Overlay : window h: 726

Sortie Logcat en cas de redimensionnement incorrect (occupe une infime partie de l'écran)  

remarquez le h = 480

12-13 15:43:00.085  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=216}
12-13 15:43:00.171  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Adjusted Position/X138/Y0/W266/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Rotation/90
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=266 h=480
12-13 15:43:00.179  1262  1268 I Overlay : dumping driver state:
12-13 15:43:00.179  1262  1268 I Overlay : output pixfmt:
12-13 15:43:00.179  1262  1268 I Overlay : w: 432
12-13 15:43:00.179  1262  1268 I Overlay : h: 240
12-13 15:43:00.179  1262  1268 I Overlay : color: 7
12-13 15:43:00.179  1262  1268 I Overlay : UYVY
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:43:00.179  1262  1268 I Overlay : window l: 138 
12-13 15:43:00.179  1262  1268 I Overlay : window t: 0 
12-13 15:43:00.179  1262  1268 I Overlay : window w: 266 
12-13 15:43:00.179  1262  1268 I Overlay : window h: 480

Peut-être que quelqu'un sait ce qu'est la superposition et pourquoi elle n'obtient pas la valeur de hauteur correcte?

50
Mark G.

EDIT: (juin 2016)

Cette réponse est très ancienne (je pense sous Android 2.2/2.3) et n’est probablement pas aussi pertinente que les autres réponses ci-dessous! Regardez-les en premier, à moins que vous ne développiez l'ancienne version d'Android :)


J'ai pu limiter le problème à la fonction onMeasure de la classe VideoView. En créant une classe enfant et en remplaçant la fonction onMeasure, j'ai pu obtenir la fonctionnalité souhaitée.

public class VideoViewCustom extends VideoView {

    private int mForceHeight = 0;
    private int mForceWidth = 0;
    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setDimensions(int w, int h) {
        this.mForceHeight = h;
        this.mForceWidth = w;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("@@@@", "onMeasure");

        setMeasuredDimension(mForceWidth, mForceHeight);
    }
}

Ensuite, dans mon activité, je viens de faire ce qui suit:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        questionVideo.setDimensions(displayHeight, displayWidth);
        questionVideo.getHolder().setFixedSize(displayHeight, displayWidth);

    } else {
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        questionVideo.setDimensions(displayWidth, smallHeight);
        questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

    }
}

La ligne: 

questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

est la clé pour que cela fonctionne. Si vous effectuez l'appel setDimensions sans ce type, la vidéo ne sera toujours pas redimensionnée.

La seule autre chose à faire est de vous assurer que vous appelez setDimensions () également dans la méthode onCreate (), sinon votre vidéo ne commencera pas à être mise en mémoire tampon, car la vidéo ne sera pas configurée pour dessiner sur une surface de taille quelconque.

// onCreate()
questionVideo.setDimensions(initialWidth, initialHeight); 

Une dernière clé partie - si vous vous demandez pourquoi le VideoView n’est pas redimensionné en rotation, vous devez vous assurer que les dimensions que vous redimensionnez sont soit exactement égales à la zone visible ou moins. que ça. J'avais un très gros problème en réglant la largeur/hauteur du VideoView sur toute la taille de l'affichage alors que j'avais toujours la barre de notification/barre de titre à l'écran et que le redimensionnement de VideoView n'était pas du tout. Le simple fait de supprimer la barre de notification et la barre de titre a résolu le problème.

Espérons que cela aide quelqu'un dans le futur!

57
Mark G.

Tout d’abord, merci beaucoup pour votre propre réponse détaillée.

J'ai eu le même problème, la vidéo serait la plupart du temps plus petite, plus grande ou déformée dans VideoView après une rotation.

J'ai essayé votre solution, mais j'ai aussi essayé des choses au hasard, et par hasard, j'ai remarqué que si mon VideoView est centré sur son parent, il fonctionne comme par magie (pas besoin de VideoView personnalisé ou autre).

Pour être plus précis, avec cette mise en page, je reproduis le problème la plupart du temps:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" >

    <VideoView
        Android:id="@+id/videoView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</RelativeLayout>

Avec cette mise en page, je n'ai jamais le problème (en plus, la vidéo est centrée, c'est comme ça que ça devrait être de toute façon;):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" >

    <VideoView
        Android:id="@+id/videoView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_centerInParent="true" />

</RelativeLayout>

Cela fonctionne aussi avec wrap_content au lieu de match_parent (la vidéo occupe tout l'espace), ce qui n'a pas beaucoup de sens pour moi.

Quoi qu'il en soit, je n'ai aucune explication à cela. Cela ressemble à un bogue de VideoView.

23
BoD

Voici un moyen très simple d'accomplir ce que vous voulez avec un code minimal:

AndroidManifest.xml:

Android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

Remarque: Modifiez si nécessaire pour votre API, cela couvre 10+, mais les API inférieures nécessitent la suppression de la partie "screenSize | screenLayout | uiMode" de cette ligne.

Dans la méthode "OnCreate", généralement sous "super.onCreate", ajoutez:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

Et puis quelque part, généralement en bas, ajoutez:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

Cela redimensionnera la vidéo en plein écran chaque fois que l'orientation aura changé sans interrompre la lecture et nécessitera uniquement le remplacement de la méthode de configuration.

22
Abandoned Cart

VideoView utilise ce qu'on appelle une superposition, c'est-à-dire une zone dans laquelle la vidéo est rendue. Cette incrustation se trouve derrière la fenêtre contenant le VideoView. VideoView perce un trou dans sa fenêtre pour que la superposition soit visible. Il reste ensuite synchronisé avec la présentation (par exemple, si vous déplacez ou redimensionnez VideoView, la superposition doit également être déplacée et redimensionnée).

Il y a un bogue quelque part pendant la phase de mise en page qui fait que la superposition utilise la taille précédente définie par VideoView.

Pour résoudre ce problème, placez VideoView dans la sous-classe et remplacez onLayout:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    getHolder().setSizeFromLayout();
}

et la superposition aura la taille appropriée par rapport aux dimensions de la disposition de VideoView.

11
Zapek

Réplication de l'application YouTube

J'ai réussi à construire un exemple de projet qui ne nécessite pasAndroid:configChanges="orientation" ni une VideoView personnalisée. L’expérience obtenue est identique à la manière dont l’application YouTube gère la rotation lors de la lecture de vidéos. En d'autres termes, la vidéo non doit être suspendue, re-tamponnée ou rechargée, et ne saute ni ne supprime aucune trame audio lorsque l'orientation du périphérique change. 

Méthode optimale

Cette méthode utilise un TextureView et sa SurfaceTexture qui l'accompagne comme un récepteur pour l'image vidéo actuelle de la MediaPlayer. Puisqu'une SurfaceTexture utilise un objet de texture GL (simplement référencé par un entier du contexte GL), croyez qu'il est correct de conserver une référence à une SurfaceTexture lors de modifications de la configuration. Le TextureView lui-même est détruit et recréé lors de la modification de la configuration (avec l'activité de sauvegarde) et le nouveau TextView créé est simplement mis à jour avec la référence SurfaceTexture avant d'être attaché.

J'ai créé un exemple de travail complet montrant comment et quand initialiser votre MediaPlayer et un éventuel MediaController, et je soulignerai ci-dessous les parties intéressantes pertinentes pour cette question:

public class VideoFragment {

    TextureView mDisplay;
    SurfaceTexture mTexture;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        mDisplay = (TextureView) rootView.findViewById(R.id.texture_view);
        if (mTexture != null) {
            mDisplay.setSurfaceTexture(mTexture);
        }
        mDisplay.setSurfaceTextureListener(mTextureListener);

        return rootView;
    }

    TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
            // Initialize your media now or flag that the SurfaceTexture is available..
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            mTexture = surface;
            return false; // this says you are still using the SurfaceTexture..
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            mTexture = surface;
        }
    };

    @Override
    public void onDestroyView() {
        mDisplay = null;
        super.onDestroyView();
    }

    // ...

}

Étant donné que la solution utilise un fragment conservé plutôt qu'une activité qui gère manuellement les modifications de configuration, vous pouvez exploiter pleinement le système de gonflage des ressources spécifique à la configuration comme vous le feriez naturellement. Malheureusement, si votre sdk minimum est inférieur à l'API 16, vous devrez essentiellement sauvegarder TextureView (ce que je n'ai pas encore fait).

Enfin, si vous êtes intéressé, vérifiez ma question initiale détaillant: mon approche originale, la pile multimédia Android, pourquoi cela ne fonctionnait pas et la solution de rechange.

7
dcow

utilisez ceci : 

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

            getActivity().getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getActivity().getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

            getActivity().getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
            getActivity().getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);

        }
    }

N'oubliez pas également d'ajouter la ligne ci-dessous à votre activité dans le manifeste:

Android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
3
Rudi

voir mon exemple de code, ça marche pour moi

public class CustomVideoView extends Android.widget.VideoView {
private int width;
private int height;
private Context context;
private VideoSizeChangeListener listener;
private boolean isFullscreen;

public CustomVideoView(Context context) {
    super(context);
    init(context);
}

public CustomVideoView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

/**
 * get video screen width and height for calculate size
 *
 * @param context Context
 */
private void init(Context context) {
    this.context = context;
    setScreenSize();
}

/**
 * calculate real screen size
 */
private void setScreenSize() {
    Display display = ((Activity) context).getWindowManager().getDefaultDisplay();

    if (Build.VERSION.SDK_INT >= 17) {
        //new pleasant way to get real metrics
        DisplayMetrics realMetrics = new DisplayMetrics();
        display.getRealMetrics(realMetrics);
        width = realMetrics.widthPixels;
        height = realMetrics.heightPixels;

    } else if (Build.VERSION.SDK_INT >= 14) {
        //reflection for this weird in-between time
        try {
            Method mGetRawH = Display.class.getMethod("getRawHeight");
            Method mGetRawW = Display.class.getMethod("getRawWidth");
            width = (Integer) mGetRawW.invoke(display);
            height = (Integer) mGetRawH.invoke(display);
        } catch (Exception e) {
            //this may not be 100% accurate, but it's all we've got
            width = display.getWidth();
            height = display.getHeight();
        }

    } else {
        //This should be close, as lower API devices should not have window navigation bars
        width = display.getWidth();
        height = display.getHeight();
    }

    // when landscape w > h, swap it
    if (width > height) {
        int temp = width;
        width = height;
        height = temp;
    }
}

/**
 * set video size change listener
 *
 */
public void setVideoSizeChangeListener(VideoSizeChangeListener listener) {
    this.listener = listener;
}

public interface VideoSizeChangeListener {
    /**
     * when landscape
     */
    void onFullScreen();

    /**
     * when portrait
     */
    void onNormalSize();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // full screen when landscape
        setSize(height, width);
        if (listener != null) listener.onFullScreen();
        isFullscreen = true;
    } else {
        // height = width * 9/16
        setSize(width, width * 9 / 16);
        if (listener != null) listener.onNormalSize();
        isFullscreen = false;
    }
}

/**
 * @return true: fullscreen
 */
public boolean isFullscreen() {
    return isFullscreen;
}

/**
 * set video sie
 *
 * @param w Width
 * @param h Height
 */
private void setSize(int w, int h) {
    setMeasuredDimension(w, h);
    getHolder().setFixedSize(w, h);
}
}

et ne recréez pas la vue lorsque vous modifiez l'orientation

// AndroidManifest.xml
Android:configChanges="screenSize|orientation|keyboardHidden"

portrain

paysagelandscape

mon code source ici

2
vuhung3990

J'ai pris des exemples dans certaines des réponses ici et j'ai essayé de le faire à ma façon. On dirait une solution facile.

videoLayout = (RelativeLayout) videoView.findViewById(R.id.videoFrame);

onConfigurationChange dans un fragment.Où la vidéo est affichée en plein écran en mode paysage.A noter que je cache la barre d'action.

@Override
public void onConfigurationChanged(Configuration newConfig) {

    int          height     = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200.0f,getResources().getDisplayMetrics());
    ActionBar    actionBar  = weatherActivity.getSupportActionBar();
    LayoutParams params     = videoLayout.getLayoutParams();

    if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
    {
        if(actionBar.isShowing())
            actionBar.hide();


        params.width  = ViewGroup.LayoutParams.MATCH_PARENT;
        params.height = ViewGroup.LayoutParams.MATCH_PARENT;

        videoLayout.requestLayout();
    }
    else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
    {
        if(!actionBar.isShowing())
            actionBar.show();

        params.width  = ViewGroup.LayoutParams.MATCH_PARENT;
        params.height = height;

        videoLayout.requestLayout();
    }

    super.onConfigurationChanged(newConfig);
}   

et voici mon fichier de mise en page

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" >

<RelativeLayout 
    Android:id="@+id/videoFrame"
    Android:layout_height="200dp"
    Android:layout_width="match_parent">

    <VideoView
        Android:id="@+id/video"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_centerHorizontal="true"/>

</RelativeLayout>

J'ai un autre délai relatif sous ce qui n'est pas dans cette mise en page. Mais cela ne ferait aucune différence.

2
Prakash

Bien que Mark37 (très utile) answer fonctionne, il faut définir les dimensions manuellement (à l'aide de setDimensions). Cela peut convenir dans une application où les dimensions souhaitées sont connues à l'avance, mais si vous souhaitez que les vues déterminent la taille automatiquement en fonction des paramètres de la vidéo (par exemple, pour vous assurer que le rapport de format d'origine est conservé), une approche différente est nécessaire. .

Heureusement, la partie setDimensions n'est pas vraiment nécessaire. OnMeasure de VideoView inclut déjà toute la logique nécessaire. Ainsi, au lieu de compter sur quelqu'un pour appeler setDimensions, il est possible d'appeler super.onMeasure, puis d'utiliser la méthode getMeasuredWidth/getMeasuredHeight de la vue pour obtenir la taille fixe.

Ainsi, la classe VideoViewCustom devient simplement:

public class VideoViewCustom extends VideoView {

    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

Cette version ne nécessite aucun code supplémentaire dans l'appelant et tire pleinement parti de la mise en œuvre onMeasure existante de VideoView.

Ceci est en fait assez similaire à l'approche suggérée par Zapek, qui fait fondamentalement la même chose, uniquement avec onLayout au lieu de onMeasure.

1
Tomty

J'ai essayé d'utiliser le code d'autres réponses, mais cela n'a pas résolu mon problème, car si je pivotais rapidement l'écran, la taille de ma vidéo était augmentée. J'utilise donc en partie leur code, que je mets dans un fil qui met à jour la fenêtre très rapidement. Donc, voici mon code:

  new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            while(true)
            {
                try
                {
                    Thread.sleep(1);
                }
                catch(InterruptedException e){}
                handler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                WindowManager.LayoutParams.FLAG_FULLSCREEN);
                    }
                });
            }
        }
    }).start();

De cette façon, la taille de la vidéo sera toujours exacte (dans mon cas).

0
Curio

Réponse SIMPLE et à jour 2018.

Points positifs pour cette réponse: Code en bas

1) Peu importe que vous définissiez sur FULL_SCREEN ou non. Comme cela correspond au parent, cela fonctionnera toujours. Dans l'ancienne réponse acceptée, si vous ne définissez pas FULL_SCREEN, Android utilisera toute la taille du téléphone, puis VideoView sera en dehors de l'écran.

2) Il n’est pas nécessaire de créer un VideoView ou un PlayerView personnalisé;

3) Il n'appelle pas la méthode plusieurs fois. Dans l'ancienne réponse acceptée, il appelle onMeasure plusieurs fois inutile; 

4) Il y a aussi un bouton pour changer d'orientation comme YouTube app;

5) Régler la rotation automatique du téléphone sur OFF bloque également le changement d'orientation de l'application.

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    ...
    setPlayerViewLayoutParams();
    ...
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        setPlayerViewLayoutParamsForLandScape();
    } else {
        setPlayerViewLayoutParamsForPortrait();
    }
}

private void setPlayerViewLayoutParamsForLandScape() {
    ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT);
    mPlayerView.setLayoutParams(lp);
}

private void setPlayerViewLayoutParamsForPortrait() {
    Display display = myContext.getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int width = size.x;
    Double doubleHeight = width / 1.5;
    Integer height = doubleHeight.intValue();

    ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT, height);
    mPlayerView.setLayoutParams(lp);
}

private void setPlayerViewLayoutParams() {
    if (myContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        setPlayerViewLayoutParamsForLandScape();
    } else {
        setPlayerViewLayoutParamsForPortrait();
    }
}

OBS: J'utilise PlayerView dans ConstraintLayout. Parce que je travaille avec Médiathèque ExoPlayer .

fragment_player.xml

<Android.support.constraint.ConstraintLayout   
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
xmlns:app="http://schemas.Android.com/apk/res-auto">

<com.google.Android.exoplayer2.ui.PlayerView
    Android:id="@+id/player_view"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/colorBlack"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintVertical_bias="0.0"/>

</Android.support.constraint.ConstraintLayout>

ADDITIONNEL

Je veux aussi un bouton pour changer l'orientation

Pour reproduire entièrement l'application youtube, vous souhaiterez probablement un bouton permettant de définir le lecteur en mode paysage ou portrait, ce qui est utile si l'utilisateur utilise la rotation automatique du téléphone .

Tout d'abord, vous devrez suivre ces étapes:

  1. Créez le bouton qui changera l'orientation;
  2. Lorsque l'utilisateur clique sur le bouton, vérifiez l'orientation actuelle.
  3. Définissez une orientation opposée, par exemple, si elle est actuelle dans LandScape, définissez sur Portrait.

PlayerFragment.Java

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.image_button_full_screen:
            if (myContext.getResources().getConfiguration().orientation 
                == Configuration.ORIENTATION_LANDSCAPE) {
                myContext.setRequestedOrientation(
                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
            } else {
                myContext.setRequestedOrientation(
                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
            }
            break;
    }
}

PROBLÈME 1: la rotation de l'appareil ne fonctionne plus

Vous remarquerez que si vous cliquez sur le bouton pour modifier l'orientation, faire pivoter l'appareil ne fonctionnera plus pour modifier la taille du lecteur. Cela est dû au fait que vous définissez l'orientation par programme sur LandScape ou Portrait par programme et qu'elle le restera pour toujours. Vous avez donc besoin d'un moyen de définir l'orientation sur CAPTEUR. Ainsi, lorsque vous faites pivoter le périphérique, il vérifie l'orientation réelle. La méthode ci-dessous le fera pour vous.

PROBLÈME 2: la rotation automatique du téléphone est désactivée et il change toujours d'orientation 

Dans la méthode ci-dessous, vous devez vérifier si la rotation automatique du téléphone est activée ou désactivée. Dans le cas contraire, vous pouvez définir l'ORIENTATION sur Capteur pour que le téléphone puisse changer d'orientation lorsque l'utilisateur fait pivoter l'appareil. Si la rotation automatique est désactivée, vous ne faites rien. Ainsi, l'orientation sera bloquée dans Portrait ou dans LandScape, selon que l'utilisateur a appuyé sur le bouton plein écran ou non. 

private void setOrientationListener() {
    OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
        @Override
        public void onOrientationChanged(int orientation) {
            // 94 > 90 - 10 and 94 < 90 + 10     //   80 < orientation < 100.  Rotating to the LEFT.
            // 278 > 270 - 10 and 278 < 270 + 10 //   260 < orientation < 280. Rotating to the RIGHT.
            int epsilon = 10;
            int leftLandscape = 90;
            int rightLandscape = 270;
            if (epsilonCheck(orientation, leftLandscape, epsilon) ||
                    epsilonCheck(orientation, rightLandscape, epsilon)) {
                if (Android.provider.Settings.System.getInt(getContentResolver(),
                        Settings.System.ACCELEROMETER_ROTATION, 0) == 1) {
                    // Phone's auto rotation is ON
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
                }
            }
        }

        private boolean epsilonCheck(int a, int b, int epsilon) {
            return a > b - epsilon && a < b + epsilon;
        }
    };
    orientationEventListener.enable();
}

Si vous travaillez avec une application multimédia, j'ai un échantillon qui vous guidera sur la manière d'utiliser puissamment Exoplayer Library.

0
Soon Santos