Android開發之對上下兩個圖層的操作

我們在玩“美女脫衣服”遊戲中,看到的可以把美女身上的衣服脫掉,其實是運用了圖層的技術。其根本還是兩張圖片,將上層圖片來依據手的觸摸使上層圖層消失。可以理解爲,上層圖層是一個View,下層可以是View的一個背景。新建一個View類,myView

這樣去寫他的構造函數:

[java] view plaincopyprint?
  1. public myView(Context context) {
  2. super(context);
  3. setFocusable(true);
  4. setScreenWH();
  5. setBackgroundResource(R.drawable.back);
  6. Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.background);
  7. setUpBmp(bmp);
  8. }

註釋:

public void setFocusable (booleanfocusable)

Set whether this view can receivethe focus. Setting this to false will also ensure that this view is notfocusable in touch mode.

public void setBackgroundResource (intresid)

Set the background to a givenresource. The resource should refer to a Drawable object or 0 to remove thebackground.

setScreenWH();函數用來獲得屏幕長寬像素。

public DisplayMetrics getDisplayMetrics ()

Return the current display metricsthat are in effect for this resource object. The returned object should betreated as read-only.

注意:Android可設置爲隨着窗口大小調整縮放比例,但即便如此,手機程序設計人員還是必須知道手機屏幕的邊界,以避免縮放造成的佈局變形問題。

手機的分辨率信息是手機的一項重要信息,很好的是,Android已經提供DisplayMetircs類可以很方便的獲取分辨率。

Andorid.util包下的DisplayMetrics類提供了一種關於顯示的通用信息,如顯示大小,分辨率和字體。

爲了獲取DisplayMetrics成員,首先初始化一個對象如下:

DisplayMetrics myMetricsnew DisplayMetrics();

myMetrics =this.getResources().getDisplayMetrics();

//getWindowManager().getDefaultDisplay().getMetrics(myMetrics);

兩種方式來給myMetrics值。

注:構造函數DisplayMetrics不需要傳遞任何參數;調用getWindowManager()之後,會取得現有ActivityHandle,此時,getDefaultDisplay()方法將取得的寬高維度存放於DisplayMetrics對象中,而取得的寬高維度是以像素爲單位(Pixel)像素所指的是絕對像素而非相對像素

setUpBmp(bmp);函數就是設置已經獲取到的bmp爲上層圖像。

在setUpBmp(bmp);函數中,進行以下操作:

[java] view plaincopyprint?
  1. private void setUpBmp(Bitmap bmp) {
  2. // TODO Auto-generatedmethod stub
  3. myPaint = new Paint();
  4. myPaint.setAlpha(0);
  5. myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
  6. myPaint.setAntiAlias(true);
  7. myPaint.setDither(true);
  8. myPaint.setStyle(Paint.Style.STROKE);
  9. myPaint.setStrokeCap(Paint.Cap.ROUND);
  10. myPaint.setStrokeJoin(Paint.Join.ROUND);
  11. myPaint.setStrokeWidth(20);
  12. // 設置路徑
  13. myPath = new Path();
  14. myBitmap = Bitmap.createBitmap(SCREENW, SCREENH,Config.ARGB_8888);
  15. myCanvas = new Canvas();
  16. myCanvas.setBitmap(myBitmap);
  17. myCanvas.drawBitmap(bmp, 0, 0,null);
  18. }

註釋:

public XfermodesetXfermode (Xfermodexfermode)

Set or clear the xfermode object.

Pass null to clear any previousxfermode. As a convenience, the parameter passed is also returned.

public PorterDuffXfermode (PorterDuff.Modemode)

Create an xfermode that uses thespecified porter-duff mode.

Xfermode有三個子類,分別如下:

AvoidXfermode 指定了一個顏色和容差,強制Paint避免在它上面繪圖(或者只在它上面繪圖)

PixelXorXfermode 當覆蓋已有的顏色時,應用一個簡單的像素XOR操作。

PorterDuffXfermode 這是一個非常強大的轉換模式,使用它,可以使用圖像合成的16Porter-Duff規則的任意一條來控制Paint如何與已有的Canvas圖像進行交互。

要應用轉換模式,可以使用setXferMode方法

對應的模式:

PorterDuff.Mode

ADD

Saturate(S + D)

PorterDuff.Mode

CLEAR

[0, 0]

PorterDuff.Mode

DARKEN

[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]

PorterDuff.Mode

DST

[Da, Dc]

PorterDuff.Mode

DST_ATOP

[Sa, Sa * Dc + Sc * (1 - Da)]

PorterDuff.Mode

DST_IN

[Sa * Da, Sa * Dc]

PorterDuff.Mode

DST_OUT

[Da * (1 - Sa), Dc * (1 - Sa)]

PorterDuff.Mode

DST_OVER

[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc]

PorterDuff.Mode

LIGHTEN

[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]

PorterDuff.Mode

MULTIPLY

[Sa * Da, Sc * Dc]

PorterDuff.Mode

OVERLAY

PorterDuff.Mode

SCREEN

[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]

PorterDuff.Mode

SRC

[Sa, Sc]

PorterDuff.Mode

SRC_ATOP

[Da, Sc * Da + (1 - Sa) * Dc]

PorterDuff.Mode

SRC_IN

[Sa * Da, Sc * Da]

PorterDuff.Mode

SRC_OUT

[Sa * (1 - Da), Sc * (1 - Da)]

PorterDuff.Mode

SRC_OVER

[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]

PorterDuff.Mode

XOR

[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]

1.PorterDuff.Mode.CLEAR

所繪製不會提交到畫布上。
2.PorterDuff.Mode.SRC

顯示上層繪製圖片
3.PorterDuff.Mode.DST

顯示下層繪製圖片
4.PorterDuff.Mode.SRC_OVER

正常繪製顯示,上下層繪製疊蓋。
5.PorterDuff.Mode.DST_OVER

上下層都顯示。下層居上顯示。
6.PorterDuff.Mode.SRC_IN

取兩層繪製交集。顯示上層。
7.PorterDuff.Mode.DST_IN

取兩層繪製交集。顯示下層。
8.PorterDuff.Mode.SRC_OUT

取上層繪製非交集部分。
9.PorterDuff.Mode.DST_OUT

取下層繪製非交集部分。
10.PorterDuff.Mode.SRC_ATOP

取下層非交集部分與上層交集部分
11.PorterDuff.Mode.DST_ATOP

取上層非交集部分與下層交集部分
12.PorterDuff.Mode.XOR


13.PorterDuff.Mode.DARKEN


14.PorterDuff.Mode.LIGHTEN


15.PorterDuff.Mode.MULTIPLY


16.PorterDuff.Mode.SCREEN

效果圖:【成都安卓培訓


myPaint.setDither(true);

有時候你可能發現設置了背景圖片之後,在屏幕上的效果不太理想,主要表現爲顏色過渡不平滑,色塊現象比較嚴重. 主要原因是因爲WiEngine底層設置OpenGL的緩衝區格式缺省是RGB565,這個設置可以提高速度,但是很顯然顏色精度會受到影響.這個時候可以用setDither(true)來解決, setDitherTextureNode的方法.

Sprite bg= ...;

bg.setDither(true);

Dither的意思是抖動,是一種用有限顏色模擬其它顏色的方式,比如將白色和紅色均勻的混合,你會看到粉紅色. 通過這種方式, 可以消除背景上的色塊,使顏色過渡平滑. 這種方式當然會損失一點性能, 但是基本也就是背景用一下, 問題不大.

WYGLSurfaceView支持透明背景,這種模式下OpenGL使用RGBA8888格式的緩衝區,所以不需要抖動背景圖片也將顯示的很好.使用透明背景的WYGLSurfaceView很簡單:

WYGLSurfaceViewv=new WYGLSurfaceView(this,true);//第二個參數傳true表示背景透明

setContentView(v);

遊戲需要用透明背景的不多,一般透明背景WYGLSurfaceView可以用在增強現實的應用中

myPaint.setStrokeCap(Paint.Cap.ROUND);

設置筆帽的樣子。

myPaint.setStrokeJoin(Paint.Join.ROUND);

Paint.setStrokeJoin(Joinjoin)這裏的Join參數爲設置結合處的樣子,Miter:結合處爲銳角, Round:結合處爲圓弧:BEVEL:結合處爲直線。

OnDraw的實現:

[java] view plaincopyprint?
  1. protected void onDraw(Canvas canvas){
  2. canvas.drawBitmap(myBitmap, 0, 0,null);
  3. myCanvas.drawPath(myPath, myPaint);
  4. super.onDraw(canvas);
  5. }

運動軌跡算法:

[java] view plaincopyprint?
  1. private void touch_start(float x, float y){
  2. myPath.reset();
  3. myPath.moveTo(x, y);
  4. myX=x;
  5. myY=y;
  6. }
  7. private void touch_move(float x,float y){
  8. float dx = Math.abs(x-myX);
  9. float dy = Math.abs(y-myY);
  10. if(dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE ){
  11. myPath.quadTo(myX, myY, (x+myX)/2, (y+myY)/2);
  12. myX=x;
  13. myY=y;
  14. }
  15. }
  16. private void touch_up(){
  17. myPath.lineTo(myX, myY);
  18. myCanvas.drawPath(myPath,myPaint);
  19. myPath.reset();
  20. }

觸摸重繪:

[java] view plaincopyprint?
  1. public boolean onTouchEvent(MotionEvent event){
  2. float x = event.getX();
  3. float y = event.getY();
  4. switch(event.getAction()){
  5. case MotionEvent.ACTION_DOWN:
  6. touch_start(x,y);
  7. invalidate();
  8. break;
  9. case MotionEvent.ACTION_MOVE:
  10. touch_move(x,y);
  11. invalidate();
  12. break;
  13. case MotionEvent.ACTION_UP:
  14. touch_up();
  15. invalidate();
  16. break;
  17. }
  18. return true;
  19. }

整個代碼:

[java] view plaincopyprint?
  1. package com.blueeagle.www;
  2. import android.app.Activity;
  3. import android.content.Context;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Canvas;
  7. import android.graphics.Paint;
  8. import android.graphics.Path;
  9. import android.graphics.PorterDuff;
  10. import android.graphics.PorterDuffXfermode;
  11. import android.graphics.Bitmap.Config;
  12. import android.os.Bundle;
  13. import android.util.DisplayMetrics;
  14. import android.view.MotionEvent;
  15. import android.view.View;

  16. public class DuotucengActivity extends Activity {
  17. private int SCREENW;
  18. private int SCREENH;
  19. /** Calledwhen the activity is first created. */
  20. @Override
  21. public void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(new myView(this));
  24. }
  25. public class myView extends View {

  26. private Bitmap myBitmap;
  27. private Canvas myCanvas;
  28. private Paint myPaint;
  29. private Path myPath;
  30. private float myX,myY;
  31. private static final float TOUCH_TOLERANCE = 4;

  32. public myView(Context context) {
  33. super(context);
  34. setFocusable(true);
  35. setScreenWH();
  36. setBackgroundResource(R.drawable.back);
  37. Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.background);
  38. setUpBmp(bmp);
  39. }

  40. private void setUpBmp(Bitmap bmp) {
  41. // TODO Auto-generatedmethod stub
  42. myPaint = new Paint();
  43. myPaint.setAlpha(0);
  44. myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
  45. myPaint.setAntiAlias(true);
  46. myPaint.setDither(true);
  47. myPaint.setStyle(Paint.Style.STROKE);
  48. //myPaint.setStrokeCap(Paint.Cap.ROUND);
  49. myPaint.setStrokeJoin(Paint.Join.ROUND);
  50. myPaint.setStrokeWidth(20);
  51. // 設置路徑
  52. myPath = new Path();
  53. myBitmap = Bitmap.createBitmap(SCREENW, SCREENH,Config.ARGB_8888);
  54. myCanvas = new Canvas();
  55. myCanvas.setBitmap(myBitmap);
  56. myCanvas.drawBitmap(bmp, 0, 0,null);
  57. }

  58. private void setScreenWH() {
  59. // TODO Auto-generatedmethod stub
  60. DisplayMetrics dm = new DisplayMetrics();
  61. //dm =this.getResources().getDisplayMetrics();
  62. getWindowManager().getDefaultDisplay().getMetrics(dm);
  63. int screenWidth = dm.widthPixels;
  64. int screenHeight = dm.heightPixels;
  65. SCREENW = screenWidth;
  66. SCREENH = screenHeight;
  67. }

  68. protected void onDraw(Canvas canvas){
  69. canvas.drawBitmap(myBitmap, 0, 0,null);
  70. myCanvas.drawPath(myPath, myPaint);
  71. super.onDraw(canvas);
  72. }

  73. private void touch_start(float x, float y){
  74. myPath.reset();
  75. myPath.moveTo(x, y);
  76. myX=x;
  77. myY=y;
  78. }
  79. private void touch_move(float x,float y){
  80. float dx = Math.abs(x-myX);
  81. float dy = Math.abs(y-myY);
  82. if(dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE ){
  83. myPath.quadTo(myX, myY, (x+myX)/2, (y+myY)/2);
  84. myX=x;
  85. myY=y;
  86. }
  87. }
  88. private void touch_up(){
  89. myPath.lineTo(myX, myY);
  90. myCanvas.drawPath(myPath,myPaint);
  91. myPath.reset();
  92. }
  93. public boolean onTouchEvent(MotionEvent event){
  94. float x = event.getX();
  95. float y = event.getY();
  96. switch(event.getAction()){
  97. case MotionEvent.ACTION_DOWN:
  98. touch_start(x,y);
  99. invalidate();
  100. break;
  101. case MotionEvent.ACTION_MOVE:
  102. touch_move(x,y);
  103. invalidate();
  104. break;
  105. case MotionEvent.ACTION_UP:
  106. touch_up();
  107. invalidate();
  108. break;
  109. }
  110. return true;
  111. }

  112. }
  113. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章