Android视频播放 的几种方式
在Android中,在做视频播放的时候,我们可以直接使用Android原生的VideoView来实现,也可以使用SurfaceView+MediaPlayer来实现,本文主要针对这两种方式进行实现。
一.VideoView实现
主要代码有:
设置VideoView的url和MediaController,然后调用start()方法,即可播放视频
videoView.setMediaController(new MediaController(this));videoView.setVideoPath(videoInfo.getFilePath());videoView.start();
可以看到非常简单,只需要短短的三行代码,就可以实现本地视频和网络视频的播放。
当然VideoView还提供一些控制视频播放的方法
如
pause() //让视频暂停
start() //播放开始播放
stop() //停止播放
以及一些监听方法
//缓冲进度的监听
//缓冲进度的监听videoView.setOnPreparedListener(new MyPlayOnPreparedListener());//播放完成回调videoView.setOnCompletionListener( new MyPlayerOnCompletionListener());class MyPlayerOnCompletionListener implements MediaPlayer.OnCompletionListener { @Override public void onCompletion(MediaPlayer mp) { Toast.makeText( VideoViewActivity.this, "播放完成了", Toast.LENGTH_SHORT).show(); } } class MyPlayOnPreparedListener implements MediaPlayer.OnPreparedListener { @Override public void onPrepared(MediaPlayer mediaPlayer) { } }
使用VideoView播放视频如果视频的分辨率小于设备的屏幕分辨率,VideoVIew在播放视频的时候都是在左上角显示的,比较影响美观,解决办法也很简单,只需要在VideoView的外层嵌套一个相对布局同时设置VideoView的layout_centerInParent=”true”就可以了。
<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <VideoView android:id="@+id/videoview_view" android:layout_centerInParent="true" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" /> RelativeLayout>
效果如下:
首先是获取了本地的视频列表,左滑的话可以选择网络视频进行播放,只是加载网络视频需要费点时间,就不演示了,点击后用VideoView进行播放,可以看到VideoView本身提供了进度条,暂停,快进等功能,对于视频要求不是太大的情况下可以选择使用这种方式,使用起来比较简单
下面是完整的代码,
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private Button btVideo; private Button btSurface; private Button btTexture; private TextView tvTitle; private ViewPager viewPager; private TabLayout tableLayout; private String[] attr=new String[]{"本地视频","网络视频"}; private List fragments; private FileFragment fileFragment; private NetFragment netFragment; private ViewPagerAdapter adapter; public static int flag=0; public static final int VIDEOVIEW_FLAG=0; public static final int SURFACEVIEW_FLAG=1; public static final int TEXTUREVIEW_FLAG=2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initData() { fragments=new ArrayList<>(); fileFragment=new FileFragment(); netFragment=new NetFragment(); fragments.add(fileFragment); fragments.add(netFragment); tableLayout.addTab(tableLayout.newTab().setText(attr[0])); tableLayout.addTab(tableLayout.newTab().setText(attr[1])); adapter=new ViewPagerAdapter(getSupportFragmentManager(),this,attr,fragments); viewPager.setAdapter(adapter); tableLayout.setupWithViewPager(viewPager); } private void initView() { btVideo= (Button) findViewById(R.id.main_video); btVideo.setOnClickListener(this); btSurface= (Button) findViewById(R.id.main_surface); btSurface.setOnClickListener(this); btTexture= (Button) findViewById(R.id.main_texture); btTexture.setOnClickListener(this); tvTitle= (TextView) findViewById(R.id.main_title); viewPager= (ViewPager) findViewById(R.id.main_viewpager); tableLayout= (TabLayout) findViewById(R.id.main_tab); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.main_video: Toast.makeText(this,"切换到VideoVIew",Toast.LENGTH_SHORT).show(); flag=VIDEOVIEW_FLAG; break; case R.id.main_surface: Toast.makeText(this,"切换到SurfaceView",Toast.LENGTH_SHORT).show(); flag=SURFACEVIEW_FLAG; break; case R.id.main_texture: Toast.makeText(this,"切换到TextureView",Toast.LENGTH_SHORT).show(); flag=TEXTUREVIEW_FLAG; break; } }}
MainActivity就是TabLayout+ViewPager 切换本地和网络视频,下面三个按钮是切换播放器。
public class FileFragment extends Fragment { private List mData; private VideoInfoAdapter adapter; private RecyclerView recyclerView; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.fm_file,container,false); initView(view); initData(); return view; } private void initView(View view) { recyclerView= view.findViewById(R.id.main_recycler); } private void initData() { mData=new ArrayList<>(); String[] attr=new String[]{ MediaStore.MediaColumns.DATA, BaseColumns._ID, MediaStore.MediaColumns.TITLE, MediaStore.MediaColumns.MIME_TYPE, MediaStore.Video.VideoColumns.DURATION, MediaStore.MediaColumns.SIZE }; Cursor cursor=getActivity().getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,attr, null,null,null); if (cursor!=null){ while (cursor.moveToNext()){ VideoInfo info=new VideoInfo(); info.setFilePath(cursor.getString(cursor .getColumnIndexOrThrow(MediaStore.MediaColumns.DATA))); info.setMimeType(cursor.getString(cursor .getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE))); info.setTitle(cursor.getString(cursor .getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE))); info.setTime(CommTools.LongToHms(cursor.getInt(cursor .getColumnIndexOrThrow(MediaStore.Video.VideoColumns.DURATION)))); info.setSize(CommTools.LongToPoint(cursor .getLong(cursor .getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)))); int id = cursor.getInt(cursor .getColumnIndexOrThrow(BaseColumns._ID)); BitmapFactory.Options options = new BitmapFactory.Options(); options.inDither = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; info.setB(MediaStore.Video.Thumbnails.getThumbnail(getActivity().getContentResolver(), id, MediaStore.Images.Thumbnails.MICRO_KIND, options)); mData.add(info); } } adapter=new VideoInfoAdapter(getActivity(),mData); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); adapter.setOnItemClickListener(new VideoInfoAdapter.OnItemClickListener() { @Override public void onItemClick(VideoInfo videoInfo,int position) { Intent intent=new Intent(); intent.putExtra("VIDEO_INFO",videoInfo); intent.putExtra("VIDEO_SORT",position+"/"+mData.size()); intent.putExtra("VIDEO_TYPE",0); switch (MainActivity.flag){ case MainActivity.VIDEOVIEW_FLAG: intent.setClass(getActivity(), VideoViewActivity.class); startActivity(intent); break; case MainActivity.SURFACEVIEW_FLAG: intent.setClass(getActivity(), SurfaceActivity.class); startActivity(intent); break; case MainActivity.TEXTUREVIEW_FLAG: break; } } }); }}
主要是获取本地的视频列表集合,然后用一个RecyclerView显示。
二.SurfaceView+MediaPlayer
虽然使用ViedoView比较简单,但是如果遇上比较复杂的布局效果,自定义程度较高的话,就要用到SurfaceView+MediaPlayer这种了,使用这种方式进行视频播放可以更加灵活,使用SurfaceView进行显示,MediaPlayer和SurfaceView进行绑定,就可以显示出完整的视频了。
关于SurfaceView,从android 1.0就有了,一般来说,UI对刷新都需要在UI线程中完成,但是,surfaceview可以在非UI线程中完成刷新。拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行绘制。又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。
大神的链接,感兴趣的可以去了解一下
Android视图SurfaceView的实现原理分析
怎么使用,首先我们先设置需要播放的资源,
可以使文件、文件路径、或者URL。
mediaPlayer.setDataSource(url);
然后设置SurfaceHolder,需要先创建SurfaceHolder,可以通过surfaceView.getHolder()取得,
holder=surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mediaPlayer.setDisplay(holder); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } });
同时监听surfaceHolder,在surfaceholder被创建的时候,与MediaPlayer进行绑定
然后调用MediaPlayer.prepare()来准备。
mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(final MediaPlayer mediaPlayer) { /**装载完成,开始播放*/ mediaPlayer.start(); } }
一般我们选择用异步的方式进行装载,然后在onPrePared方法里调用开始播放
下面是全部的代码,对SurfaceView进行了简单的封装,包括暂停,开始,播放进度,设置播放地址,全屏和半屏切换,状态栏的显示和隐藏等,
下面是完整的代码:
public class MysurfaceView extends SurfaceView implements MediaPlayer.OnErrorListener ,MediaPlayer.OnCompletionListener ,MediaPlayer.OnVideoSizeChangedListener ,SurfaceHolder.Callback{ private static final String TAG=MysurfaceView.class.getSimpleName(); private MediaPlayer mediaPlayer; private SurfaceHolder holder; /**视频播放的Url*/ private String url; /**播放状态*/ private boolean isPlay; /**横竖屏标识*/ private boolean screenDirection=true; /**视频的宽高*/ private float videoHeight; private float videoWidth; /**系统屏幕的宽高*/ private float systemWidth; private float systemHeight; /**控件的宽高*/ private float surWidth; private float surHeight; private Context context; public MysurfaceView(Context context) { super(context); init(context); } public MysurfaceView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MysurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public interface OnVideoPlayingListener{ void onVideoSizeChanged(int vWidth, int vHeight); void onPlaying(int duration, int percent); void onStart(); void onPlayOver(); void onVideoSize(int videoSize); } private OnVideoPlayingListener listener; public void setOnVideoPlayingListener(OnVideoPlayingListener listener){ this.listener=listener; } /**设置监听*/ private void initEvent() { /**注册当surfaceView创建、改变和销毁时应该执行的方法*/ holder.addCallback(this); /**播放出错时的监听*/ mediaPlayer.setOnErrorListener(this); /**播放结束时的监听*/ mediaPlayer.setOnCompletionListener(this); /**视频尺寸的监听*/ mediaPlayer.setOnVideoSizeChangedListener(this); } /**初始化*/ private void init(Context context) { this.context=context; mediaPlayer=new MediaPlayer(); holder=this.getHolder(); /** * 这里必须设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS哦,意思 * 是创建一个push的'surface',主要的特点就是不进行缓冲 */ holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); DisplayMetrics dm = new DisplayMetrics(); ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm); systemWidth = dm.widthPixels; systemHeight=dm.heightPixels; initEvent(); } /**设置全屏播放*/ public void setFullScreen(){ hideNavigationBar(); ((Activity)context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); scaleChangeSize(systemHeight,systemWidth); } /**恢复半屏播放*/ public void setHalfScreen(){ showNavigationBar(); ((Activity)context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); scaleChangeSize(surWidth,surHeight); } /**显示状态栏*/ private void showNavigationBar(){ View decorView =((Activity)context). getWindow().getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } /**隐藏状态栏*/ public void hideNavigationBar() { int uiFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN; // hide status bar ((Activity)context).getWindow().getDecorView().setSystemUiVisibility(uiFlags); } /**设置视频播放路径*/ public void setUrl(String url){ this.url=url; } /**暂停播放和继续播放*/ public void pause() { if (mediaPlayer!=null){ if (mediaPlayer.isPlaying()&&isPlay==true){ mediaPlayer.pause(); }else { mediaPlayer.start(); } } } /**停止播放*/ public void stop(){ mediaPlayer.stop(); } /**指定位置播放*/ public void seekTo(int progress){ if (mediaPlayer != null && mediaPlayer.isPlaying()) { /**设置当前播放的位置*/ mediaPlayer.seekTo(progress); } } /**销毁 回收资源*/ public void finishVideo(){ mediaPlayer.stop(); mediaPlayer.release(); } /**等比例缩放视频*/ public void scaleChangeSize(float width,float height){ float xsca=width/videoWidth; float ysca=height/videoHeight; float r=min(xsca,ysca); float w=videoWidth*r; float h=videoHeight*r; ViewGroup.LayoutParams params= getLayoutParams(); params.width= (int) w; params.height= (int) h; setLayoutParams(params); } /**开始播放*/ public void play(){ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); try { mediaPlayer.setDataSource(url); /**异步装载*/ mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(final MediaPlayer mediaPlayer) { /**等比例缩放视频尺寸*/ videoWidth=mediaPlayer.getVideoWidth(); videoHeight=mediaPlayer.getVideoHeight(); surWidth=getWidth(); surHeight=getHeight(); scaleChangeSize(surWidth,surHeight); mediaPlayer.start(); listener.onVideoSize(mediaPlayer.getDuration()); new Thread() { @Override public void run() { try { isPlay = true; while (isPlay) { int current = mediaPlayer.getCurrentPosition(); Message message=Message.obtain(); message.what=1; message.obj=current; handler.sendMessage(message); sleep(500); } } catch (Exception e) { e.printStackTrace(); } } }.start(); isPlay=true; listener.onStart(); } }); } catch (IOException e) { e.printStackTrace(); } } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { if (listener != null ) { listener.onPlaying(mediaPlayer.getDuration(), (Integer) msg.obj); sendEmptyMessageDelayed(0, 1000); } } } }; /**播放结束的监听*/ @Override public void onCompletion(MediaPlayer mediaPlayer) { isPlay=false; listener.onPlayOver(); } /**播放错误的监听*/ @Override public boolean onError(MediaPlayer mediaPlayer, int i, int i1) { isPlay=false; return false; } /**视频尺寸的监听*/ @Override public void onVideoSizeChanged(MediaPlayer mediaPlayer, int i, int i1) { int videoW=mediaPlayer.getVideoWidth(); int videoH=mediaPlayer.getVideoHeight(); if (listener!=null){ listener.onVideoSizeChanged(videoW,videoH); } } /**SurfaceHolder被创建*/ @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mediaPlayer.setDisplay(holder); } /**SurfaceHolder被改变*/ @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {} /**SurfaceHolder被销毁*/ @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) {}}
上面的注释都十分的详细,应该都可以看懂,对视频拉伸也做了处理,下面是Activity的代码:
public class TestSurfaceActivity extends AppCompatActivity implements View.OnClickListener{ private Intent intent; private VideoInfo videoInfo; private ImageView imgPlay; private ImageView imgBack; private SeekBar seekBar; private TextView tvTotalTime; private TextView tvPlayTime; private ImageView ivAll; private MysurfaceView mysurfaceView; private boolean isFull; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_surface); initView(); initData(); initEvent(); } private void initEvent() { mysurfaceView.setOnVideoPlayingListener(new MysurfaceView.OnVideoPlayingListener() { @Override public void onVideoSizeChanged(int vWidth, int vHeight) { } @Override public void onPlaying(int duration, int percent) { Log.i("surface","播放进度"+"总时长"+duration+" 当前播放进度"+percent); seekBar.setMax(duration); seekBar.setProgress(percent); tvPlayTime.setText(CommTools.LongToHms(percent)); } @Override public void onStart() { Toast.makeText(TestSurfaceActivity.this,"开始播放",Toast.LENGTH_SHORT).show(); } @Override public void onPlayOver() { finish(); } /**播放总时长*/ @Override public void onVideoSize(int videoSize) { tvTotalTime.setText(CommTools.LongToHms(videoSize)); seekBar.setMax(videoSize); } }); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { /**当进度条停止修改的时候触发*/ /**取得当前进度条的刻度*/ int progress = seekBar.getProgress(); /**设置当前播放的位置*/ mysurfaceView.seekTo(progress); tvPlayTime.setText(""+CommTools.LongToHms(progress)); } }); } private void initData() { intent=getIntent(); videoInfo=intent.getParcelableExtra("VIDEO_INFO"); mysurfaceView.setUrl(videoInfo.getFilePath()); } private void initView() { imgBack= (ImageView) findViewById(R.id.test_sur_iv_back); imgBack.setOnClickListener(this); imgPlay= (ImageView) findViewById(R.id.test_sur_iv_play); imgPlay.setOnClickListener(this); ivAll= (ImageView) findViewById(R.id.test_sur_iv_full); ivAll.setOnClickListener(this); seekBar= (SeekBar) findViewById(R.id.test_sur_seekbar); tvTotalTime= (TextView) findViewById(R.id.test_sur_tv_total_time); tvPlayTime= (TextView) findViewById(R.id.test_sur_tv_start_time); mysurfaceView= (MysurfaceView) findViewById(R.id.test_sur_view); } @Override protected void onResume() { super.onResume(); mysurfaceView.setUrl(videoInfo.getFilePath()); mysurfaceView.play(); } @Override public void finish() { super.finish(); mysurfaceView.finishVideo(); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.test_sur_iv_back: finish(); break; case R.id.test_sur_iv_play: mysurfaceView.pause(); break; case R.id.test_sur_iv_full: isFull(); break; } } public void isFull(){ if (isFull){ mysurfaceView.setHalfScreen(); isFull=false; }else { mysurfaceView.setFullScreen(); isFull=true; } }}
主要是对当前的播放时间和进度,进行实时监听,同时还有全屏切换的功能,
主要功能就是这些的,
下面是效果图
因为是模拟器,所以切横竖屏的时候屏幕会变成横向,演示的效果不会太好,
下面是完整的Demo:
http://download.csdn.net/download/tzl0322/9941551
有分的支持一下,没分的可以到我的GitHub上下载:
https://github.com/ZhiLiangT/AndroidVideo
更多相关文章
- vlc android 截图和录制视频(vlc0.9.9)
- 基于FFmpeg的音视频播放器
- Android入门教程 MediaPlayer教程【使用进度条】
- Android的WebView如何播放视频
- android studio 60音乐播放器 下载音乐
- Android(安卓)关闭后台音频播放
- Android(安卓)Tcp服务器端
- Android之SurfaceView窗口/全屏播放
- 垂直滚动公告栏