android SurfaceView
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
參考:http://blog.csdn.net/luoshengyang/article/details/8661317/
參考:http://blog.csdn.net/listening_music/article/details/6860786
參考:http://www.cnblogs.com/yxx123/p/5720907.html
參考:http://blog.csdn.net/chenzheng_java/article/details/6272212
參考:http://blog.csdn.net/lovexieyuan520/article/details/50614670
學(xué)習(xí)內(nèi)容:
1.掌握Surface的使用...
2.Android中如何實(shí)現(xiàn)視頻播放...
?
1.SurfaceView類的使用
? 在Android中,一般播放音頻時我們可以去使用Android提供的MediaPlayer類,但是想要播放視頻僅僅依靠MediaPlayer類是遠(yuǎn)遠(yuǎn)不夠的...這里還需要使用到一個SurfaceView這個組件來完成..為什么?因?yàn)橄褚曨l和SD圖形等都需要迅速的更新...如果這個更新實(shí)在主線程內(nèi)去完成,那么顯然是不合理的,因?yàn)橐粋€視頻的播放,系統(tǒng)會首先確定視頻的格式,然后得到視頻的編碼..然后對編碼進(jìn)行解碼,得到一幀一幀的圖像,最后在畫布上進(jìn)行迅速更新...這就是視頻播放的機(jī)制...那么這個過程顯然我們需要在另外一個線程內(nèi)部去完成...這樣主線程的其他內(nèi)容,比如說其他的渲染操作,圖片加載什么的,就不會導(dǎo)致主線程阻塞...這樣我們就可以使用SurfaceView來完成...
? SurfaceView繼承了View類,也是屬于視圖的一部分...SurfaceView視圖內(nèi)嵌了一個專門用于繪制的Surface...其實(shí)每一個Surface都在每一個窗口的后面,我們可以通過SurfaceView類來控制哪些Surface的內(nèi)容可以顯示出來...其實(shí)說白了它的作用就是可以直接從內(nèi)存或者是DMA硬件里獲取圖像數(shù)據(jù)...將這些圖像數(shù)據(jù)迅速的顯示出來,通過啟動另外一個線程可以迅速的完成畫面的更新操作,不會導(dǎo)致主線程阻塞...這個在游戲開發(fā)中也是起著非常重要的作用...
? SurfaceView使用雙緩沖機(jī)制,這個機(jī)制可以使SurfaceView同時對兩張圖片進(jìn)行渲染操作...其實(shí)目的也是為了迅速更新圖片的顯示,第一個緩沖對這一幀進(jìn)行解析,第二個會對下一幀進(jìn)行解析...這樣就可以避免在上一幀的圖片顯示完成后,下一幀的圖片還沒有進(jìn)行顯示的情況發(fā)生...這樣就可以流暢的播放一個視頻文件...
? SurfaceView的實(shí)現(xiàn)...
? 首先需要一個類去繼承這個類,然后實(shí)現(xiàn)SurfaceHolder.Callback()接口...使用這個接口的目的就是在Android中,SurfaceView的雙緩沖機(jī)制是非常消耗資源的,因此Android規(guī)定,在SurfaceView可見的時候,SurfaceView會對文件進(jìn)行解析并顯示,當(dāng)其不可見時,要直接銷毀掉SurfaceView以節(jié)省資源...那么這個接口的目的就是形成這個過程...
需要重寫的方法
(1)public?void?surfaceChanged(SurfaceHolder?holder,int?format,int?width,int?height){}
??? //在surface的大小發(fā)生改變時激發(fā)
(2)public?void?surfaceCreated(SurfaceHolder?holder){}
??? //在創(chuàng)建時激發(fā),一般在這里調(diào)用畫圖的線程。然后畫圖的工作開始...
(3)public?void?surfaceDestroyed(SurfaceHolder?holder)?{}
??? //銷毀時激發(fā),一般在這里將畫圖的線程停止、釋放。
? 這三個方法就形成了上述說的那種關(guān)系...在視頻的播放當(dāng)中,它會結(jié)合MediaPlayer完成視頻的流暢播放..而在游戲的開發(fā)當(dāng)中,它可以結(jié)合Canvas元素來完成游戲中的一些畫面迅速更新的操作...
? 總而言之,SurfaceView就是一個繪圖的容器,它可以在不干擾主線程的情況下,調(diào)用另一個線程完成繪圖的操作,并迅速的更新圖像...以完成更好的顯示效果....
這里來兩個實(shí)例...一個是SurfaceView配合MediaPlayer完成視頻的播放,另一個是結(jié)合Canvas完成一個圖像在畫布上顯示出來...
1.結(jié)合Canvas完成圖像在畫布上的顯示...
? 這里做了一個摩爾斯燈塔....什么是摩爾斯燈塔...這玩意其實(shí)在以前的戰(zhàn)爭中可以使用的到,這里有一個概念就是摩爾斯碼...這個碼就是將數(shù)字,字母等符號以"."和"-"的形式顯示出來就是對文字的一種加密操作...出了可以發(fā)報(bào)文的形式,還可以以信號燈的形式來發(fā)送密報(bào),然后另一方根據(jù)對應(yīng)的信息進(jìn)行解析...扯遠(yuǎn)了...
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 ----. . .-.-.- - -....- , ..--.. / -..- ; -.-.-. ( -.--. ) -.--. @ .--.- * ...-.- + .-.-. % .-... ! =---. $ ...-..-? 這上面的就是摩爾斯碼...我寫在了txt文檔當(dāng)中...為了方便...但是我們需要注意一個地方就是我們在讀取txt文檔的時候,一定要把txt文檔的對應(yīng)內(nèi)容拷貝到res/asset文件夾下面...或者是放在模擬器的內(nèi)存卡中...千萬別放在本地文件...因?yàn)锳ndroid的資源加載全在res文件夾下面或者是內(nèi)存卡中,本地文件在正常的java程序中是可以讀到的,但是在Android是一定讀不到的..這個我已經(jīng)試過,被坑3小時...因此這點(diǎn)需要注意....
<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="轉(zhuǎn)換"/><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="發(fā)送"/> </RelativeLayout>? 布局文件非常簡單..看看就行....然后就是如何使用SurfaceView和Canvas的結(jié)合使用....在沒有Canvas的基礎(chǔ)下,還是去腦補(bǔ)一下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;@Overrideprotected 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...}@Overridepublic 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;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch (v.getId()) {case R.id.translate:try {Scanner in = new Scanner(getResources().getAssets().open("morce.txt"));//獲取資源文件morce.txt文件內(nèi)部的內(nèi)容...while (in.hasNextLine()) {String str = in.nextLine();String abc[] = str.trim().split("[\\p{Space}]+");map.put(abc[0], abc[1]);//定義了一個HashMap<String,String>,以鍵值對的形式來保存內(nèi)容...}map.put(" ", "/");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}String text = Mouse.morcecode(input.getText().toString(), map);//對我們輸入的字符進(jìn)行匹配..來完成加密操作...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);//將畫布的繪畫內(nèi)容加載到布局中....}}}class LightView extends SurfaceView {//定義一個類繼承SurfaceView類...SurfaceHolder holder;public LightView(Context context) {// TODO Auto-generated constructor stubsuper(context);holder = this.getHolder();//調(diào)用getHolder()來獲取SurfaceHolder對象..holder.addCallback(new SurfaceHolder.Callback() {//實(shí)現(xiàn)接口...class LightThread implements Runnable {//內(nèi)部定義異步線程來完成繪畫操作...@Overridepublic void run() {// TODO Auto-generated method stubwhile (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);//下面這個是對報(bào)文的每一個字節(jié)解析以信號燈的形式顯示出來...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 blocke.printStackTrace();}}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stubloop = false;}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubnew Thread(new LightThread()).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format,int width, int height) {// TODO Auto-generated method stub}});loop = true;}} }這樣就完成了對輸入的文本以摩爾斯碼的形式進(jìn)行加密操作....很簡單的一個小東西....
2.SurfaceView與MediaPlayer的結(jié)合操作....
<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>一個簡單的布局文件...三個圖片按鈕...一個進(jìn)度條和一個表面視圖....
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")@Overrideprotected 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);//設(shè)置SurfaceView的類型...media=new MediaPlayer();try {media.setDataSource("/sdcard/test.3gp");//這里需要把想要播放的文件拷貝到模擬器的sd卡中去...} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} }private class updateseekbar extends AsyncTask<Integer, Integer, String>{//AsyncTask的使用...protected void onPostExecute(String result){}protected void onProgressUpdate(Integer...progress){seekbar.setProgress(progress[0]);}@Overrideprotected String doInBackground(Integer... params) {// TODO Auto-generated method stubwhile(MainActivity.this.playflag){try {Thread.sleep(params[0]);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}this.publishProgress(MainActivity.this.media.getCurrentPosition());}return null;}}@Overridepublic 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;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.play:{MainActivity.this.media.setOnCompletionListener(new OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer arg0) {// TODO Auto-generated method stubplayflag=false;media.release();}});seekbar.setMax(MainActivity.this.media.getDuration());updateseekbar update=new updateseekbar();//執(zhí)行者executeupdate.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 blocktv.setText("文件出現(xiàn)異常...");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;}}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {// TODO Auto-generated method stub}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {// TODO Auto-generated method stub}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {// TODO Auto-generated method stubmedia.seekTo(seekbar.getProgress());}}這樣就完成了MediaPlayer和SurfaceView的配合使用...完成了視頻的播放...同時添加了一個進(jìn)度條來控制播放的進(jìn)度...
轉(zhuǎn)載于:https://my.oschina.net/kun123/blog/967998
總結(jié)
以上是生活随笔為你收集整理的android SurfaceView的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编译GSLSDevil的全过程
- 下一篇: 兔子-ps抠图