Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 編寫簡單自定義Android VideoView

編寫簡單自定義Android VideoView

編輯:Android開發實例

簡單定制VideoView中做了簡單的VideoView定制,其實就是在布局上做了一些事情。要向更靈活的定制播放器的行為,必須寫自己的VideoView。參考android VideoView源代碼,寫了個最簡單的實現。

看起來和簡單定制VideoView中的效果差不多,但是還有很多邏輯沒有加進來,比如:

  • 視頻大小有問題,被拉長了,需要在後續版本中改進;
  • 還沒有加入MediaController,沒有前進、後退、暫停等按鈕界面。

 

自定義的VideoView源代碼:

package com.easymorse.videoplayer;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;

public class CustomerVideoView extends SurfaceView implements
        MediaPlayerControl {

    private static String TAG = "customer.videoplayer";

    private boolean pause;

    private boolean seekBackward;

    private boolean seekForward;

    private Uri videoUri;

    private MediaPlayer mediaPlayer;

    private Context context;

    private OnPreparedListener onPreparedListener;

    private int videoWidth;

    private int videoHeight;

    private MediaController mediaController;

    protected SurfaceHolder surfaceHolder;

    private Callback surfaceHolderCallback = new SurfaceHolder.Callback() {

        public void surfaceChanged(SurfaceHolder holder, int format, int w,
                int h) {
        }

        public void surfaceCreated(SurfaceHolder holder) {
            surfaceHolder = holder;
            if (mediaPlayer != null) {
                mediaPlayer.setDisplay(surfaceHolder);
                resume();
            } else {
                openVideo();
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            surfaceHolder = null;
            if (mediaController != null) {
                mediaController.hide();
            }

            release(true);
        }
    };

    private void release(boolean cleartargetstate) {
        if (mediaPlayer != null) {
            mediaPlayer.reset();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    public void resume() {
        if (surfaceHolder == null) {
            return;
        }
        if (mediaPlayer != null) {
            return;
        }
        openVideo();
    }

    public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        this.initVideoView();
    }

    public CustomerVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        this.initVideoView();
    }

    public CustomerVideoView(Context context) {
        super(context);
        this.context = context;
        this.initVideoView();
    }

    @Override
    public boolean canPause() {
        return this.pause;
    }

    @Override
    public boolean canSeekBackward() {
        return this.seekBackward;
    }

    @Override
    public boolean canSeekForward() {
        return this.seekForward;
    }

    @Override
    public int getBufferPercentage() {
        return 0;
    }

    @Override
    public int getCurrentPosition() {
        return mediaPlayer!=null?mediaPlayer.getCurrentPosition():0;
    }

    @Override
    public int getDuration() {
        return mediaPlayer!=null?mediaPlayer.getDuration():0;
    }

    @Override
    public boolean isPlaying() {
        return false;
    }

    @Override
    public void pause() {
    }

    @Override
    public void seekTo(int mSec) {
    }

    @Override
    public void start() {
    }

    public void setVideoURI(Uri uri) {
        this.videoUri = uri;
        openVideo();
        requestLayout();
        invalidate();
    }

    private void openVideo() {
        this.mediaPlayer = new MediaPlayer();
        try {
            this.mediaPlayer.setDataSource(this.context, this.videoUri);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            throw new RuntimeException(e);
        }
        this.mediaPlayer.prepareAsync();
        this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        this.mediaPlayer.setOnPreparedListener(onPreparedListener);
        attachMediaController();

    }

    private void attachMediaController() {
        if (mediaPlayer != null && mediaController != null) {
            mediaController.setMediaPlayer(this);
            View anchorView = this.getParent() instanceof View ? (View) this
                    .getParent() : this;
            mediaController.setAnchorView(anchorView);
            mediaController.setEnabled(true);
        }

    }

    public void setMediaController(MediaController controller) {
        if (mediaController != null) {
            mediaController.hide();
        }
        mediaController = controller;
        attachMediaController();
    }

    public void setOnPreparedListener(OnPreparedListener onPreparedListener) {
        this.onPreparedListener = onPreparedListener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultSize(videoWidth, widthMeasureSpec);
        int height = getDefaultSize(videoHeight, heightMeasureSpec);
        if (videoWidth > 0 && videoHeight > 0) {
            if (videoWidth * height > width * videoHeight) {
                height = width * videoHeight / videoWidth;
            } else if (videoWidth * height < width * videoHeight) {
                width = height * videoWidth / videoHeight;
            }
        }
        Log.i(TAG, "setting size: " + width + ‘x’ + height);
        setMeasuredDimension(width, height);
    }

    private void initVideoView() {
        videoWidth = 0;
        videoHeight = 0;
        getHolder().addCallback(surfaceHolderCallback);
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        setFocusable(true);
        setFocusableInTouchMode(true);
        requestFocus();
    }

}

 

和VideoView實現類似,繼承了SurfaceView並且實現了MediaPlayerControl。

一般情況下,android界面的繪制和更新,要交給主ui線程來操作,通過Handler機制。但是播放視頻,需要比較優先和實時的改變和繪制界面。android提供了使用單獨線程繪制UI的機制,就是SurfaceView。

使用SurfaceView,需要實現SurfaceHolder.Callback接口:

  • surfaceCreated,在Surface(SurfaceView內部包含一個Surface實例)創建後,會立即調用該方法,可在該方法中做繪制界面相關的初始化工作;
  • surfaceChanged,當Surface的狀態發生變化,比如大小,會調用該方法,在surfaceCreated方法調用過至少會調用一次該方法;
  • surfaceDestroyed,當銷毀Surface的時候調用。

開發者不能直接操作Surface實例,要通過SurfaceHandler,在SurfaceView中可以通過getHandler方法獲取到SurfaceHandler實例。

SurfaceHander有一些類型,用來標識Surface實例界面數據來源,可以通過setType來操作:

  • SURFACE_TYPE_NORMAL:RAM緩存的原生數據
  • SURFACE_TYPE_HARDWARE:通過DMA,direct memory access,就是直接寫屏技術獲取到的數據,或者其他硬件加速的數據
  • SURFACE_TYPE_GPU:通過GPU加速的數據
  • SURFACE_TYPE_PUSH_BUFFERS:標識數據來源於其他對象,比如照相機,比如視頻播放服務器(android內部有視頻播放的服務器,所有播放視頻相當於客戶端)

CustomerVideoView的構造方法,使用超類的構造方法。都會執行initVideoView()方法用來初始化界面和參數。

另外一個主要的內容是openVideo()方法:

  • mediaPlayer.prepareAsync(),用來異步准備播放,另外還有個prepare()方法,是同步的,也就是全部下載完畢才能播放,顯然,在播放網上視頻的時候需要用前者;
  • 通過attachMediaController()方法,把控制條附加到播放視頻的SurfaceView上,這裡實現的不完全,因此還不能使用,僅僅是把MediaPlayerControl實例通過setMediaPlayer方法設置一下,供OnPreparedListener用來得到加載成功的回調,另外供外面代碼調用得到視頻的時長和當前時長。

源代碼見:

http://easymorse.googlecode.com/svn/tags/videoplayer-0.4/

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved