我正在尝试复制Android市场中最新YouTube应用的功能。在观看视频时,有两个独立的布局,一个是纵向提供额外的信息,一个是横向,提供视频的全屏视图。
YouTupe应用程序处于纵向模式
横向模式的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
也许有人知道'叠加'是什么以及为什么它没有得到正确的身高值?
答案 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)
我设法构建了一个不需要 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"
景观
答案 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应用,您可能需要一个按钮将播放器设置为横向或纵向,如果用户关闭了手机的自动旋转功能,此功能将非常有用。
首先,您需要执行以下步骤:
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。