基于VideoView 可调节进度的电视端的简单视频播放器
Android 中VideoVIew的主要过程:
1.播放
2.播放器的播放监听(准备完成、播放信息、播放完成、播放出错)
3.进度条控制(快进、快退、暂停)
下面进行介绍:
一:播放
VideoView的中,设置setVideoPath()或者setVideoURI()则可以进行播放。
当成功载入视频后,会调用onPreparedListener().进行播放视频。
mVideoView.setVideoPath("//url");
mVideoView.setOnPreparedListener(new
MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//准备完成进行播放。
mp.start();
//监听播放完成
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
//播放完成,并停止更新播放进度条。
}
}
}
})
二:监听VideoView的播放情况。
mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mediaPlayer, int what, int i1) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
//显示加载动画
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
//取消加载动画
default:
break;
}
return false;
})
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
//提示播放错误
return false;
}
)
三:进度条的更新
1.注意: 让控制条监听按键,但不能return true去消费左右按键,因为如果消费掉,则不能监听到seekbar的左右快进/快退事件。
mSeekBar.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keycode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
//当左右键按下时,①显示快进/快退②控制进度条
switch (keycode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
mPlayingRewIv.setVisibility(View.VISIBLE);
//此处不return true 不进行消费 下面同里
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mPlayingFfIv.setVisibility(View.VISIBLE);
break;
//当按下中间键,暂停/继续播放
case KeyEvent.KEYCODE_DPAD_CENTER:
break;
default:
break;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
//当左右键抬起时,隐藏左右键提示。
switch (keycode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
mPlayingRewIv.setVisibility(View.GONE);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mPlayingFfIv.setVisibility(View.GONE);
break;
default:
break;
}
}
// 不进行return true
return false;
}
})
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
//监听seekbar的滑动事件
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//遥控器控制可以忽略此处
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//遥控器控制可以忽略此处
}
};)
主要代码如下:
playActivity.java
package com.example.playerproject;
import android.annotation.SuppressLint;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextClock;
import android.widget.TextView;
import android.widget.VideoView;
import com.example.playerproject.utils.DateUtils;
import com.github.ybq.android.spinkit.SpinKitView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class PlayActivity extends AppCompatActivity {
@BindView(R.id.video_view)
VideoView mVideoView;
@BindView(R.id.channel_name_tv)
TextView mChannelNameTv;
@BindView(R.id.current_time_tv)
TextClock mCurrentTimeTv;
@BindView(R.id.playing_ff_iv)
ImageView mPlayingFfIv;
@BindView(R.id.playing_rew_iv)
ImageView mPlayingRewIv;
@BindView(R.id.current_play_times_tv)
TextView mCurrentPlayTimesTv;
@BindView(R.id.seekbar)
SeekBar mSeekBar;
@BindView(R.id.videoTimes)
TextView mVideoTimesTv;
@BindView(R.id.pause_iv)
ImageView mPauseIv;
@BindView(R.id.controller_rl)
RelativeLayout mControllerRl;
@BindView(R.id.video_loading)
SpinKitView mVideoLoading;
private long videoTimes;
private static final int UPDATE_UI = 1;
private static final int SEEK_TO = 2;
private static final String TAG = "PlayActivity";
private TextView slideTimesTv;
private PopupWindow indicator;
private int mSeekBarLength = 1464;
private boolean isSeeking;
private int maxProgress;
private MediaPlayer.OnPreparedListener mOnPreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
Log.i(TAG, "onPrepared: ");
mp.start();
videoTimes = mp.getDuration();
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 1000);
if (videoTimes > 0) {
String totalTime = DateUtils.showTime(videoTimes / 1000);
mVideoTimesTv.setText(totalTime);
}
maxProgress = mp.getDuration();
mSeekBar.setMax(maxProgress);
//设置滑动完成监听
mp.setOnSeekCompleteListener(mOnSeekCompleteListener);
}
};
private MediaPlayer.OnInfoListener mOnInfoListener = new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mediaPlayer, int what, int i1) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
mVideoLoading.setVisibility(View.VISIBLE);
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
mVideoLoading.setVisibility(View.INVISIBLE);
default:
break;
}
return false;
}
};
private MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
return false;
}
};
private MediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(MediaPlayer mediaPlayer) {
mVideoView.start();
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 500);
}
};
private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mHandler.removeMessages(UPDATE_UI);
}
};
@SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
ButterKnife.bind(this);
mVideoView.setVideoPath("http://flv2.bn.netease.com/videolib3/1611/28/nNTov5571/SD/nNTov5571-mobile.mp4");
initVideoViewListener();
initSeekBar();
//http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
//http://gslb.miaopai.com/stream/oxX3t3Vm5XPHKUeTS-zbXA__.mp4
//http://flv2.bn.netease.com/videolib3/1611/28/nNTov5571/SD/nNTov5571-mobile.mp4
initIndicator();
}
private void initIndicator() {
View popView = LayoutInflater.from(this).inflate(R.layout.popwindow_indicator, null);
indicator = new PopupWindow(popView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, false);
slideTimesTv = popView.findViewById(R.id.seek_time);
}
private View.OnKeyListener mOnKeyListener = new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keycode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
isSeeking = true;
mHandler.removeCallbacksAndMessages(null);
switch (keycode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
mPlayingRewIv.setVisibility(View.VISIBLE);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mPlayingFfIv.setVisibility(View.VISIBLE);
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
if (mVideoView.isPlaying()) {
showPauseIv();
mVideoView.pause();
}
break;
default:
break;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
isSeeking = false;
mHandler.sendEmptyMessage(SEEK_TO);
switch (keycode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
mPlayingRewIv.setVisibility(View.GONE);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mPlayingFfIv.setVisibility(View.GONE);
break;
default:
break;
}
}
return false;
}
};
private void hidePauseIv() {
for (int i = 0; i < mControllerRl.getChildCount(); i++) {
mControllerRl.getChildAt(i).setVisibility(View.VISIBLE);
}
mVideoLoading.setVisibility(View.GONE);
mPauseIv.setVisibility(View.GONE);
mPlayingFfIv.setVisibility(View.GONE);
mPlayingRewIv.setVisibility(View.GONE);
mSeekBar.requestFocus();
}
private void showPauseIv() {
for (int i = 0; i < mControllerRl.getChildCount(); i++) {
mControllerRl.getChildAt(i).setVisibility(View.INVISIBLE);
}
mPauseIv.setVisibility(View.VISIBLE);
mPauseIv.requestFocus();
}
private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
Log.i(TAG, "onProgressChanged: " + progress);
int xoff2 = 0;
if (maxProgress != 0) {
xoff2 = progress * mSeekBarLength / maxProgress;
Log.i(TAG, "onxOff: " + xoff2);
}
if (isSeeking) {
indicator.showAtLocation(mSeekBar, Gravity.NO_GRAVITY, 228, 965);
indicator.update(228 + xoff2, 965, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
} else {
indicator.dismiss();
}
String mTimePass = DateUtils.showTime(seekBar.getProgress() / 1000);
slideTimesTv.setText(mTimePass);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.i(TAG, "onStartTrackingTouch: ");
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Log.i(TAG, "onStopTrackingTouch: ");
}
};
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI:
updateProgress();
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 1000);
break;
case SEEK_TO:
mVideoView.seekTo(mSeekBar.getProgress());
break;
default:
break;
}
}
};
@OnClick(R.id.pause_iv)
public void onViewClicked(View v) {
switch (v.getId()) {
case R.id.pause_iv:
hidePauseIv();
if (!mVideoView.isPlaying()) {
mVideoView.start();
}
break;
}
}
private void initSeekBar() {
mSeekBar.requestFocus();
mSeekBar.setOnKeyListener(mOnKeyListener);
mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
}
private void initVideoViewListener() {
mVideoView.setOnPreparedListener(mOnPreparedListener);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mVideoView.setOnInfoListener(mOnInfoListener);
}
mVideoView.setOnCompletionListener(mOnCompletionListener);
mVideoView.setOnErrorListener(mOnErrorListener);
}
private void updateProgress() {
mSeekBar.setProgress(mVideoView.getCurrentPosition());
mCurrentPlayTimesTv.setText(DateUtils.showTime(mVideoView.getCurrentPosition() / 1000));
}
}
activity_play.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/video_controller"/>
</android.support.constraint.ConstraintLayout>
video_controller.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/controller_rl"
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:paddingLeft="@dimen/d85"
android:paddingRight="@dimen/d86"
android:paddingTop="@dimen/d50">
<TextView
android:id="@+id/channel_name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="熊出没之探险记"
android:textColor="@color/color_write"
android:textSize="@dimen/d48"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/d204"
android:drawableLeft="@mipmap/playing_back"
android:text="返回键退出回看"
android:textColor="@color/color_write"
android:textSize="@dimen/d30"/>
<TextClock
android:id="@+id/current_time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:format24Hour="HH:mm:ss"
android:textColor="@color/color_write"
tools:targetApi="jelly_bean_mr1"/>
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/video_loading"
style="@style/SpinKitView.Circle"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:padding="10dp"
app:SpinKit_Color="#9e9e9e"/>
<ImageView
android:id="@+id/playing_ff_iv"
android:layout_width="@dimen/d150"
android:layout_height="@dimen/d150"
android:layout_centerInParent="true"
android:src="@mipmap/playing_ff"
android:visibility="gone"/>
<ImageView
android:id="@+id/playing_rew_iv"
android:layout_width="@dimen/d150"
android:layout_height="@dimen/d150"
android:layout_centerInParent="true"
android:src="@mipmap/playing_rew"
android:visibility="gone"/>
<TextView
android:id="@+id/current_play_times_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/d50"
android:text="00:00:00"
android:textColor="@color/color_write"
android:textSize="@dimen/d30"/>
<SeekBar
android:id="@+id/seekbar"
android:layout_width="@dimen/d1464"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/d67"
android:layout_marginLeft="@dimen/d8"
android:background="@null"
android:focusable="true"
android:maxHeight="@dimen/d2"
android:minHeight="@dimen/d2"
android:progressDrawable="@drawable/play_progress_style"
android:splitTrack="false"
android:thumb="@null"/>
<TextView
android:id="@+id/videoTimes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/d50"
android:text="00:00:00"
android:textColor="@color/color_write"
android:textSize="@dimen/d30"/>
<ImageView
android:id="@+id/pause_iv"
android:layout_width="@dimen/d100"
android:layout_height="@dimen/d100"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/d50"
android:focusable="true"
android:src="@mipmap/playing_pause"
android:visibility="gone"/>
</RelativeLayout>
popwindow_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/seek_time"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#FFC700"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00:00"
android:paddingLeft="@dimen/d2"
android:paddingRight="@dimen/d2">
</TextView>
DateUtils.java
public class DateUtils {
public static ArrayList<String> getEpgData() {
ArrayList<String> list = new ArrayList<>();
long now = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
list.clear();
int oneWeek = 7;
for (int i = oneWeek; i >= 0; i--) {
if (i == 0) {
continue;
}
Date date = new Date(now - i * 24 * 60 * 60 * 1000); //7 6 5 4 3 2 1
list.add(sdf.format(date));
}
for (int i = 0; i < oneWeek; ++i) { //now 1 2 3 4 5 6
Date date = new Date(now + i * 24 * 60 * 60 * 1000);
list.add(sdf.format(date));
}
return list;
}
/**
* 获取系统当前所在时区
* @return
*/
public static int getCurrentTimeZone() {
// TimeZone.setDefault(TimeZone.getTimeZone("GMT+4"));///设置系统所在时区
int timeZone = Calendar.getInstance().getTimeZone().getOffset(System.currentTimeMillis());
timeZone /= 3600000; // 确定时区
return timeZone;
}
public static String getMonth() {
String month = null;
Calendar calendar = Calendar.getInstance();
Log.e("getMonth", "calendar.get(Calendar.MONTH)=" + calendar.get(Calendar.DAY_OF_MONTH));
int m = calendar.get(Calendar.MONTH);
switch (m) {
case 0:
month = "January " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 1:
month = "February " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 2:
month = "March " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 3:
month = "April " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 4:
month = "May " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 5:
month = "June " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 6:
month = "July " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 7:
month = "August " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 8:
month = "September " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 9:
month = "October " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 10:
month = "November " + calendar.get(Calendar.DAY_OF_MONTH);
break;
case 11:
month = "December " + calendar.get(Calendar.DAY_OF_MONTH);
break;
default:
break;
}
return month;
}
/**
* 将长时间格式字符串转换为字符串 HH:mm:ss
*
* @param time
* @return String
*/
public static String showTime(long time) {
int minute = (int) time / 60;
int hour = minute / 60;
int second = (int) time % 60;
minute %= 60;
return String.format("%02d:%02d:%02d", hour, minute, second);
}
/**
*
* @param mss
* @return HH:mm
*/
public static String showHours(long mss) {
SimpleDateFormat sdformat = new SimpleDateFormat("HH:mm");//24小时制
return sdformat.format(mss);
}
/**
* 将时间戳转换为日期格式
*
* @param time
* @return
*/
public static String showDate(long time) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
String date = format.format(time);
return TextUtils.isEmpty(date) ? "" : date;
}
public static long getMillisecond(String data) {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
long time = System.currentTimeMillis();
try {
Date dateTime = format.parse(data);
time = dateTime.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return time;
}
public static ArrayList<String> getLastWeek() {
ArrayList<String> list = new ArrayList<>();
long now = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
list.clear();
int oneWeek = 7;
for (int i = oneWeek; i >= 0; i--) {
if (i == 0) {
continue;
}
//前七天 0 - 6
Date date = new Date(now - i * 24 * 60 * 60 * 1000);
list.add(sdf.format(date));
}
for (int i = 0; i < oneWeek; ++i) {
//后七天 7 - 13
Date date = new Date(now + i * 24 * 60 * 60 * 1000);
list.add(sdf.format(date));
}
return list;
}
}