Android多媒體開發系列文章
TextureView+MediaPlayer實現在線短視頻播放
列表item佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_bg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@mipmap/beautiful"
android:visibility="visible"/>
<com.jackchan.videoplayer.view.VideoPlayer
android:id="@+id/videoPlayer"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="visible"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/iv_author"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/qq_allshare_normal"/>
<TextView
android:id="@+id/tv_author_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="飛行的鋼蛋兒"
android:textColor="#000"/>
<TextView
android:id="@+id/tv_play_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="109萬次播放"
android:textColor="#8b8787"/>
<ImageView
android:id="@+id/iv_comment"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="2dp"
android:background="@drawable/comment_video_normal"/>
<TextView
android:id="@+id/tv_comment_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="3940"
android:textColor="#8b8787"/>
<ImageView
android:id="@+id/iv_comment_more"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/more_pgc_comment_normal_night"/>
</LinearLayout>
</LinearLayout>
其中VideoPlayer是自定義View,繼承於RelativeLayout,是視頻播放的關鍵類
VideoPlayer
public VideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//初始化佈局
private void initView() {
View view = View.inflate(getContext(), R.layout.video_play, this);
ButterKnife.bind(this,view);
...
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--視頻播放界面-->
<TextureView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="visible"/>
<!--視頻播放控制界面-->
<com.jackchan.videoplayer.view.VideoMediaController
android:id="@+id/mediaController"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
實現視頻播放功能的是TextureView,VideoMediaController視頻播放控制器,自定義View,繼承於RelativeLayout
視頻播放(視頻的初始化)
private void play(String url){
try {
mPlayer = MediaHelper.getInstance();
mPlayer.reset();
mPlayer.setDataSource(url);
//讓MediaPlayer和TextureView進行視頻畫面的結合
mPlayer.setSurface(mSurface);
//設置監聽
mPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener);
mPlayer.setOnCompletionListener(onCompletionListener);
mPlayer.setOnErrorListener(onErrorListener);
mPlayer.setOnPreparedListener(onPreparedListener);
mPlayer.setScreenOnWhilePlaying(true);//在視頻播放的時候保持屏幕的高亮
//異步準備
mPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
準備完成監聽
private MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//隱藏視頻加載進度條
mediaController.setPbLoadingVisiable(View.GONE);
//進行視頻的播放
MediaHelper.play();
hasPlay = true;
//隱藏標題
mediaController.delayHideTitle();
//設置視頻的總時長
mediaController.setDuration(mPlayer.getDuration());
//更新播放的時間和進度
mediaController.updatePlayTimeAndProgress();
}
};
進行TextureView控件創建的監聽
videoView.setSurfaceTextureListener(surfaceTextureListener);
private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
//創建完成 TextureView纔可以進行視頻畫面的顯示
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// Log.i(TAG,"onSurfaceTextureAvailable");
mSurface = new Surface(surface);//連接對象(MediaPlayer和TextureView)
play(info.url);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Log.i(TAG,"onSurfaceTextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// Log.i(TAG,"onSurfaceTextureDestroyed");
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Log.i(TAG,"onSurfaceTextureUpdated");
}
};
VideoMediaController
佈局文件如下
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
>
<!--視頻加載進度條-->
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
<RelativeLayout
android:id="@+id/rl_play_finish"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#b4000000"
android:visibility="visible">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:orientation="vertical"
>
<ImageView
android:id="@+id/iv_replay"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/replay_video"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:text="重播"
android:textColor="#fff"
android:textSize="14sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView
android:id="@+id/iv_share"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/share_video"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:text="分享"
android:textColor="#fff"
android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="@string/title"
android:textColor="#fff"
android:textSize="14sp"/>
<ImageView
android:id="@+id/iv_play"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/new_play_video"
android:visibility="visible"/>
<TextView
android:id="@+id/tv_all_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="10dp"
android:background="@drawable/tv_time_bg"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:text="02:30"
android:textColor="#fff"
android:textSize="10sp"
android:visibility="visible"/>
<LinearLayout
android:id="@+id/ll_play_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#ca000000"
android:gravity="center_vertical"
android:padding="10dp"
android:visibility="visible"
>
<TextView
android:id="@+id/tv_use_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="00:00"
android:textColor="#fff"
android:textSize="10sp"/>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:progressDrawable="@drawable/sb_progress_drawable"
android:thumb="@drawable/biz_video_progress_thumb"
/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="02:30"
android:textColor="#fff"
android:textSize="10sp"/>
<ImageView
android:id="@+id/iv_fullscreen"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/enlarge_fullscreen"/>
</LinearLayout>
</FrameLayout>
顯示或者隱藏視頻控制界面
private void showOrHideVideoController() {
if(llPlayControl.getVisibility() == View.GONE){
//顯示(標題、播放按鈕、視頻進度控制)
tvTitle.setVisibility(View.VISIBLE);
ivPlay.setVisibility(View.VISIBLE);
//加載動畫
Animation animation = AnimationUtils.loadAnimation(getContext(),R.anim.bottom_enter);
animation.setAnimationListener(new SimpleAnimationListener(){
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
llPlayControl.setVisibility(View.VISIBLE);
//過2秒後自動隱藏
mHandler.sendEmptyMessageDelayed(MSG_HIDE_CONTROLLER,2000);
}
});
//執行動畫
llPlayControl.startAnimation(animation);
}else{
//隱藏(標題、播放按鈕、視頻進度控制)
tvTitle.setVisibility(View.GONE);
ivPlay.setVisibility(View.GONE);
//加載動畫
Animation animation = AnimationUtils.loadAnimation(getContext(),R.anim.bottom_exit);
animation.setAnimationListener(new SimpleAnimationListener(){
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
llPlayControl.setVisibility(View.GONE);
}
});
//執行動畫
llPlayControl.startAnimation(animation);
}
}
更新播放的時間和進度
public void updatePlayTimeAndProgress() {
//獲取目前播放的進度
int currentPosition = MediaHelper.getInstance().getCurrentPosition();
//格式化
String useTime = formatDuration(currentPosition);
tvUseTime.setText(useTime);
//更新進度
int duration = MediaHelper.getInstance().getDuration();
if(duration == 0){
return;
}
int progress = 100*currentPosition/duration;
seekBar.setProgress(progress);
//發送一個更新的延時消息
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME_PROGRESS,500);
}
自定義SeekBar
<SeekBar
android:id="@+id/seekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:progressDrawable="@drawable/sb_progress_drawable"
android:thumb="@drawable/biz_video_progress_thumb"
/>
sb_progress_drawable
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background"
android:drawable="@drawable/sb_bg"/>
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%">
<selector>
<item android:state_enabled="false">
<color android:color="@android:color/transparent"/>
</item>
<item android:drawable="@drawable/sb_second"/>
</selector>
</scale>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<selector>
<item android:state_enabled="false">
<color android:color="@android:color/transparent"/>
</item>
<item android:drawable="@drawable/sb_progress"/>
</selector>
</scale>
</item>
</layer-list>
列表的item滾動出屏幕時停止播放
private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
//進行滑動
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//獲取屏幕上顯示的第一個條目和最後一個條目的下標
int firstVisibleItemPosition = lm.findFirstVisibleItemPosition();
int lastVisibleItemPosition = lm.findLastVisibleItemPosition();
//獲取播放條目的下標
int currentPosition = adapter.currentPosition;
if((firstVisibleItemPosition > currentPosition || lastVisibleItemPosition < currentPosition) && currentPosition > -1){
//讓播放隱藏的條目停止
MediaHelper.release();
adapter.currentPosition = -1;
adapter.notifyDataSetChanged();
}
}
};