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<Fragment> 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<VideoInfo> 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

發佈了20 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章