分享

用SurfaceView和MediaPlayer做一个Android视频播放器

 Dragon_chen 2017-04-27
用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(浏览器的播放视频小控件还有底部描述控件,重新封装个布局,就能达到那样的效果)就行了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多