如何去除Android中VideoView播放视频时的黑边区域?
问题描述
我在VideoView中加载教程视频播放,但视频无法填满整个VideoView,因为视频本身的比例和VideoView的比例不匹配,导致VideoView出现黑边。我试过用CardView包裹VideoView并设置cardCornerRadius,但没法彻底消除黑边,而且也不确定能不能给VideoView设置背景圆角。

VideoFragment.kt
private fun initVideo() { simpleVideoView = binding.videoViewTutorial simpleVideoView?.setVideoURI( Uri.parse("android.resource://" + requireActivity().packageName + "/" + R.raw.tutorial) ) simpleVideoView?.requestFocus() simpleVideoView?.start() }
fragment_video.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:tools="http://schemas.android.com/tools"> <androidx.fragment.app.FragmentContainerView android:id="@+id/tabContainerUser" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="0dp" android:layout_height="70dp" android:layout_gravity="bottom" android:layout_margin="30dp" android:background="@drawable/background_white_gray_stroke_60dp_radius" app:itemIconSize="27dp" app:labelVisibilityMode="unlabeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/bottom_nav_menu_user" /> <ImageView android:id="@+id/ivAskAdvise" android:layout_width="50dp" android:layout_height="50dp" android:translationZ="999dp" android:src="@drawable/ic_black_plus_yellow_bg" app:layout_constraintBottom_toBottomOf="@+id/bottomNavigationView" app:layout_constraintEnd_toEndOf="@+id/bottomNavigationView" app:layout_constraintStart_toStartOf="@+id/bottomNavigationView" app:layout_constraintTop_toTopOf="@+id/bottomNavigationView" app:layout_constraintVertical_bias="0.63" tools:ignore="ContentDescription" /> <View android:id="@+id/viewBlackBg" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black_0_70" android:visibility="gone" android:translationZ="999dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> <!-- <androidx.cardview.widget.CardView android:id="@+id/cardViewVideo" android:layout_width="0dp" android:layout_height="0dp" app:cardCornerRadius="70dp" app:cardBackgroundColor="@color/transparent" android:visibility="gone" android:translationZ="999dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.80" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_percent="0.80" tools:visibility="visible">--> <VideoView android:id="@+id/videoViewTutorial" android:layout_width="0dp" android:layout_height="0dp" android:translationZ="999dp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.80" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_percent="0.80" tools:visibility="visible"/> <!--</androidx.cardview.widget.CardView>--> <TextView android:id="@+id/tvSkipTutorial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:fontFamily="@font/mulish_regular" android:text="@string/skip" android:textColor="@color/white" android:textSize="16sp" android:visibility="gone" android:translationZ="999dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/videoViewTutorial" tools:visibility="visible" /> </androidx.constraintlayout.widget.ConstraintLayout>
解决方案
一、消除黑边:适配视频比例
黑边本质是视频和VideoView的比例不匹配导致的,VideoView默认会保留视频原始比例,所以会留出黑边。可以通过以下两种方式解决:
1. 自定义ScalingVideoView
写一个继承自VideoView的类,重写onMeasure方法,让视频按比例填充整个View(超出部分会被裁剪):
class ScalingVideoView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : VideoView(context, attrs, defStyleAttr) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val videoWidth = videoWidth val videoHeight = videoHeight if (videoWidth != 0 && videoHeight != 0) { val videoRatio = videoWidth.toFloat() / videoHeight.toFloat() val viewWidth = MeasureSpec.getSize(widthMeasureSpec) val viewHeight = MeasureSpec.getSize(heightMeasureSpec) val viewRatio = viewWidth.toFloat() / viewHeight.toFloat() var measuredWidth = viewWidth var measuredHeight = viewHeight // 根据比例计算适配后的尺寸 if (videoRatio > viewRatio) { // 视频更宽,按宽度适配,高度超出部分裁剪 measuredHeight = (viewWidth / videoRatio).toInt() } else { // 视频更高,按高度适配,宽度超出部分裁剪 measuredWidth = (viewHeight * videoRatio).toInt() } setMeasuredDimension(measuredWidth, measuredHeight) } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec) } } }
然后把xml里的VideoView替换成这个自定义类:
<your.package.name.ScalingVideoView android:id="@+id/videoViewTutorial" android:layout_width="0dp" android:layout_height="0dp" android:translationZ="999dp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.80" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_percent="0.80" tools:visibility="visible"/>
2. 使用MediaPlayer+SurfaceView(更灵活)
如果自定义VideoView满足不了需求,用SurfaceView配合MediaPlayer可以更精细地控制视频显示:
先在xml里把VideoView换成SurfaceView:
<SurfaceView android:id="@+id/surfaceViewTutorial" android:layout_width="0dp" android:layout_height="0dp" android:translationZ="999dp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.80" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_percent="0.80" tools:visibility="visible"/>
然后在代码里初始化MediaPlayer并适配比例:
private lateinit var surfaceView: SurfaceView private lateinit var mediaPlayer: MediaPlayer private fun initVideo() { surfaceView = binding.surfaceViewTutorial mediaPlayer = MediaPlayer() // 设置视频源 mediaPlayer.setDataSource(requireContext(), Uri.parse("android.resource://" + requireActivity().packageName + "/" + R.raw.tutorial)) surfaceView.holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { mediaPlayer.setDisplay(holder) mediaPlayer.prepareAsync() mediaPlayer.setOnPreparedListener { mp -> val videoWidth = mp.videoWidth val videoHeight = mp.videoHeight val surfaceWidth = surfaceView.width val surfaceHeight = surfaceView.height val videoRatio = videoWidth.toFloat() / videoHeight.toFloat() val surfaceRatio = surfaceWidth.toFloat() / surfaceHeight.toFloat() val layoutParams = surfaceView.layoutParams as ConstraintLayout.LayoutParams // 计算适配后的尺寸 if (videoRatio > surfaceRatio) { layoutParams.width = surfaceWidth layoutParams.height = (surfaceWidth / videoRatio).toInt() } else { layoutParams.height = surfaceHeight layoutParams.width = (surfaceHeight * videoRatio).toInt() } surfaceView.layoutParams = layoutParams mp.start() } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {} override fun surfaceDestroyed(holder: SurfaceHolder) { // 释放资源 mediaPlayer.release() } }) }
二、实现VideoView圆角效果
VideoView本身不支持直接设置圆角,但是可以通过以下两种方法实现:
1. 正确使用CardView包裹
你之前的CardView用法有问题,需要开启裁剪并关闭默认的内边距:
恢复CardView的包裹,添加以下属性:
<androidx.cardview.widget.CardView android:id="@+id/cardViewVideo" android:layout_width="0dp" android:layout_height="0dp" app:cardCornerRadius="70dp" app:cardBackgroundColor="@color/transparent" app:cardPreventCornerOverlap="false" <!-- 关闭默认的圆角重叠修正 --> app:cardUseCompatPadding="false" <!-- 关闭默认内边距 --> android:clipToOutline="true" <!-- 开启裁剪 --> android:visibility="gone" android:translationZ="999dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.80" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_percent="0.80" tools:visibility="visible"> <your.package.name.ScalingVideoView android:id="@+id/videoViewTutorial" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/transparent" <!-- 设置背景透明 --> android:translationZ="999dp" android:visibility="visible"/> </androidx.cardview.widget.CardView>
2. 使用OutlineProvider(API>=21)
给VideoView设置自定义的OutlineProvider,手动创建圆角轮廓:
在代码中添加:
simpleVideoView?.clipToOutline = true simpleVideoView?.outlineProvider = object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { // 第一个参数是左偏移,第二个上偏移,第三个右边界,第四个下边界,最后一个是圆角半径 outline.setRoundRect(0, 0, view.width, view.height, 70f) } }
注意:这种方法要求系统版本在Android 5.0以上,并且VideoView的父布局不能有多余padding,否则圆角可能显示不全。
内容的提问来源于stack exchange,提问作者Ahmet Yılmaz
相关产品推荐
相关产品推荐

