• Android 学习笔记之SurfaceView的使用+如何实现视频播放...


    学习内容:

    1.掌握Surface的使用...

    2.Android中如何实现视频播放...

    1.SurfaceView类的使用

      在Android中,一般播放音频时我们可以去使用Android提供的MediaPlayer类,但是想要播放视频仅仅依靠MediaPlayer类是远远不够的...这里还需要使用到一个SurfaceView这个组件来完成..为什么?因为像视频和SD图形等都需要迅速的更新...如果这个更新实在主线程内去完成,那么显然是不合理的,因为一个视频的播放,系统会首先确定视频的格式,然后得到视频的编码..然后对编码进行解码,得到一帧一帧的图像,最后在画布上进行迅速更新...这就是视频播放的机制...那么这个过程显然我们需要在另外一个线程内部去完成...这样主线程的其他内容,比如说其他的渲染操作,图片加载什么的,就不会导致主线程阻塞...这样我们就可以使用SurfaceView来完成...

      SurfaceView继承了View类,也是属于视图的一部分...SurfaceView视图内嵌了一个专门用于绘制的Surface...其实每一个Surface都在每一个窗口的后面,我们可以通过SurfaceView类来控制哪些Surface的内容可以显示出来...其实说白了它的作用就是可以直接从内存或者是DMA硬件里获取图像数据...将这些图像数据迅速的显示出来,通过启动另外一个线程可以迅速的完成画面的更新操作,不会导致主线程阻塞...这个在游戏开发中也是起着非常重要的作用...

      SurfaceView使用双缓冲机制,这个机制可以使SurfaceView同时对两张图片进行渲染操作...其实目的也是为了迅速更新图片的显示,第一个缓冲对这一帧进行解析,第二个会对下一帧进行解析...这样就可以避免在上一帧的图片显示完成后,下一帧的图片还没有进行显示的情况发生...这样就可以流畅的播放一个视频文件...

      SurfaceView的实现...

      首先需要一个类去继承这个类,然后实现SurfaceHolder.Callback()接口...使用这个接口的目的就是在Android中,SurfaceView的双缓冲机制是非常消耗资源的,因此Android规定,在SurfaceView可见的时候,SurfaceView会对文件进行解析并显示,当其不可见时,要直接销毁掉SurfaceView以节省资源...那么这个接口的目的就是形成这个过程...

    需要重写的方法

     (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

         //在surface的大小发生改变时激发

     (2)public void surfaceCreated(SurfaceHolder holder){}

         //在创建时激发,一般在这里调用画图的线程。然后画图的工作开始...

     (3)public void surfaceDestroyed(SurfaceHolder holder) {}

         //销毁时激发,一般在这里将画图的线程停止、释放。

      这三个方法就形成了上述说的那种关系...在视频的播放当中,它会结合MediaPlayer完成视频的流畅播放..而在游戏的开发当中,它可以结合Canvas元素来完成游戏中的一些画面迅速更新的操作...

      总而言之,SurfaceView就是一个绘图的容器,它可以在不干扰主线程的情况下,调用另一个线程完成绘图的操作,并迅速的更新图像...以完成更好的显示效果....

    这里来两个实例...一个是SurfaceView配合MediaPlayer完成视频的播放,另一个是结合Canvas完成一个图像在画布上显示出来...

    1.结合Canvas完成图像在画布上的显示...

      这里做了一个摩尔斯灯塔....什么是摩尔斯灯塔...这玩意其实在以前的战争中可以使用的到,这里有一个概念就是摩尔斯码...这个码就是将数字,字母等符号以"."和"-"的形式显示出来就是对文字的一种加密操作...出了可以发报文的形式,还可以以信号灯的形式来发送密报,然后另一方根据对应的信息进行解析...扯远了...

    a .-
    b -...
    c -.-.
    d -..
    e .
    f ..-.
    g --.
    h ....
    i ..
    j .---
    k -.-
    l .-..
    m --
    n -.
    o ---
    p .--.
    q --.-
    r .-.
    s ...
    t -
    u ..-
    v ...-
    w .--
    x -..-
    y -.--
    z --..
    0 -----
    1 .----
    2 ..---
    3 ...--
    4 ....-
    5 .....
    6 -....
    7 --...
    8 ---..
    9 ----.
    . .-.-.-
    - -....-
    , ..--..
    / -..-
    ; -.-.-.
    ( -.--.
    ) -.--.
    @ .--.-
    * ...-.-
    + .-.-.
    % .-...
    ! =---.
    $ ...-..-
    View Code

      这上面的就是摩尔斯码...我写在了txt文档当中...为了方便...但是我们需要注意一个地方就是我们在读取txt文档的时候,一定要把txt文档的对应内容拷贝到res/asset文件夹下面...或者是放在模拟器的内存卡中...千万别放在本地文件...因为Android的资源加载全在res文件夹下面或者是内存卡中,本地文件在正常的java程序中是可以读到的,但是在Android是一定读不到的..这个我已经试过,被坑3小时...因此这点需要注意....

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/layout_1"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >
        <EditText 
             android:id="@+id/input"
             android:layout_height="wrap_content"
             android:layout_width="fill_parent"
             android:hint=""/>
        <Button 
            android:id="@+id/translate"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/input"
            android:text="转换"/>
        <TextView 
            android:id="@+id/show"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:layout_below="@id/translate"
            android:text="输出"/>
        <EditText 
            android:id="@+id/output"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:layout_below="@id/show"
            android:enabled="false"/>
        <Button 
            android:id="@+id/send"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:layout_below="@id/output"
            android:text="发送"/>
    </RelativeLayout>
    View Code

      布局文件非常简单..看看就行....然后就是如何使用SurfaceView和Canvas的结合使用....在没有Canvas的基础下,还是去脑补一下Canvas。。。

    package com.example.mouse;
    
    import java.util.HashMap;
    import java.util.Scanner;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.view.Menu;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.RelativeLayout;
    
    public class MainActivity extends Activity implements View.OnClickListener {
    
        public static HashMap<String, String> map = new HashMap<String, String>();
        private EditText input;
        private EditText output;
        private char chars[];
        private RelativeLayout layout;
        private boolean flag = false;
        private boolean loop = false;
        int count;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            input = (EditText) findViewById(R.id.input);
            output = (EditText) findViewById(R.id.output);
            findViewById(R.id.translate).setOnClickListener(this);
            findViewById(R.id.send).setOnClickListener(this);
            layout= (RelativeLayout) findViewById(R.id.layout_1);//获取布局的id...
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
    
            switch (v.getId()) {
            case R.id.translate:
                try {
                    Scanner in = new Scanner(getResources().getAssets().open(
                            "morce.txt"));//获取资源文件morce.txt文件内部的内容...
                    while (in.hasNextLine()) {
                        String str = in.nextLine();
                        String abc[] = str.trim().split("[\p{Space}]+");
                        map.put(abc[0], abc[1]);//定义了一个HashMap<String,String>,以键值对的形式来保存内容...
                    }
                    map.put(" ", "/");
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                String text = Mouse.morcecode(input.getText().toString(), map);//对我们输入的字符进行匹配..来完成加密操作...
                output.setText(text);
                break;
            case R.id.send:
                if (output.getText() != null
                        && output.getText().toString().length() > 0) {
                    chars = output.getText().toString().toCharArray();
                    count = chars.length;
                    LightView light = new LightView(MainActivity.this);
                    layout.addView(light);//将画布的绘画内容加载到布局中....
                }
            }
        }
    
        class LightView extends SurfaceView {//定义一个类继承SurfaceView类...
    
            SurfaceHolder holder;
    
            public LightView(Context context) {
                // TODO Auto-generated constructor stub
                super(context);
                holder = this.getHolder();//调用getHolder()来获取SurfaceHolder对象..
                holder.addCallback(new SurfaceHolder.Callback() {//实现接口...
    
                    class LightThread implements Runnable {//内部定义异步线程来完成绘画操作...
    
                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            while (loop) {
                                if (count > 0) {
                                    String s = String.valueOf(chars[chars.length
                                            - count]);
                                    Canvas canvas = holder.lockCanvas(null);//锁定画布...
                                    Paint paint = new Paint();
                                    paint.setAntiAlias(true);//去掉锯齿..
                                    paint.setColor(Color.BLACK);//画布的颜色为黑色...
                                    canvas.drawRect(0, 0, 480, 480, paint);
                                    //下面这个是对报文的每一个字节解析以信号灯的形式显示出来...
                                    if (flag) {
                                        paint.setColor(Color.BLACK);
                                    } else {
                                        if (s.equalsIgnoreCase(".")) {
                                            sleep(2);
                                            paint.setColor(Color.YELLOW);
                                        } else if (s.equalsIgnoreCase("-")) {
                                            sleep(4);
                                            paint.setColor(Color.YELLOW);
                                        } else if (s.equalsIgnoreCase(" ")) {
                                            sleep(2);
                                            paint.setColor(Color.BLACK);
                                        } else if (s.equalsIgnoreCase("/")) {
                                            sleep(2);
                                            paint.setColor(Color.BLACK);
                                        }
                                        count--;
                                    }
                                    canvas.drawCircle(250.0f, 200.0f, 100, paint);//画出一个圆用来显示信号...
                                    flag = !flag;
                                    holder.unlockCanvasAndPost(canvas);//解除锁定
                                }
                            }
                        }
    
                        public void sleep(int time) {
                            try {
                                Thread.sleep(time * 80);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
    
                    @Override
                    public void surfaceDestroyed(SurfaceHolder holder) {
                        // TODO Auto-generated method stub
                        loop = false;
                    }
    
                    @Override
                    public void surfaceCreated(SurfaceHolder holder) {
                        // TODO Auto-generated method stub
                        new Thread(new LightThread()).start();
                    }
    
                    @Override
                    public void surfaceChanged(SurfaceHolder holder, int format,
                            int width, int height) {
                        // TODO Auto-generated method stub
    
                    }
                });
                loop = true;
            }
        }
    }

    这样就完成了对输入的文本以摩尔斯码的形式进行加密操作....很简单的一个小东西....

    2.SurfaceView与MediaPlayer的结合操作....

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity" >
        <TextView 
            android:id="@+id/info"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="等待播放"/>
        <LinearLayout 
            android:orientation="horizontal"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content">
            <ImageButton 
                android:id="@+id/play"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:src="@drawable/play"/>
            <ImageButton 
                android:id="@+id/pause"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:src="@drawable/pause"/>
            <ImageButton 
                android:id="@+id/stop"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:src="@drawable/stop"/>
        </LinearLayout>
        <SeekBar 
            android:id="@+id/seekbar"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
        <SurfaceView 
            android:id="@+id/surface_1"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent"/>
    </LinearLayout>

    一个简单的布局文件...三个图片按钮...一个进度条和一个表面视图....

    package com.example.exam7_2;
    
    
    
    import java.io.IOException;
    
    import android.media.MediaPlayer;
    import android.media.MediaPlayer.OnCompletionListener;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.app.Activity;
    import android.view.Menu;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.SeekBar;
    import android.widget.TextView;
    
    public class MainActivity extends Activity implements View.OnClickListener, SeekBar.OnSeekBarChangeListener{
        private MediaPlayer media;
        private boolean playflag=true;
        private boolean pauseflag=false;
        private SeekBar seekbar=null;
        private TextView tv;
        private SurfaceView suf=null;
        private SurfaceHolder sufh=null;
        @SuppressWarnings("deprecation")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tv=(TextView) findViewById(R.id.info);
            findViewById(R.id.play).setOnClickListener(this);
            findViewById(R.id.pause).setOnClickListener(this);
            findViewById(R.id.stop).setOnClickListener(this);
            suf=(SurfaceView) findViewById(R.id.surface_1);
            sufh=suf.getHolder(); //获取SurfaceHolder。。。
            seekbar=(SeekBar) findViewById(R.id.seekbar);
            sufh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置SurfaceView的类型...
            media=new MediaPlayer();
            try {
                media.setDataSource("/sdcard/test.3gp");//这里需要把想要播放的文件拷贝到模拟器的sd卡中去...
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
            
        }
        private class updateseekbar extends AsyncTask<Integer, Integer, String>{//AsyncTask的使用...
    
            protected void onPostExecute(String result){}
            protected void onProgressUpdate(Integer...progress){
                seekbar.setProgress(progress[0]);
            }
            @Override
            protected String doInBackground(Integer... params) {
                // TODO Auto-generated method stub
                while(MainActivity.this.playflag){
                    try {
                        Thread.sleep(params[0]);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    this.publishProgress(MainActivity.this.media.getCurrentPosition());
                }
                return null;
            }
            
        }
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            switch(v.getId()){
            case R.id.play:
            {
                MainActivity.this.media.setOnCompletionListener(new OnCompletionListener() {
                    
                    @Override
                    public void onCompletion(MediaPlayer arg0) {
                        // TODO Auto-generated method stub
                        playflag=false;
                        media.release();
                    }
                });
                seekbar.setMax(MainActivity.this.media.getDuration());
                updateseekbar update=new updateseekbar();
                //执行者execute
                update.execute(1000);
                seekbar.setOnSeekBarChangeListener(this);
                if(MainActivity.this.media!=null){
                    MainActivity.this.media.stop();
                }
                try {
                    MainActivity.this.media.prepare();
                    MainActivity.this.media.start();
                    tv.setText("正在播放文件...");
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    tv.setText("文件出现异常...");
                    e.printStackTrace();
                } 
                break;
            }
            case R.id.pause:
                if(media!=null){
                    if(pauseflag){
                        media.start();
                        pauseflag=false;
                    }else{
                        media.pause();
                        pauseflag=true;
                    }
                }
                break;
            case R.id.stop:
                if(media!=null){
                    media.stop();
                    tv.setText("停止播放文件...");
                }
                break;
            }
        }
    
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,
                boolean fromUser) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
            media.seekTo(seekbar.getProgress());
        }
       
    }

    这样就完成了MediaPlayer和SurfaceView的配合使用...完成了视频的播放...同时添加了一个进度条来控制播放的进度...

     

     

  • 相关阅读:
    Delphi2010 DataSnap 学习(一)
    DLL封装ReportMachine/FastReport报表
    Delphi中的字符串压缩与解压缩
    Delphi 2010 DataSnap封装COM对象
    PHP&MySQL的一个乱码
    Indy接收邮件中文无编码乱码问题
    Asp.net Mvc自定义客户端验证(CheckBox列表的验证)
    浅谈在asp.net mvc3中使用IValidatableObject接口实现Model数据验证
    使用ModelBinder绑定IPrincipal (User)简化ASP.NET MVC单元测试
    asp.net mvc使用TagBuilder的应用程序集
  • 原文地址:https://www.cnblogs.com/RGogoing/p/4574841.html
Copyright © 2020-2023  润新知