用AndroidAPI的VideoView也可以做一个视频播放器,但是太丑了。不能改SufraceView上的控件。所以选择用SurfaceView和MediaPlayer做一个Android视频播放器。 参考 浏览器中浏览视频的小控件。 思路:要做的功能:有暂停和全屏播放,移动seekBar,视频也跟着跳到指定位置。全屏切回来的时候必须保持视频的同步。这引用同一个MediaPlayer可以实现此功能。全屏SurfaceView销毁的时候不释放MediaPlayer的资源,全屏Activity怎么引用同一个MediaPlayer,不想直接引用MediaPlayer所在对象,可以通过公有静态MediaPlayer成员来直接引用非全屏MediaPlayer。 第一步;自定义一个继承FrameLayout的ViedoView,将SufraceView和MediaPlayer,其他在SurfaceView上的控件作为它的成员变量,好操作。代码如下。 public class VideoView extends FrameLayout implements View.OnClickListener,SeekBar.OnSeekBarChangeListener{ private SurfaceView surfaceView; private MediaPlayer mediaPlayer=null; private RelativeLayout pauseLayout; private TextView titleView; private ImageButton pauseButton; private SeekBar seekBar; private TextView progressDes; private TextView progressTotal; private ImageButton fullScreen; private Timer timer; private TimerTask timeTask; private boolean pauseLayoutIsHide=true; private Uri uri; private boolean isFullScreen=false; private Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if(msg.what==1) progressDes.setText((String) msg.obj); else if(msg.what==2) { pauseLayout.setVisibility(INVISIBLE); pauseLayoutIsHide = true; } return true; } });
public VideoView(Context context) { super(context); }
public VideoView(Context context, AttributeSet attrs) { super(context, attrs); }
public VideoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void initUI() {
surfaceView = (SurfaceView) findViewById(R.id.video_surface); pauseLayout = (RelativeLayout) findViewById(R.id.pause_layout); pauseButton = (ImageButton) findViewById(R.id.pause_btn); titleView = (TextView) findViewById(R.id.video_tv_title); seekBar = (SeekBar) findViewById(R.id.video_seekbar); progressDes = (TextView) findViewById(R.id.video_tv_progress); progressTotal = (TextView) findViewById(R.id.video_tv_progress_total); fullScreen = (ImageButton) findViewById(R.id.video_full_screen); pauseButton.setOnClickListener(this); surfaceView.setOnClickListener(this); fullScreen.setOnClickListener(this); seekBar.setOnSeekBarChangeListener(this); } private int autoHideCount=0; public void initData(String url,String title) { titleView.setText(title); uri = Uri.parse(url); SurfaceHolder surfaceHolder =surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(final SurfaceHolder holder) { MainActivity.log("创建"); try { if(mediaPlayer==null) { mediaPlayer = new MediaPlayer(); mediaPlayer.reset(); mediaPlayer.setDataSource(getContext(), uri); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.prepare(); }//设置mediaPlayer与当前surfaceView关联一起,这里是非全屏 mediaPlayer.setDisplay(holder);
} catch (IOException e) { e.printStackTrace(); } int max=mediaPlayer.getDuration(); progressTotal.setText(getTime(max)); seekBar.setMax(max); //定时器更新进度条 timer=new Timer();
timeTask=new TimerTask() {
@Override public void run() { if(mediaPlayer.isPlaying()) { int current = mediaPlayer.getCurrentPosition(); seekBar.setProgress(current); handler.obtainMessage(1, getTime(current)).sendToTarget(); }//自动隐藏布局隔4*500=两秒 if(!pauseLayoutIsHide) { if(autoHideCount>4) { handler.obtainMessage(2,null).sendToTarget(); } autoHideCount++; } else { autoHideCount=0; } } };
timer.schedule(timeTask, 0, 500);
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override public void surfaceDestroyed(SurfaceHolder holder) { //非全屏状态下销毁MediaPlayer,比如上下移动 if (mediaPlayer!=null&&mediaPlayer.isPlaying()&&!isFullScreen) { currentPosition=mediaPlayer.getCurrentPosition(); mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; timer.cancel(); timeTask.cancel(); timer=null; timeTask=null; } } });
} public void againInitData(MediaPlayer media) { mediaPlayer=media; initUI(); SurfaceHolder surfaceHolder =surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(final SurfaceHolder holder) { //mediaPlayer.reset(); //设置全屏sufrace mediaPlayer.setDisplay(holder); int max = mediaPlayer.getDuration(); progressTotal.setText(getTime(max)); seekBar.setMax(max); //定时器更新进度条 timer = new Timer();
timeTask = new TimerTask() { @Override public void run() { if (mediaPlayer.isPlaying()) { int current = mediaPlayer.getCurrentPosition(); seekBar.setProgress(current); handler.obtainMessage(1, getTime(current)).sendToTarget(); } if (!pauseLayoutIsHide) { if (autoHideCount > 4) { handler.obtainMessage(2, null).sendToTarget(); } autoHideCount++; } else { autoHideCount = 0; } } };
timer.schedule(timeTask, 0, 500); }
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override public void surfaceDestroyed(SurfaceHolder holder) { if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { currentPosition=mediaPlayer.getCurrentPosition(); //全屏和缩小屏共用一个MediaPlayer所以不能全屏surfaceview销毁时释放mediaplayer // mediaPlayer.stop(); // mediaPlayer.release(); mediaPlayer=null; timer.cancel(); timeTask.cancel(); timer=null; timeTask=null; } } }); } public String getTime(int millSeconds) { int seconds =millSeconds/1000; int minutes =seconds/60; int second = seconds%60; int hour = minutes/60; int minute =minutes%60; StringBuilder stringBuilder =new StringBuilder(); if(hour!=0) { if(hour<10) { stringBuilder.append("0"); } stringBuilder.append(hour); stringBuilder.append(":"); } else { stringBuilder.append("00:"); } if(minute!=0) { if(minute<10) { stringBuilder.append("0"); } stringBuilder.append(minute); stringBuilder.append(":"); } else { stringBuilder.append("00:"); } if(second!=0) { if(second<10) { stringBuilder.append("0"); } stringBuilder.append(second); } else { stringBuilder.append("00"); } return stringBuilder.toString(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.video_surface: if(pauseLayoutIsHide) { pauseLayout.setVisibility(VISIBLE); pauseLayoutIsHide=false; } else { pauseLayout.setVisibility(INVISIBLE); pauseLayoutIsHide=true; } break; case R.id.pause_btn: if(mediaPlayer.isPlaying()) mediaPlayer.pause(); else { mediaPlayer.start(); pauseLayout.setVisibility(INVISIBLE); pauseLayoutIsHide=false; } break; case R.id.video_full_screen: Intent intent = new Intent(MainActivity.activityContext, VideoActivity.class); VideoActivity.mediaPlayer = mediaPlayer; isFullScreen=true; MainActivity.activityContext.startActivity(intent); break;
} } private int currentPosition; @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { currentPosition = progress; }
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) { mediaPlayer.seekTo(currentPosition); }
}
第二步:定义和ViedoView对应的xml文件。注意给父布局添加descendatFocusability属性=blocksDescendants,因为SeekBar,ImageButton会抢焦点。从View的触摸事件分发这篇文章我们知道有焦点的View会先处理,从而消费了触摸事件。设置显示操作栏的触摸点击事件的时候注意不要给根布局设置,会被SurfaceView挡住的。应该给SurfaceView设置。如果给RelativeLayout设置,不可见之后,不会响应点击事件。 <com.example.administrator.news.View.VideoView xmlns:android="http://schemas./apk/res/android" android:id="@+id/video_video" android:paddingTop="10dp" android:paddingBottom="10dp" android:layout_width="match_parent" android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"> <SurfaceView android:layout_width="match_parent" android:id="@+id/video_surface" android:layout_height="match_parent" />
<RelativeLayout android:visibility="invisible" android:id="@+id/pause_layout" android:layout_width="match_parent" android:layout_height="wrap_content" >
<TextView android:textColor="@color/white" android:id="@+id/video_tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:maxLines="2" android:text="csdsafdasdfsadfsdf" />
<ImageButton android:id="@+id/pause_btn" android:src="@mipmap/ic_launcher_round" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" /> <LinearLayout android:gravity="center_vertical" android:layout_alignParentBottom="true" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_marginRight="5dp" android:textColor="@color/white" android:id="@+id/video_tv_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0:00" />
<SeekBar android:id="@+id/video_seekbar" android:layout_marginRight="5dp" android:minHeight="1.0dip" android:maxHeight="1.0dip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:max="100" android:progress="10" />
<TextView android:layout_marginRight="5dp" android:textColor="@color/white" android:id="@+id/video_tv_progress_total" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0:00" />
<ImageButton android:id="@+id/video_full_screen" android:layout_width="30dp" android:layout_height="30dp" android:src="@mipmap/ic_launcher" /> </LinearLayout> </RelativeLayout> </com.example.administrator.news.View.VideoView> 第三步:定义全屏Activity,记得注册。 public class VideoActivity extends AppCompatActivity{ private VideoView mVideoView; public static MediaPlayer mediaPlayer; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.videoview_layout); mVideoView = (VideoView) findViewById(R.id.video_video); mVideoView.setPadding(0,0,0,0); mVideoView.againInitData(mediaPlayer); } } 最后只要在RecycleView或ListView中排列显示VideoView(浏览器的播放视频小控件还有底部描述控件,重新封装个布局,就能达到那样的效果)就行了。
|