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;
        }
    }
}

主要是对当前的播放时间和进度,进行实时监听,同时还有全屏切换的功能,
主要功能就是这些的,
下面是效果图
Android视频播放 的几种方式_第1张图片

因为是模拟器,所以切横竖屏的时候屏幕会变成横向,演示的效果不会太好,
下面是完整的Demo:
http://download.csdn.net/download/tzl0322/9941551
有分的支持一下,没分的可以到我的GitHub上下载:
https://github.com/ZhiLiangT/AndroidVideo

你可能感兴趣的:(android视频播放器)