使用缓冲视频更改Android VideoView方向

时间:2010-12-13 22:16:28

标签: android android-orientation android-videoview

我正在尝试复制Android市场中最新YouTube应用的功能。在观看视频时,有两个独立的布局,一个是纵向提供额外的信息,一个是横向,提供视频的全屏视图。

YouTube app portrait layout
YouTupe应用程序处于纵向模式

YouTube app landscape layout
横向模式的YouTube应用

(抱歉照片的随机性,但它们是我能找到的实际布局的第一张照片)

通常很容易做到 - 只需在layout-land中指定一个替代布局,一切都会很好。 YouTube应用确实做得很好(以及我想要复制的内容)是在方向更改时,视频会继续播放,而不必从头开始重新缓冲。

我已经发现重写onConfigurationChange()并设置新的LayoutParameters将允许我在不强制拒绝的情况下调整视频大小 - 但是当多次旋转屏幕时,视频会随机缩放到不同的宽度/高度。我已尝试在VideoView上进行各种invalidate()调用,尝试在父RelativeLayout容器上调用RequestLayout()并尝试尽可能多的不同内容,但我似乎无法让它正常工作。任何建议将不胜感激!

这是我的代码:

@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));
    }
}
编辑:我在logcat中发现了一些有趣的输出,当我的视频被轮换时出现这似乎是罪魁祸首 - 虽然我不知道如何解决它:

正确调整大小时占用Logcat(占用整个窗口)

注意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

调整大小错误时的Logcat输出(占据全屏的一小部分)

注意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

也许有人知道'叠加'是什么以及为什么它没有得到正确的身高值?

11 个答案:

答案 0 :(得分:57)

编辑:( 2016年6月)

这个答案非常陈旧(我认为android 2.2 / 2.3)并且可能与下面的其他答案没有相关性!除非您正在开发旧版Android,否则请先了解它们。)


我能够将问题缩小到VideoView类中的onMeasure功能。通过创建子类并覆盖onMeasure函数,我能够获得所需的功能。

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

然后在我的活动中,我刚刚做了以下事情:

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

    }
}

该行:

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

是使这项工作成功的关键。如果你在没有这个人的情况下进行setDimensions调用,视频仍然不会调整大小。

您唯一需要做的就是确保在onCreate()方法中调用setDimensions()或者您的视频不会开始缓冲,因为视频不会设置为在任何大小的表面上绘制

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

最后一个部分 - 如果您发现自己想知道为什么VideoView没有在旋转时调整大小,您需要确保要调整大小的尺寸完全等于可见区域或小于它。我有一个非常大的问题,当我在屏幕上仍然有通知栏/标题栏并且根本没有调整VideoView的大小时,我将VideoView的宽度/高度设置为整个显示大小。只需删除通知栏和标题栏即可解决问题。

希望这有助于将来的某个人!

答案 1 :(得分:23)

首先,非常感谢您自己的广泛答案。

我遇到了同样的问题,视频旋转后大部分时间内视频会变小或变大或变形。

我尝试了你的解决方案,但我也尝试过随机的东西,我偶然注意到如果我的VideoView以其父级为中心,它会神奇地自行工作(不需要自定义VideoView或其他任何东西)。

更具体地说,通过这种布局,我可以在大多数时间重现问题:

<?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>

使用这一个布局,我从来没有遇到过这个问题(另外,视频是居中的,无论如何都应该如此;):

<?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>

它也适用于wrap_content而不是match_parent(视频仍占用所有空间),这对我来说没有多大意义。

无论如何,我对此没有任何解释 - 这看起来像是一个VideoView错误。

答案 2 :(得分:23)

使用最少的代码,这是一种非常简单的方法来实现您的目标:

的AndroidManifest.xml:

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

注意:根据您的API需要进行编辑,这包含10+,但是较低的API需要删除此行的“screenSize | screenLayout | uiMode”部分

在“OnCreate”方法中,通常在“super.onCreate”下,添加:

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

然后在某处,通常在底部,添加:

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

只要方向发生变化而不中断播放,这会将视频大小调整为全屏,只需要覆盖配置方法。

答案 3 :(得分:11)

VideoView使用所谓的叠加层,即覆盖视频的区域。该叠加层位于持有VideoView的窗口后面。 VideoView在其窗口中打出一个洞,以便可以看到叠加层。然后它与布局保持同步(例如,如果你移动或调整VideoView的大小,叠加层也必须移动和调整大小)。

在布局阶段某处存在一个错误,使得叠加层使用VideoView设置的先前尺寸。

要修复它,请继承VideoView并覆盖onLayout:

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

并且叠加层的大小与VideoView的布局尺寸相符。

答案 4 :(得分:7)

复制YouTube应用

我设法构建了一个不需要 android:configChanges="orientation"或自定义VideoView的示例项目。由此产生的体验与YouTube应用在视频播放过程中处理轮换的方式相同。换句话说,视频不需要暂停,重新缓冲或重新加载,并且在设备方向改变时不会跳过或丢弃任何音频帧。

最佳方法

此方法使用TextureView及其随附的SurfaceTexture作为MediaPlayer当前视频帧的接收器。由于SurfaceTexture使用GL纹理对象(简单地通过GL上下文中的整数引用),因此相信通过配置更改保留对SurfaceTexture的引用是可以的。在配置更改期间(以及后备Activity),TextureView本身将被销毁并重新创建,并且在连接之前,只需使用SurfaceTexture引用更新新创建的TextureView。

我创建了一个full working example,展示了如何以及何时初始化您的MediaPlayer和可能的MediaController,我将在下面重点介绍与此问题相关的有趣部分:

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

    // ...

}

由于解决方案使用保留的Fragment而不是手动处理配置更改的Activity,因此您可以自然地充分利用特定于配置的资源充裕系统。不幸的是,如果你的最低sdk低于API 16,你基本上需要向后移植TextureView(我还没有)。

最后,如果您有兴趣,checkout my initial question详细说明:我的原始方法,Android媒体堆栈,它为什么不起作用,以及替代解决方法。

答案 5 :(得分:3)

使用它:

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

        }
    }

另外不要忘记将下面的行添加到Manifest中的Activity:

android:configChanges="orientation|keyboard|keyboardHidden|screenSize"

答案 6 :(得分:2)

我从这里的一些答案中抽取了例子并试图按照我的方式去做。看起来很简单。

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

片段内的onConfigurationChange。视频以横向模式全屏显示。注意我隐藏了操作栏。

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

这是我的布局文件

<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>

我还有另一个不在此布局中的相对布局。但这没有任何区别。

答案 7 :(得分:2)

查看我的示例代码,它适用于我

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

并且在更改方向时不要重新创建视图

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

portrain

景观 landscape

my source code here

答案 8 :(得分:1)

虽然Mark37(非常有用)answer确实有效,但它需要手动设置尺寸(使用setDimensions)。这可能适用于预先知道所需尺寸的应用,但如果您希望视图根据视频的参数自动确定尺寸(例如,为了确保保持原始宽高比),需要采用不同的方法。

幸运的是,事实证明setDimensions部分实际上并不是必需的。 VideoView的onMeasure已经包含了所有必要的逻辑,因此不必依赖某人来调用setDimensions,只需调用super.onMeasure即可,然后使用视图的getMeasuredWidth / getMeasuredHeight获得固定大小。

因此,VideoViewCustom类变得简单:

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

此版本不需要调用者中的任何其他代码,并充分利用VideoView现有的onMeasure实现。

这实际上与Zapek建议的approach非常相似,它基本上是相同的,只有onLayout而不是onMeasure。

答案 9 :(得分:0)

我尝试使用其他答案的代码,但它并没有解决我的问题,因为如果我快速旋转屏幕,我的视频大小就会受到影响。所以我部分使用了他们的代码,我把它放在一个快速更新窗口的线程中。 所以,这是我的代码:

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

通过这种方式,视频大小将始终正确(在我的情况下)。

答案 10 :(得分:0)

简单的答案,直到2018年为止。

此答案的重点:代码在下方

1)是否设置为FULL_SCREEN都没关系。由于它与父项匹配,因此它将始终有效。在旧的可接受答案中,如果您未设置为FULL_SCREEN,则android将使用整个手机的尺寸,然后VideoView将在屏幕之外;

2)您无需创建自定义VideoView或PlayerView;

3)它没有多次调用该方法。在旧的正确答案中,它不必要地调用几次。

4)还有一个按钮可以更改方向,例如youtube app;

5)将手机的自动旋转设置为关闭也会阻止应用程序方向更改。

@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:我在ConstraintLayout内使用PlayerView。因为我正在与ExoPlayer Media Library合作。

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>

附加

我还想要一个按钮来更改方向

要完全再现youtube应用,您可能需要一个按钮将播放器设置为横向或纵向,如果用户关闭了手机的自动旋转功能,此功能将非常有用。

首先,您需要执行以下步骤:

  1. 创建将更改方向的按钮;
  2. 用户单击按钮时,检查当前方向;
  3. 设置为相反的方向,例如,如果当前在LandScape中是当前方向,则设置为“纵向”。

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

问题1:旋转设备不再起作用

您会注意到,如果单击按钮更改方向,旋转设备将无法再更改播放器大小。发生这种情况是因为您已通过编程将方向设置为LandScape或Portrait,并且它将一直保持这种状态,因此您需要一种将方向设置为SENSOR的方法,因此,在旋转设备时,它将检查实际的当前方向。下面的方法将为您做到这一点。

问题2:手机的自动旋转功能已关闭,并且仍会更改方向

在下面的方法中,您需要检查手机的自动旋转是打开还是关闭,如果打开,您可以将ORIENTATION设置为SENSOR,以便用户旋转设备时手机可以更改方向。如果“自动旋转”设置为“关闭”,则您什么也不做,因此,在竖向或LandScape中,方向将受阻,具体取决于用户是否按下了全屏按钮。

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

如果您使用的是媒体应用程序,那么我有一个sample,它将指导您如何有效使用Exoplayer Library。