<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants">
<com.qiangyu.test.commonvideoview.MyVideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
//底部状态栏
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#CC282828"
android:padding="3dip"
android:id="@+id/videoControllerLayout"
android:gravity="center"
android:layout_gravity="bottom">
<LinearLayout android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:id="@+id/videoPauseBtn"
android:paddingRight="10dip"
android:paddingLeft="10dp">
<ImageView android:layout_width="22dp"
android:layout_height="22dp"
android:id="@+id/videoPauseImg" />
</LinearLayout>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingRight="0dip">
<SeekBar android:layout_width="fill_parent"
android:id="@+id/videoSeekBar"
android:layout_weight="1"
style="@android:style/Widget.Holo.SeekBar"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:text="00:00"
android:textSize="12dp"
android:id="@+id/videoCurTime"
android:textColor="#FFF"
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="12dp"
android:textColor="#FFF"
android:text="/"/>
<TextView android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:text="00:00"
android:textSize="12dp"
android:id="@+id/videoTotalTime"
android:textColor="#FFF"
android:layout_marginRight="10dp"
</LinearLayout>
<LinearLayout
android:id="@+id/screen_status_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/screen_status_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/iconfont_enter_32"/>
</LinearLayout>
</LinearLayout>
//VideoVIEW中间的开始按钮和进度条以及快进快退的提示
<ProgressBar android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/progressBar"
style="@android:style/Widget.Holo.ProgressBar.Small"/>
<ImageView android:layout_width="30dip"
android:layout_height="30dip"
android:id="@+id/videoPlayImg"
android:layout_gravity="center"
android:src="@mipmap/video_box_play"/>
<LinearLayout
android:id="@+id/touch_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:visibility="invisible"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:background="#000">
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:id="@+id/touchStatusImg"/>
<TextView
android:id="@+id/touch_time"
android:layout_width="wrap_content"
android:text="25:00/59:00"
android:textSize="12sp"
android:textColor="#fff"
android:layout_height="wrap_content"/>
</LinearLayout>
</FrameLayout>
上面的布局很简单,VideoView用了自定义是因为当布局改变的时候,要让VideoView重新获取布局位置,在里面设置它的分辨率为全屏.VideoView的代码如下
public class MyVideoView extends VideoView {
public MyVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
public MyVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
public MyVideoView(Context context) {
super(context);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
this.getHolder().setFixedSize(width,height);
setMeasuredDimension(width, height);
好,布局写好了我来第二步,获取内部控件初始化事件
2、第二步,onFinishInflate()中得到内部的控件,做初始化工作
onFinishInflate方法在xml解析完毕的时候会回调该方法,一般在做组合控件的时候最常用
@Override
protected void onFinishInflate() {
super.onFinishInflate()
initView()
private void initView() {
View view = LayoutInflater.from(context).inflate(R.layout.common_video_view,null)
viewBox = (FrameLayout) view.findViewById(R.id.viewBox)
videoView = (MyVideoView) view.findViewById(R.id.videoView)
videoPauseBtn = (LinearLayout) view.findViewById(R.id.videoPauseBtn)
screenSwitchBtn = (LinearLayout) view.findViewById(R.id.screen_status_btn)
videoControllerLayout = (LinearLayout) view.findViewById(R.id.videoControllerLayout)
touchStatusView = (LinearLayout) view.findViewById(R.id.touch_view)
touchStatusImg = (ImageView) view.findViewById(R.id.touchStatusImg)
touchStatusTime = (TextView) view.findViewById(R.id.touch_time)
videoCurTimeText = (TextView) view.findViewById(R.id.videoCurTime)
videoTotalTimeText = (TextView) view.findViewById(R.id.videoTotalTime)
videoSeekBar = (SeekBar) view.findViewById(R.id.videoSeekBar)
videoPlayImg = (ImageView) view.findViewById(R.id.videoPlayImg)
videoPlayImg.setVisibility(GONE)
videoPauseImg = (ImageView) view.findViewById(R.id.videoPauseImg)
progressBar = (ProgressBar) view.findViewById(R.id.progressBar)
videoPauseBtn.setOnClickListener(this)
videoSeekBar.setOnSeekBarChangeListener(this)
videoPauseBtn.setOnClickListener(this)
videoView.setOnPreparedListener(this)
videoView.setOnCompletionListener(this)
screenSwitchBtn.setOnClickListener(this)
videoPlayImg.setOnClickListener(this)
//注册在设置或播放过程中发生错误时调用的回调函数。如果未指定回调函数,或回调函数返回false,VideoView 会通知用户发生了错误。
videoView.setOnErrorListener(this)
viewBox.setOnTouchListener(this)
viewBox.setOnClickListener(this)
addView(view)
很简单的做了代码初始化和videoView的播放事件的注册,这里的事件待会要处理的有viewBox.setOnTouchListener(this)注册的onTouch事件,我们要在里面写左右滑动快进快退的效果,videoSeekBar.setOnSeekBarChangeListener(this);处理拖动seekbar快进快退。
3、第三步,事件、效果处理
viewBox.setOnTouchListener(this);
1、onTouch里的实现快进快退
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if (!videoView.isPlaying()){
return false;
float downX = event.getRawX();
touchLastX = downX;
Log.d("FilmDetailActivity", "downX" + downX);
this.position = videoView.getCurrentPosition();
break;
case MotionEvent.ACTION_MOVE:
if (!videoView.isPlaying()){
return false;
float currentX = event.getRawX();
float deltaX = currentX - touchLastX;
float deltaXAbs = Math.abs(deltaX);
if (deltaXAbs>1){
if (touchStatusView.getVisibility()!=View.VISIBLE){
touchStatusView.setVisibility(View.VISIBLE);
touchLastX = currentX;
Log.d("FilmDetailActivity","deltaX"+deltaX);
if (deltaX > 1) {
position += touchStep;
if (position > duration) {
position = duration;
touchPosition = position;
touchStatusImg.setImageResource(R.mipmap.ic_fast_forward_white_24dp);
int[] time = getMinuteAndSecond(position);
touchStatusTime.setText(String.format("%02d:%02d/%s", time[0], time[1],formatTotalTime));
} else if (deltaX < -1) {
position -= touchStep;
if (position < 0) {
position = 0;
touchPosition = position;
touchStatusImg.setImageResource(R.mipmap.ic_fast_rewind_white_24dp);
int[] time = getMinuteAndSecond(position);
touchStatusTime.setText(String.format("%02d:%02d/%s", time[0], time[1],formatTotalTime));
break;
case MotionEvent.ACTION_UP:
if (touchPosition!=-1){
videoView.seekTo(touchPosition);
touchPosition = -1;
if (videoControllerShow){
return true;
break;
return false;
2、处理 seekBar的拖动事件
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int[] time = getMinuteAndSecond(progress);
videoCurTimeText.setText(String.format("%02d:%02d", time[0], time[1]));
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
videoView.pause();
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
videoView.seekTo(videoSeekBar.getProgress());
videoView.start();
videoPlayImg.setVisibility(View.INVISIBLE);
videoPauseImg.setImageResource(R.mipmap.icon_video_pause);
3、videoView的回调事件
@Override
public void onPrepared(MediaPlayer mp) {
duration = videoView.getDuration()
int[] time = getMinuteAndSecond(duration)
videoTotalTimeText.setText(String.format("%02d:%02d", time[0], time[1]))
formatTotalTime = String.format("%02d:%02d", time[0], time[1])
videoSeekBar.setMax(duration)
progressBar.setVisibility(View.GONE)
mp.start()
videoPauseBtn.setEnabled(true)
videoSeekBar.setEnabled(true)
videoPauseImg.setImageResource(R.mipmap.icon_video_pause)
timer.schedule(timerTask, 0, 1000)
//初始化总时间等一些界面显示,同时用timer定时去修改时间进度textView
@Override
public void onCompletion(MediaPlayer mp) {
videoView.seekTo(0)
videoSeekBar.setProgress(0)
videoPauseImg.setImageResource(R.mipmap.icon_video_play)
videoPlayImg.setVisibility(View.VISIBLE)
还有一些其它的点击时间就不放了,都是暂停,播放,点击显示隐藏底部状态栏,全屏切换等的事件。到了这里我们的事件处理完毕啦,接下来就要我们视频怎么播放呢?为了播放我们为外部提供一个方法
//开始播放
public void start(String url){
videoPauseBtn.setEnabled(false)
videoSeekBar.setEnabled(false)
videoView.setVideoURI(Uri.parse(url))
videoView.start()
//进入全屏时候调用
public void setFullScreen(){
touchStatusImg.setImageResource(R.mipmap.iconfont_exit)
this.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
videoView.requestLayout()
//退出全屏时候调用
public void setNormalScreen(){
touchStatusImg.setImageResource(R.mipmap.iconfont_enter_32)
this.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,400))
videoView.requestLayout()
上面提供的setFullScreen()和setNormalScreen()需要在Activity的 onConfigurationChanged(Configuration newConfig)横竖屏发生改变的 回调方法里面调用,还需要注意的是我这里写的是LinearLayout的LayoutParams,所以我们自定义的view的父空间要是LinearLayout,当然你也可以修改。
4、控件的使用
我们只需要在获得空间调用start方法,然后在onConfigurationChanged方法里调用setFullScreen和setNormalScreen就可以了,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.qiangyu.test.commonvideoview.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
<com.qiangyu.test.commonvideoview.CommonVideoView
android:id="@+id/common_videoView"
android:layout_width="match_parent"
android:layout_height="300dp" />
</LinearLayout>
activity代码
public class MainActivity extends AppCompatActivity {
CommonVideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
videoView = (CommonVideoView) findViewById(R.id.common_videoView);
videoView.start("你的服务器视频地址");
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
videoView.setFullScreen();
}else {
videoView.setNormalScreen();
最后为了防止你的Activity在横竖屏切换的时候重新创建别忘记在AndroidManifest.xml文件里面配置
android:configChanges=”orientation|screenSize|screenLayout”, 如果你这里有疑惑可以参考我的文章–>深入了解Activity-生命周期
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
好了,整个控件到这里就完成了,源码下载请戳它—>欢迎戳我
如果你觉得有对你有帮助,请动动手给个赞吧,有问题记得反馈给我哦!