1. <tt id="5hhch"><source id="5hhch"></source></tt>
    1. <xmp id="5hhch"></xmp>

  2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

    <rp id="5hhch"></rp>
        <dfn id="5hhch"></dfn>

      1. AndroidApp中使用SurfaceView制作多線程動畫的實(shí)例講解

        時間:2024-09-05 04:36:29 動畫設(shè)計(jì) 我要投稿
        • 相關(guān)推薦

        AndroidApp中使用SurfaceView制作多線程動畫的實(shí)例講解

          1. SurfaceView的定義

          通常情況程序的View和用戶響應(yīng)都是在同一個線程中處理的,這也是為什么處理長時間事件(例如訪問網(wǎng)絡(luò))需要放到另外的線程中去(防止阻塞當(dāng)前UI線程的操作和繪制)。但是在其他線程中卻不能修改UI元素,例如用后臺線程更新自定義View(調(diào)用View的在自定義View中的onDraw函數(shù))是不允許的。

          如果需要在另外的線程繪制界面、需要迅速的更新界面或則渲染UI界面需要較長的時間,這種情況就要使用SurfaceView了。SurfaceView中包含一個Surface對象,而Surface是可以在后臺線程中繪制的。SurfaceView的性質(zhì)決定了其比較適合一些場景:需要界面迅速更新、對幀率要求較高的情況。使用SurfaceView需要注意以下幾點(diǎn)情況:

          SurfaceView和SurfaceHolder.Callback函數(shù)都從當(dāng)前SurfaceView窗口線程中調(diào)用(一般而言就是程序的主線程)。有關(guān)資源狀態(tài)要注意和繪制線程之間的同步。

          在繪制線程中必須先合法的獲取Surface才能開始繪制內(nèi)容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間的狀態(tài)為合法的,另外在Surface類型為SURFACE_TYPE_PUSH_BUFFERS時候是不合法的。

          額外的繪制線程會消耗系統(tǒng)的資源,在使用SurfaceView的時候要注意這點(diǎn)。

          2. SurfaceView的使用

          首先繼承SurfaceView,并實(shí)現(xiàn)SurfaceHolder.Callback接口,實(shí)現(xiàn)它的三個方法:surfaceCreated,surfaceChanged,surfaceDestroyed。

         。1)surfaceCreated(SurfaceHolder holder):surface創(chuàng)建的時候調(diào)用,一般在該方法中啟動繪圖的線程。

          (2)surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸發(fā)生改變的時候調(diào)用,如橫豎屏切換。

         。3)surfaceDestroyed(SurfaceHolder holder) :surface被銷毀的時候調(diào)用,如退出游戲畫面,一般在該方法中停止繪圖線程。

          還需要獲得SurfaceHolder,并添加回調(diào)函數(shù),這樣這三個方法才會執(zhí)行。

          只要繼承SurfaceView類并實(shí)現(xiàn)SurfaceHolder.Callback接口就可以實(shí)現(xiàn)一個自定義的SurfaceView了,SurfaceHolder.Callback在底層的Surface狀態(tài)發(fā)生變化的時候通知View,SurfaceHolder.Callback具有如下的接口:

          (1)surfaceCreated(SurfaceHolder holder):當(dāng)Surface第一次創(chuàng)建后會立即調(diào)用該函數(shù)。程序可以在該函數(shù)中做些和繪制界面相關(guān)的初始化工作,一般情況下都是在另外的線程來繪制界面,所以不要在這個函數(shù)中繪制Surface。

         。2)surfaceChanged(SurfaceHolder holder, int format, int width,int height):當(dāng)Surface的狀態(tài)(大小和格式)發(fā)生變化的時候會調(diào)用該函數(shù),在surfaceCreated調(diào)用后該函數(shù)至少會被調(diào)用一次。

         。3)surfaceDestroyed(SurfaceHolder holder):當(dāng)Surface被摧毀前會調(diào)用該函數(shù),該函數(shù)被調(diào)用后就不能繼續(xù)使用Surface了,一般在該函數(shù)中來清理使用的資源。

          通過SurfaceView的getHolder()函數(shù)可以獲取SurfaceHolder對象,Surface 就在SurfaceHolder對象內(nèi)。雖然Surface保存了當(dāng)前窗口的像素?cái)?shù)據(jù),但是在使用過程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或則Canvas lockCanvas(Rect dirty)函數(shù)來獲取Canvas對象,通過在Canvas上繪制內(nèi)容來修改Surface中的數(shù)據(jù)。如果Surface不可編輯或則尚未創(chuàng)建調(diào)用該函數(shù)會返回null,在 unlockCanvas() 和 lockCanvas()中Surface的內(nèi)容是不緩存的,所以需要完全重繪Surface的內(nèi)容,為了提高效率只重繪變化的部分則可以調(diào)用lockCanvas(Rect dirty)函數(shù)來指定一個dirty區(qū)域,這樣該區(qū)域外的內(nèi)容會緩存起來。在調(diào)用lockCanvas函數(shù)獲取Canvas后,SurfaceView會獲取Surface的一個同步鎖直到調(diào)用unlockCanvasAndPost(Canvas canvas)函數(shù)才釋放該鎖,這里的同步機(jī)制保證在Surface繪制過程中不會被改變(被摧毀、修改)。

          當(dāng)在Canvas中繪制完成后,調(diào)用函數(shù)unlockCanvasAndPost(Canvas canvas)來通知系統(tǒng)Surface已經(jīng)繪制完成,這樣系統(tǒng)會把繪制完的內(nèi)容顯示出來。為了充分利用不同平臺的資源,發(fā)揮平臺的最優(yōu)效果可以通過SurfaceHolder的setType函數(shù)來設(shè)置繪制的類型,目前接收如下的參數(shù):

          (1)SURFACE_TYPE_NORMAL:用RAM緩存原生數(shù)據(jù)的普通Surface

         。2)SURFACE_TYPE_HARDWARE:適用于DMA(Direct memory access )引擎和硬件加速的Surface

          (3)SURFACE_TYPE_GPU:適用于GPU加速的Surface

         。4)SURFACE_TYPE_PUSH_BUFFERS:表明該Surface不包含原生數(shù)據(jù),Surface用到的數(shù)據(jù)由其他對象提供,在Camera圖像預(yù)覽中就使用該類型的Surface,有Camera負(fù)責(zé)提供給預(yù)覽Surface數(shù)據(jù),這樣圖像預(yù)覽會比較流暢。如果設(shè)置這種類型則就不能調(diào)用lockCanvas來獲取Canvas對象了。

          訪問SurfaceView的底層圖形是通過SurfaceHolder接口來實(shí)現(xiàn)的,通過getHolder()方法可以得到這個SurfaceHolder對象。你應(yīng)該實(shí)現(xiàn)surfaceCreated(SurfaceHolder)和surfaceDestroyed(SurfaceHolder)方法來知道在這個Surface在窗口的顯示和隱藏過程中是什么時候創(chuàng)建和銷毀的。

          注意:一個SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()調(diào)用之間是可用的,其他時間是得不到它的Canvas對象的(null)。

          3. SurfaceView實(shí)戰(zhàn)

          下面通過一個小demo來學(xué)習(xí)SurfaceView在實(shí)際項(xiàng)目中的使用,繪制一個精靈,該精靈有四個方向的行走動畫,讓精靈沿著屏幕四周不停的行走。游戲中精靈素材和最終實(shí)現(xiàn)的效果圖:

          首先創(chuàng)建核心類GameView.java,源碼如下:

          public class GameView extends SurfaceView implements SurfaceHolder.Callback { //屏幕寬高 public static int SCREEN_WIDTH; public static int SCREEN_HEIGHT; private Context mContext; private SurfaceHolder mHolder; //最大幀數(shù) (1000 / 30) private static final int DRAW_INTERVAL = 30; private DrawThread mDrawThread; private FrameAnimation []spriteAnimations; private Sprite mSprite; private int spriteWidth = 0; private int spriteHeight = 0; private float spriteSpeed = (float)((500 * SCREEN_WIDTH / 480) * 0.001); private int row = 4; private int col = 4; public GameSurfaceView(Context context) { super(context); this.mContext = context; mHolder = this.getHolder(); mHolder.addCallback(this); initResources(); mSprite = new Sprite(spriteAnimations,0,0,spriteWidth,spriteHeight,spriteSpeed); } private void initResources() { Bitmap[][] spriteImgs = generateBitmapArray(mContext, R.drawable.sprite, row, col); spriteAnimations = new FrameAnimation[row]; for(int i = 0; i < row; i ++) { Bitmap []spriteImg = spriteImgs[i]; FrameAnimation spriteAnimation = new FrameAnimation(spriteImg,new int[]{150,150,150,150},true); spriteAnimations[i] = spriteAnimation; } } public Bitmap decodeBitmapFromRes(Context context, int resourseId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; InputStream is = context.getResources().openRawResource(resourseId); return BitmapFactory.decodeStream(is, null, opt); } public Bitmap createBitmap(Context context, Bitmap source, int row, int col, int rowTotal, int colTotal) { Bitmap bitmap = Bitmap.createBitmap(source, (col - 1) * source.getWidth() / colTotal, (row - 1) * source.getHeight() / rowTotal, source.getWidth() / colTotal, source.getHeight() / rowTotal); return bitmap; } public Bitmap[][] generateBitmapArray(Context context, int resourseId, int row, int col) { Bitmap bitmaps[][] = new Bitmap[row][col]; Bitmap source = decodeBitmapFromRes(context, resourseId); this.spriteWidth = source.getWidth() / col; this.spriteHeight = source.getHeight() / row; for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { bitmaps[i - 1][j - 1] = createBitmap(context, source, i, j, row, col); } } if (source != null && !source.isRecycled()) { source.recycle(); source = null; } return bitmaps; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } public void surfaceCreated(SurfaceHolder holder) { if(null == mDrawThread) { mDrawThread = new DrawThread(); mDrawThread.start(); } } public void surfaceDestroyed(SurfaceHolder holder) { if(null != mDrawThread) { mDrawThread.stopThread(); } } private class DrawThread extends Thread { public boolean isRunning = false; public DrawThread() { isRunning = true; } public void stopThread() { isRunning = false; boolean workIsNotFinish = true; while (workIsNotFinish) { try { this.join();// 保證run方法執(zhí)行完畢 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } workIsNotFinish = false; } } public void run() { long deltaTime = 0; long tickTime = 0; tickTime = System.currentTimeMillis(); while (isRunning) { Canvas canvas = null; try { synchronized (mHolder) { canvas = mHolder.lockCanvas(); //設(shè)置方向 mSprite.setDirection(); //更新精靈位置 mSprite.updatePosition(deltaTime); drawSprite(canvas); } } catch (Exception e) { e.printStackTrace(); } finally { if (null != mHolder) { mHolder.unlockCanvasAndPost(canvas); } } deltaTime = System.currentTimeMillis() - tickTime; if(deltaTime < DRAW_INTERVAL) { try { Thread.sleep(DRAW_INTERVAL - deltaTime); } catch (InterruptedException e) { e.printStackTrace(); } } tickTime = System.currentTimeMillis(); } } } private void drawSprite(Canvas canvas) { //清屏操作 canvas.drawColor(Color.BLACK); mSprite.draw(canvas); } }

          GameView.java中包含了一個繪圖線程DrawThread,在線程的run方法中鎖定Canvas、繪制精靈、更新精靈位置、釋放Canvas等操作。因?yàn)榫`素材是一張大圖,所以這里進(jìn)行了裁剪生成一個二維數(shù)組。使用這個二維數(shù)組初始化了精靈四個方向的動畫,下面看Sprite.java的源碼。

          public class Sprite { public static final int DOWN = 0; public static final int LEFT = 1; public static final int RIGHT = 2; public static final int UP = 3; public float x; public float y; public int width; public int height; //精靈行走速度 public double speed; //精靈當(dāng)前行走方向 public int direction; //精靈四個方向的動畫 public FrameAnimation[] frameAnimations; public Sprite(FrameAnimation[] frameAnimations, int positionX, int positionY, int width, int height, float speed) { this.frameAnimations = frameAnimations; this.x = positionX; this.y = positionY; this.width = width; this.height = height; this.speed = speed; } public void updatePosition(long deltaTime) { switch (direction) { case LEFT: //讓物體的移動速度不受機(jī)器性能的影響,每幀精靈需要移動的距離為:移動速度*時間間隔 this.x = this.x - (float) (this.speed * deltaTime); break; case DOWN: this.y = this.y + (float) (this.speed * deltaTime); break; case RIGHT: this.x = this.x + (float) (this.speed * deltaTime); break; case UP: this.y = this.y - (float) (this.speed * deltaTime); break; } } /** * 根據(jù)精靈的當(dāng)前位置判斷是否改變行走方向 */ public void setDirection() { if (this.x <= 0 && (this.y + this.height) < GameSurfaceView.SCREEN_HEIGHT) { if (this.x < 0) this.x = 0; this.direction = Sprite.DOWN; } else if ((this.y + this.height) >= GameSurfaceView.SCREEN_HEIGHT && (this.x + this.width) < GameSurfaceView.SCREEN_WIDTH) { if ((this.y + this.height) > GameSurfaceView.SCREEN_HEIGHT) this.y = GameSurfaceView.SCREEN_HEIGHT - this.height; this.direction = Sprite.RIGHT; } else if ((this.x + this.width) >= GameSurfaceView.SCREEN_WIDTH && this.y > 0) { if ((this.x + this.width) > GameSurfaceView.SCREEN_WIDTH) this.x = GameSurfaceView.SCREEN_WIDTH - this.width; this.direction = Sprite.UP; } else { if (this.y < 0) this.y = 0; this.direction = Sprite.LEFT; } } public void draw(Canvas canvas) { FrameAnimation frameAnimation = frameAnimations[this.direction]; Bitmap bitmap = frameAnimation.nextFrame(); if (null != bitmap) { canvas.drawBitmap(bitmap, x, y, null); } }}

          精靈類主要是根據(jù)當(dāng)前位置判斷行走的方向,然后根據(jù)行走的方向更新精靈的位置,再繪制自身的動畫。由于精靈的動畫是一幀一幀的播放圖片,所以這里封裝了FrameAnimation.java,源碼如下:

          public class FrameAnimation{ /**動畫顯示的需要的資源 */ private Bitmap[] bitmaps; /**動畫每幀顯示的時間 */ private int[] duration; /**動畫上一幀顯示的時間 */ protected Long lastBitmapTime; /**動畫顯示的索引值,防止數(shù)組越界 */ protected int step; /**動畫是否重復(fù)播放 */ protected boolean repeat; /**動畫重復(fù)播放的次數(shù)*/ protected int repeatCount; /** * @param bitmap:顯示的圖片
        * @param duration:圖片顯示的時間
        * @param repeat:是否重復(fù)動畫過程
        */ public FrameAnimation(Bitmap[] bitmaps, int duration[], boolean repeat) { this.bitmaps = bitmaps; this.duration = duration; this.repeat = repeat; lastBitmapTime = null; step = 0; } public Bitmap nextFrame() { // 判斷step是否越界 if (step >= bitmaps.length) { //如果不無限循環(huán) if( !repeat ) { return null; } else { lastBitmapTime = null; } } if (null == lastBitmapTime) { // 第一次執(zhí)行 lastBitmapTime = System.currentTimeMillis(); return bitmaps[step = 0]; } // 第X次執(zhí)行 long nowTime = System.currentTimeMillis(); if (nowTime - lastBitmapTime <= duration[step]) { // 如果還在duration的時間段內(nèi),則繼續(xù)返回當(dāng)前Bitmap // 如果duration的值小于0,則表明永遠(yuǎn)不失效,一般用于背景 return bitmaps[step]; } lastBitmapTime = nowTime; return bitmaps[step++];// 返回下一Bitmap } }

          FrameAnimation根據(jù)每一幀的顯示時間返回當(dāng)前的圖片幀,若沒有超過指定的時間則繼續(xù)返回當(dāng)前幀,否則返回下一幀。

          接下來需要做的是讓Activty顯示的View為我們之前創(chuàng)建的GameView,然后設(shè)置全屏顯示。

          public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); DisplayMetrics outMetrics = new DisplayMetrics(); this.getWindowManager().getDefaultDisplay().getMetrics(outMetrics); GameSurfaceView.SCREEN_WIDTH = outMetrics.widthPixels; GameSurfaceView.SCREEN_HEIGHT = outMetrics.heightPixels; GameSurfaceView gameView = new GameSurfaceView(this); setContentView(gameView); }

          現(xiàn)在運(yùn)行Android工程,應(yīng)該就可以看到一個手持寶劍的武士在沿著屏幕不停的走了。

          

        【AndroidApp中使用SurfaceView制作多線程動畫的實(shí)例講解】相關(guān)文章:

        PHP中curl的使用實(shí)例07-31

        實(shí)例講解托福閱讀推斷題技巧06-20

        動畫制作流程詳解11-25

        廣告動畫的制作流程01-09

        動畫制作方法05-19

        建筑動畫的制作流程06-10

        flash怎么制作動畫08-20

        MG動畫制作的小技巧09-22

        flash動畫制作流程簡介10-07

        定格動畫的制作流程是怎樣的?01-11

        国产高潮无套免费视频_久久九九兔免费精品6_99精品热6080YY久久_国产91久久久久久无码

        1. <tt id="5hhch"><source id="5hhch"></source></tt>
          1. <xmp id="5hhch"></xmp>

        2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

          <rp id="5hhch"></rp>
              <dfn id="5hhch"></dfn>