亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android App中使用SurfaceView制作多線(xiàn)程動(dòng)畫(huà)的實(shí)例講解

 更新時(shí)間:2016年04月28日 16:07:16   作者:Alex  
這篇文章主要介紹了Android App中使用SurfaceView制作多線(xiàn)程動(dòng)畫(huà)的實(shí)例講解,SurfaceView經(jīng)常被用來(lái)制作游戲中的動(dòng)畫(huà),不過(guò)同時(shí)要注意畫(huà)面閃爍的問(wèn)題,需要的朋友可以參考下

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

如果需要在另外的線(xiàn)程繪制界面、需要迅速的更新界面或則渲染UI界面需要較長(zhǎng)的時(shí)間,這種情況就要使用SurfaceView了。SurfaceView中包含一個(gè)Surface對(duì)象,而Surface是可以在后臺(tái)線(xiàn)程中繪制的。SurfaceView的性質(zhì)決定了其比較適合一些場(chǎng)景:需要界面迅速更新、對(duì)幀率要求較高的情況。使用SurfaceView需要注意以下幾點(diǎn)情況:
SurfaceView和SurfaceHolder.Callback函數(shù)都從當(dāng)前SurfaceView窗口線(xiàn)程中調(diào)用(一般而言就是程序的主線(xiàn)程)。有關(guān)資源狀態(tài)要注意和繪制線(xiàn)程之間的同步。
在繪制線(xiàn)程中必須先合法的獲取Surface才能開(kāi)始繪制內(nèi)容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間的狀態(tài)為合法的,另外在Surface類(lèi)型為SURFACE_TYPE_PUSH_BUFFERS時(shí)候是不合法的。
額外的繪制線(xiàn)程會(huì)消耗系統(tǒng)的資源,在使用SurfaceView的時(shí)候要注意這點(diǎn)。


2. SurfaceView的使用
首先繼承SurfaceView,并實(shí)現(xiàn)SurfaceHolder.Callback接口,實(shí)現(xiàn)它的三個(gè)方法:surfaceCreated,surfaceChanged,surfaceDestroyed。
(1)surfaceCreated(SurfaceHolder holder):surface創(chuàng)建的時(shí)候調(diào)用,一般在該方法中啟動(dòng)繪圖的線(xiàn)程。
(2)surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸發(fā)生改變的時(shí)候調(diào)用,如橫豎屏切換。
(3)surfaceDestroyed(SurfaceHolder holder) :surface被銷(xiāo)毀的時(shí)候調(diào)用,如退出游戲畫(huà)面,一般在該方法中停止繪圖線(xiàn)程。
還需要獲得SurfaceHolder,并添加回調(diào)函數(shù),這樣這三個(gè)方法才會(huì)執(zhí)行。
只要繼承SurfaceView類(lèi)并實(shí)現(xiàn)SurfaceHolder.Callback接口就可以實(shí)現(xiàn)一個(gè)自定義的SurfaceView了,SurfaceHolder.Callback在底層的Surface狀態(tài)發(fā)生變化的時(shí)候通知View,SurfaceHolder.Callback具有如下的接口:
(1)surfaceCreated(SurfaceHolder holder):當(dāng)Surface第一次創(chuàng)建后會(huì)立即調(diào)用該函數(shù)。程序可以在該函數(shù)中做些和繪制界面相關(guān)的初始化工作,一般情況下都是在另外的線(xiàn)程來(lái)繪制界面,所以不要在這個(gè)函數(shù)中繪制Surface。
(2)surfaceChanged(SurfaceHolder holder, int format, int width,int height):當(dāng)Surface的狀態(tài)(大小和格式)發(fā)生變化的時(shí)候會(huì)調(diào)用該函數(shù),在surfaceCreated調(diào)用后該函數(shù)至少會(huì)被調(diào)用一次。
(3)surfaceDestroyed(SurfaceHolder holder):當(dāng)Surface被摧毀前會(huì)調(diào)用該函數(shù),該函數(shù)被調(diào)用后就不能繼續(xù)使用Surface了,一般在該函數(shù)中來(lái)清理使用的資源。
通過(guò)SurfaceView的getHolder()函數(shù)可以獲取SurfaceHolder對(duì)象,Surface 就在SurfaceHolder對(duì)象內(nèi)。雖然Surface保存了當(dāng)前窗口的像素?cái)?shù)據(jù),但是在使用過(guò)程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或則Canvas lockCanvas(Rect dirty)函數(shù)來(lái)獲取Canvas對(duì)象,通過(guò)在Canvas上繪制內(nèi)容來(lái)修改Surface中的數(shù)據(jù)。如果Surface不可編輯或則尚未創(chuàng)建調(diào)用該函數(shù)會(huì)返回null,在 unlockCanvas() 和 lockCanvas()中Surface的內(nèi)容是不緩存的,所以需要完全重繪Surface的內(nèi)容,為了提高效率只重繪變化的部分則可以調(diào)用lockCanvas(Rect dirty)函數(shù)來(lái)指定一個(gè)dirty區(qū)域,這樣該區(qū)域外的內(nèi)容會(huì)緩存起來(lái)。在調(diào)用lockCanvas函數(shù)獲取Canvas后,SurfaceView會(huì)獲取Surface的一個(gè)同步鎖直到調(diào)用unlockCanvasAndPost(Canvas canvas)函數(shù)才釋放該鎖,這里的同步機(jī)制保證在Surface繪制過(guò)程中不會(huì)被改變(被摧毀、修改)。
當(dāng)在Canvas中繪制完成后,調(diào)用函數(shù)unlockCanvasAndPost(Canvas canvas)來(lái)通知系統(tǒng)Surface已經(jīng)繪制完成,這樣系統(tǒng)會(huì)把繪制完的內(nèi)容顯示出來(lái)。為了充分利用不同平臺(tái)的資源,發(fā)揮平臺(tái)的最優(yōu)效果可以通過(guò)SurfaceHolder的setType函數(shù)來(lái)設(shè)置繪制的類(lèi)型,目前接收如下的參數(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ù)由其他對(duì)象提供,在Camera圖像預(yù)覽中就使用該類(lèi)型的Surface,有Camera負(fù)責(zé)提供給預(yù)覽Surface數(shù)據(jù),這樣圖像預(yù)覽會(huì)比較流暢。如果設(shè)置這種類(lèi)型則就不能調(diào)用lockCanvas來(lái)獲取Canvas對(duì)象了。
訪(fǎng)問(wèn)SurfaceView的底層圖形是通過(guò)SurfaceHolder接口來(lái)實(shí)現(xiàn)的,通過(guò)getHolder()方法可以得到這個(gè)SurfaceHolder對(duì)象。你應(yīng)該實(shí)現(xiàn)surfaceCreated(SurfaceHolder)和surfaceDestroyed(SurfaceHolder)方法來(lái)知道在這個(gè)Surface在窗口的顯示和隱藏過(guò)程中是什么時(shí)候創(chuàng)建和銷(xiāo)毀的。

注意:一個(gè)SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()調(diào)用之間是可用的,其他時(shí)間是得不到它的Canvas對(duì)象的(null)。
3. SurfaceView實(shí)戰(zhàn)
下面通過(guò)一個(gè)小demo來(lái)學(xué)習(xí)SurfaceView在實(shí)際項(xiàng)目中的使用,繪制一個(gè)精靈,該精靈有四個(gè)方向的行走動(dòng)畫(huà),讓精靈沿著屏幕四周不停的行走。游戲中精靈素材和最終實(shí)現(xiàn)的效果圖:

2016428160517972.png (330×250)

2016428160544089.png (338×177)

首先創(chuàng)建核心類(lèi)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中包含了一個(gè)繪圖線(xiàn)程DrawThread,在線(xiàn)程的run方法中鎖定Canvas、繪制精靈、更新精靈位置、釋放Canvas等操作。因?yàn)榫`素材是一張大圖,所以這里進(jìn)行了裁剪生成一個(gè)二維數(shù)組。使用這個(gè)二維數(shù)組初始化了精靈四個(gè)方向的動(dòng)畫(huà),下面看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;
  //精靈四個(gè)方向的動(dòng)畫(huà)
  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:
      //讓物體的移動(dòng)速度不受機(jī)器性能的影響,每幀精靈需要移動(dòng)的距離為:移動(dòng)速度*時(shí)間間隔
      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);
    }
  }
}

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

public class FrameAnimation{
  /**動(dòng)畫(huà)顯示的需要的資源 */
  private Bitmap[] bitmaps;
  /**動(dòng)畫(huà)每幀顯示的時(shí)間 */
  private int[] duration;
  /**動(dòng)畫(huà)上一幀顯示的時(shí)間 */
  protected Long lastBitmapTime;
  /**動(dòng)畫(huà)顯示的索引值,防止數(shù)組越界 */
  protected int step;
  /**動(dòng)畫(huà)是否重復(fù)播放 */
  protected boolean repeat;
  /**動(dòng)畫(huà)重復(fù)播放的次數(shù)*/
  protected int repeatCount;
 
  /**
   * @param bitmap:顯示的圖片<br/>
   * @param duration:圖片顯示的時(shí)間<br/>
   * @param repeat:是否重復(fù)動(dòng)畫(huà)過(guò)程<br/>
   */
  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) {
      //如果不無(wú)限循環(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的時(shí)間段內(nèi),則繼續(xù)返回當(dāng)前Bitmap
      // 如果duration的值小于0,則表明永遠(yuǎn)不失效,一般用于背景
      return bitmaps[step];
    }
    lastBitmapTime = nowTime;
    return bitmaps[step++];// 返回下一Bitmap
  }
 
}

FrameAnimation根據(jù)每一幀的顯示時(shí)間返回當(dāng)前的圖片幀,若沒(méi)有超過(guò)指定的時(shí)間則繼續(xù)返回當(dāng)前幀,否則返回下一幀。
接下來(lái)需要做的是讓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)該就可以看到一個(gè)手持寶劍的武士在沿著屏幕不停的走了。

相關(guān)文章

最新評(píng)論