天天看點

Android Video簡述

<b>Android Video</b><b>簡述</b>

         Video的播放實作,至于錄制請參見《Android Camera簡述》。

<b>一、播放方式</b>

<b>1</b><b>)自帶播放器播放</b>

         入口Action為ACTION_VIEW,Data為Uri,Type為MIME類型。

// 自帶播放器播放 

private void sysPlayer(String filename) { 

    Uri uri = Uri.parse(filename); 

    Intent intent = new Intent(Intent.ACTION_VIEW); 

    intent.setDataAndType(uri, "video/3gp"); 

    startActivity(intent); 

<b>2</b><b>)VideoView</b><b>播放</b>

         VideoView元件進行播放。在《Android Camera簡述》的Camera攝像樣例中的預覽操作即是這種方式。(例子選擇VideoView播放,仍是用的同一個類==)

<b>3</b><b>)MediaPlayer</b><b>播放</b>

         MediaPlayer進行播放控制。不再贅述了==,具體看下節例子。

<b>二、視訊播放器</b>

<b>1</b><b>)視訊播放活動</b>

         檔案浏覽方式,找到3gp檔案進行播放。

         1)直接點選,以MediaPlayer方式播放(簡單的自定義了下界面^^)

         2)長按時,可選"自帶播放器播放"、"VideoView播放"、"MediaPlayer播放"三種方式

<b>2</b><b>)檔案浏覽器接口</b>

         定義了檔案夾點選、檔案點選、檔案長按、錯誤四方法。

<b>3</b><b>)檔案清單擴充卡</b>

         自定義檔案清單擴充卡。主要就是getView()内對一個Item布局的的指派,有圖檔、名稱、類型、大小四項。

<b>4</b><b>)檔案浏覽器</b>

         自定義的檔案浏覽器。

         1)屬性資源的使用。即在attrs.xml定義的FileBrowser的屬性。

不過,extName&amp;fileImage以字尾數字不斷增加的好像不好定義,仍是自定義空間方式。

         2)接口事件,參見OnFileBrowserListener.java。

 <b></b>

public class FileBrowser extends ListView implements 

        AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { 

    public static final int ERROR_ACCESS_DIR = 1; // 通路目錄錯誤 

    /* 

     * 這是其他屬性的命名空間。 

     *  

     * 而attrs.xml定義的FileBrowser的屬性,命名空間需如下定義,否則取不到屬性值。 

     * xmlns:空間名="http://schemas.android.com/apk/res/自定義元件所在包名" 

     */ 

    private final String namespace = "http://vaero.blog.51cto.com/"; 

    /* 自定義屬性 */ 

    private String baseDir; // 初始根目錄 

    private boolean backFinish; // 是否傳回退出 

    private int folderImageResId; // 檔案夾的圖檔資源id 

    private Map&lt;String, Integer&gt; fileImageResIdMap = new HashMap&lt;String, Integer&gt;(); // 各檔案類型的圖檔資源id 

    private int otherFileImageResId; // 其他檔案類型的圖檔資源id 

    private Stack&lt;String&gt; dirStack = new Stack&lt;String&gt;(); // 棧方式存放目前目錄 

    private List&lt;File&gt; fileList = new ArrayList&lt;File&gt;();; // 目前目錄内File 

    private FileListAdapter fileListAdapter; // 自定義檔案清單擴充卡 

    private OnFileBrowserListener listener; // 檔案浏覽器接口 

    public FileBrowser(Context context, AttributeSet attrs) { 

        super(context, attrs); 

        // 獲得TypedArray對象 

        TypedArray typedArray = context.obtainStyledAttributes(attrs, 

                R.styleable.FileBrowser); 

        // 擷取baseDir 

        baseDir = typedArray.getString(R.styleable.FileBrowser_baseDir); 

        if (null == baseDir) { // 未定義時為SDCard目錄 

            baseDir = Environment.getExternalStorageDirectory().toString(); 

        } 

        setCurrentPath(baseDir); // 由dir設定目前棧 

        // 擷取backFinish,預設true 

        backFinish = typedArray.getBoolean(R.styleable.FileBrowser_backFinish, 

                false); 

        // 擷取folderImage,預設0 

        folderImageResId = typedArray.getResourceId( 

                R.styleable.FileBrowser_folderImage, 0); 

        // 擷取otherFileImage,預設0 

        otherFileImageResId = typedArray.getResourceId( 

                R.styleable.FileBrowser_otherFileImage, 0); 

        // 擷取extName&amp;fileImage(以字尾數字不斷增加的方式動态擷取) 

        String extName; 

        int fileImageResId; 

        int index = 1; 

        while (true) { 

            extName = attrs.getAttributeValue(namespace, "extName" + index); 

            fileImageResId = attrs.getAttributeResourceValue(namespace, 

                    "fileImage" + index, 0); 

            // 不存在extName&amp;fileImage屬性時跳出循環 

            if ("".equals(extName) || extName == null || fileImageResId == 0) { 

                break; 

            } 

            fileImageResIdMap.put(extName, fileImageResId); 

            index++; 

        // 設定Item點選監聽接口 

        setOnItemClickListener(this); 

        setOnItemLongClickListener(this); 

        // 設定目前目錄的File 

        setCurrentDirFiles(); 

        // 設定擴充卡 

        fileListAdapter = new FileListAdapter(context, fileList, 

                folderImageResId, fileImageResIdMap, otherFileImageResId); 

        setAdapter(fileListAdapter); 

    } 

    // 由dir設定目前棧 

    private void setCurrentPath(String dir) { 

        dirStack.clear(); 

        String spDir = dir.charAt(0) == '/' ? dir.substring(1) : dir; 

        for (String s : spDir.split("/")) { 

            dirStack.push(s); 

    // 由棧擷取目前目錄 

    private String getCurrentDir() { 

        StringBuffer result = new StringBuffer(); 

        result.append("/"); 

        for (int i = 0; i &lt; dirStack.size(); i++) { 

            result.append(dirStack.get(i)); 

            result.append("/"); 

        return result.toString(); 

    // 設定目前目錄的File 

    private boolean setCurrentDirFiles() { 

        // 獲得目前目錄中所有的File對象 

        File[] files = new File(getCurrentDir()).listFiles(); 

        if (null == files) { // 一些系統目錄可能獲不到,為null 

            dirStack.pop(); 

            return false; 

        fileList.clear(); 

        // 多于一級時,增加null以表示傳回上一級 

        if (!dirStack.isEmpty()) 

            fileList.add(null); 

        for (File file : files) { 

            fileList.add(file); 

        // 類型順序:null、檔案夾、檔案;同類型順序:字典順序 

        Collections.sort(fileList, new Comparator&lt;File&gt;() { 

            @Override 

            public int compare(File f1, File f2) { 

                if (null == f1) { 

                    return -1; 

                } else if (null == f2) { 

                    return 1; 

                } else if (f1.isDirectory() &amp;&amp; !f2.isDirectory()) { 

                } else if (!f1.isDirectory() &amp;&amp; f2.isDirectory()) { 

                } else { 

                    return f1.toString().compareTo(f2.toString()); // 字典順序比較 

                } 

        }); 

        return true; 

    @Override 

    public void onItemClick(AdapterView&lt;?&gt; parent, View view, int position, 

            long id) { 

        File file = fileList.get(position); 

        if (file == null) { // null時,傳回上一級 

            dirStack.pop(); // 出棧 

            setCurrentDirFiles(); // 設定目前目錄的File 

            fileListAdapter.notifyDataSetChanged(); // 通知資料改變,重新整理清單 

            if (listener != null) { 

                listener.onDirItemClick(getCurrentDir()); 

        } else if (file.isDirectory()) { // 目錄時 

            dirStack.push(file.getName()); // 壓棧 

            if (setCurrentDirFiles()) { // 設定目前目錄的File 

                fileListAdapter.notifyDataSetChanged(); // 通知資料改變,重新整理清單 

                if (listener != null) { 

                    listener.onDirItemClick(getCurrentDir()); 

            } else { 

                    listener.onError(ERROR_ACCESS_DIR); 

        } else { // 檔案時 

                listener.onFileItemClick(file.getAbsolutePath()); 

    public boolean onItemLongClick(AdapterView&lt;?&gt; parent, View view, 

            int position, long id) { 

        if (file.isFile()) { // 檔案時 

                listener.onFileItemLongClick(file.getAbsolutePath()); 

                return true; 

        return false; 

    public boolean onKeyDown(int keyCode, KeyEvent event) { 

        if (keyCode == KeyEvent.KEYCODE_BACK &amp;&amp; !backFinish 

                &amp;&amp; !dirStack.isEmpty()) { 

            return true; 

        return super.onKeyDown(keyCode, event); 

    // 設定檔案浏覽器接口 

    public void setOnFileBrowserListener(OnFileBrowserListener listener) { 

        this.listener = listener; 

<b>5</b><b>)MediaPlayer</b><b>播放視訊</b>

         MediaPlayer播放視訊。控制界面很簡單,描述如下:

         1)暫停、播放、停止三按鈕

         2)進度條實時進度顯示,及拖動跳轉

         3)進度條左側目前時間、右側總時間顯示

         ps:播放畫面自适應等比例滿屏

public class VideoPlayer extends Activity implements 

        MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, 

        SurfaceHolder.Callback, SeekBar.OnSeekBarChangeListener { 

    public static final int STATUS_STOPPED = 1; 

    public static final int STATUS_PAUSING = 2; 

    public static final int STATUS_PLAYING = 3; 

    private MediaPlayer mPlayer; // MediaPlayer對象 

    private SurfaceHolder mSurfaceHolder; // SurfaceHolder對象 

    private SurfaceView surfaceView; // SurfaceView元件 

    private SeekBar seekBar; // SeekBar元件 

    private TextView nowTime, totleTime; // TextView元件 

    private LinearLayout linearLayout; // 播放控制布局 

    private String mTimerFormat = "%02d:%02d"; // 時間格式 

    /* 時間更新Handler */ 

    private final Handler mHandler = new Handler(); 

    private Runnable mUpdateTimer = new Runnable() { 

        public void run() { 

            updateTimerView(); 

    }; 

    private String filename; // 檔案名稱 

    private int mStatus = STATUS_STOPPED; // 目前狀态 

    protected void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.video_player); 

        surfaceView = (SurfaceView) findViewById(R.id.surfaceView); 

        seekBar = (SeekBar) findViewById(R.id.seekBar); 

        seekBar.setOnSeekBarChangeListener(this); 

        nowTime = (TextView) findViewById(R.id.nowTime); 

        totleTime = (TextView) findViewById(R.id.totleTime); 

        linearLayout = (LinearLayout) findViewById(R.id.linearLayout); 

        // 擷取檔案名稱 

        filename = getIntent().getStringExtra(VideoPlayerActivity.KEY_FILENAME); 

        /* 初始化mSurfaceHolder */ 

        mSurfaceHolder = surfaceView.getHolder(); 

        mSurfaceHolder.addCallback(this); // 設定回調接口 

        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 設定為Buffer類型(播放視訊&amp;Camera預覽) 

        /* 初始化MediaPlayer */ 

        mPlayer = new MediaPlayer(); 

        mPlayer.setOnPreparedListener(this); 

        mPlayer.setOnCompletionListener(this); 

        try { 

            mPlayer.setDataSource(filename); 

        } catch (Exception e) { 

            e.printStackTrace(); 

    // 暫停按鈕 

    public void pauseBtn(View v) { 

        if (mStatus == STATUS_PLAYING) { 

            mPlayer.pause(); 

            mStatus = STATUS_PAUSING; 

    // 播放按鈕 

    public void playBtn(View v) { 

        if (mStatus == STATUS_PAUSING) { 

            mPlayer.start(); // 繼續播放 

            mStatus = STATUS_PLAYING; 

            updateTimerView(); // 更新時間 

        } else { 

            /* 重新開始播放 */ 

            mPlayer.reset(); 

            try { 

                mPlayer.setDataSource(filename); 

            } catch (Exception e) { 

                e.printStackTrace(); 

            mPlayer.prepareAsync(); // 異步準備 

    // 停止按鈕 

    public void stopBtn(View v) { 

        if (mStatus != STATUS_STOPPED) { 

            mPlayer.stop(); // 停止播放 

            mPlayer.reset(); // 重置 

            mStatus = STATUS_STOPPED; 

    // 當Surface被建立的時被觸發 

    public void surfaceCreated(SurfaceHolder holder) { 

        mPlayer.setDisplay(holder); // 指定SurfaceHolder 

        mPlayer.prepareAsync(); // 異步準備(将回調OnPreparedListener接口) 

    // 當Surface尺寸等參數改變時觸發 

    public void surfaceChanged(SurfaceHolder holder, int format, int width, 

            int height) { 

    // 當surface銷毀時觸發 

    public void surfaceDestroyed(SurfaceHolder holder) { 

    public void onPrepared(MediaPlayer mp) { 

        /* 獲得視窗寬長 */ 

        Display display = getWindowManager().getDefaultDisplay(); 

        int wWidth = display.getWidth(); 

        int wHeight = display.getHeight(); 

        /* 獲得視訊寬長 */ 

        int vWidth = mPlayer.getVideoWidth(); 

        int vHeight = mPlayer.getVideoHeight(); 

        /* 最适螢幕 */ 

        float wRatio = (float) vWidth / (float) wWidth; // 寬度比 

        float hRatio = (float) vHeight / (float) wHeight; // 高度比 

        float ratio = Math.max(wRatio, hRatio); // 較大的比 

        vWidth = (int) Math.ceil((float) vWidth / ratio); // 新視訊寬度 

        vHeight = (int) Math.ceil((float) vHeight / ratio); // 新視訊高度  

        // 改變SurfaceHolder大小 

        mSurfaceHolder.setFixedSize(vWidth, vHeight); 

        // 設定新布局參數(這在samsung i9088上出現stretch的錯誤==) 

        // surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight)); 

        // 設定總時間顯示 

        setTimeView(totleTime, mPlayer.getDuration()); 

        // 啟動播放 

        mPlayer.start(); 

        // 設定狀态 

        mStatus = STATUS_PLAYING; 

        // 更新時間 

        updateTimerView(); 

    public void onCompletion(MediaPlayer mp) { 

        mStatus = STATUS_STOPPED; 

        finish(); // 播放完成後退出 

    public boolean onTouchEvent(MotionEvent event) { 

        if (event.getAction() == MotionEvent.ACTION_DOWN) { 

            // 播放控制布局的顯示&amp;隐藏 

            linearLayout 

                    .setVisibility(linearLayout.getVisibility() == View.VISIBLE ? View.GONE 

                            : View.VISIBLE); 

        return super.onTouchEvent(event); 

    public void onBackPressed() { 

        super.onBackPressed(); 

        stopBtn(null); // 停止 

    protected void onDestroy() { 

        super.onDestroy(); 

        mPlayer.release(); // 釋放 

    // 更新時間 

    private void updateTimerView() { 

            int position = mPlayer.getCurrentPosition(); 

            int duration = mPlayer.getDuration(); 

            setTimeView(nowTime, position); // 設定時間顯示 

            long pos = seekBar.getMax() * position / duration; 

            seekBar.setProgress((int) pos); // 設定進度條 

            mHandler.postDelayed(mUpdateTimer, 1000); 

        } else if (mStatus == STATUS_STOPPED) { 

            nowTime.setText("00:00"); 

            seekBar.setProgress(0); 

    // 設定時間顯示 

    private void setTimeView(TextView textView, int msec) { 

        if (msec &gt;= 1000 * 60 * 60) { // &gt;=1h 

            textView.setText(String.format(mTimerFormat, msec / 1000 / 60 / 60, 

                    msec / 1000 / 60 % 60)); 

            textView.setText(String.format(mTimerFormat, msec / 1000 / 60, 

                    msec / 1000 % 60)); 

    // SeekBar進度改變時 

    public void onProgressChanged(SeekBar seekBar, int progress, 

            boolean fromUser) { 

    // SeekBar開始拖動時 

    public void onStartTrackingTouch(SeekBar seekBar) { 

    // SeekBar結束拖動時 

    public void onStopTrackingTouch(SeekBar seekBar) { 

            mPlayer.seekTo(seekBar.getProgress() * mPlayer.getDuration() 

                    / seekBar.getMax()); // 跳轉 

<b>三、後記</b>

<b>1</b><b>)擴充内容</b>

1.1)應用資源系列之屬性[Attribute]資源

1.2)RMVB軟解,有興趣的可以找找(我稍搜了下,無發現==)

<b>2</b><b>)子產品概覽</b>

2.1)Video播放

<a target="_blank" href="http://blog.51cto.com/attachment/201204/090814440.png"></a>

<b>3</b><b>)運作效果</b>

3.1)Video播放

<a target="_blank" href="http://blog.51cto.com/attachment/201204/091013412.png"></a>

3.2)檔案浏覽器

<a target="_blank" href="http://blog.51cto.com/attachment/201204/090814882.png"></a>

3.3)長按選擇方式

<a target="_blank" href="http://blog.51cto.com/attachment/201204/090814263.png"></a>

3.4)自帶播放器播放

<a target="_blank" href="http://blog.51cto.com/attachment/201204/091013667.png"></a>

3.5)VideoView播放

<a target="_blank" href="http://blog.51cto.com/attachment/201204/091013734.png"></a>

3.6)MediaPlayer播放

     本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/834887,如需轉載請自行聯系原作者

繼續閱讀