Android开发之对上下两个图层的操作
/*
* Android开发之对上下两个图层的操作
* 北京Android俱乐部群:167839253
* Created on: 2012-7-17
* Author: blueeagle
* Email:liujiaxiang@gmail.com
*/
我们在玩“美女脱衣服”游戏中,看到的可以把美女身上的衣服脱掉,其实是运用了图层的技术。其根本还是两张图片,将上层图片来依据手的触摸使上层图层消失。可以理解为,上层图层是一个View,下层可以是View的一个背景。新建一个View类,myView
这样去写他的构造函数:
public myView(Context context) {
super(context);
setFocusable(true);
setScreenWH();
setBackgroundResource(R.drawable.back);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.background);
setUpBmp(bmp);
}
注释:
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 myMetrics=new DisplayMetrics();
myMetrics =this.getResources().getDisplayMetrics();
//getWindowManager().getDefaultDisplay().getMetrics(myMetrics);
两种方式来给myMetrics值。
注:构造函数DisplayMetrics不需要传递任何参数;调用getWindowManager()之后,会取得现有Activity的Handle,此时,getDefaultDisplay()方法将取得的宽高维度存放于DisplayMetrics对象中,而取得的宽高维度是以像素为单位(Pixel),“像素”所指的是“绝对像素”而非“相对像素”。
setUpBmp(bmp);函数就是设置已经获取到的bmp为上层图像。
在setUpBmp(bmp);函数中,进行以下操作:
private void setUpBmp(Bitmap bmp) {
// TODO Auto-generatedmethod stub
myPaint = new Paint();
myPaint.setAlpha(0);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
myPaint.setAntiAlias(true);
myPaint.setDither(true);
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeCap(Paint.Cap.ROUND);
myPaint.setStrokeJoin(Paint.Join.ROUND);
myPaint.setStrokeWidth(20);
// 设置路径
myPath = new Path();
myBitmap = Bitmap.createBitmap(SCREENW, SCREENH,Config.ARGB_8888);
myCanvas = new Canvas();
myCanvas.setBitmap(myBitmap);
myCanvas.drawBitmap(bmp, 0, 0,null);
}
注释:
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 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。
要应用转换模式,可以使用setXferMode方法
对应的模式:
PorterDuff.Mode | ADD | Saturate(S + D) |
PorterDuff.Mode | CLEAR | [0, 0] |
DARKEN | [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] | |
DST | [Da, Dc] | |
DST_ATOP | [Sa, Sa * Dc + Sc * (1 - Da)] | |
DST_IN | [Sa * Da, Sa * Dc] | |
DST_OUT | [Da * (1 - Sa), Dc * (1 - Sa)] | |
DST_OVER | [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] | |
LIGHTEN | [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] | |
MULTIPLY | [Sa * Da, Sc * Dc] | |
OVERLAY |
| |
SCREEN | [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] | |
SRC | [Sa, Sc] | |
SRC_ATOP | [Da, Sc * Da + (1 - Sa) * Dc] | |
SRC_IN | [Sa * Da, Sc * Da] | |
SRC_OUT | [Sa * (1 - Da), Sc * (1 - Da)] | |
SRC_OVER | [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] | |
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)来解决, setDither是TextureNode的方法.
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的实现:
protected void onDraw(Canvas canvas){
canvas.drawBitmap(myBitmap, 0, 0,null);
myCanvas.drawPath(myPath, myPaint);
super.onDraw(canvas);
}
运动轨迹算法:
private void touch_start(float x, float y){
myPath.reset();
myPath.moveTo(x, y);
myX=x;
myY=y;
}
private void touch_move(float x,float y){
float dx = Math.abs(x-myX);
float dy = Math.abs(y-myY);
if(dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE ){
myPath.quadTo(myX, myY, (x+myX)/2, (y+myY)/2);
myX=x;
myY=y;
}
}
private void touch_up(){
myPath.lineTo(myX, myY);
myCanvas.drawPath(myPath,myPaint);
myPath.reset();
}
触摸重绘:
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touch_start(x,y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
整个代码:
package com.blueeagle.www;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
public class DuotucengActivity extends Activity {
private int SCREENW;
private int SCREENH;
/** Calledwhen the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new myView(this));
}
public class myView extends View {
private Bitmap myBitmap;
private Canvas myCanvas;
private Paint myPaint;
private Path myPath;
private float myX,myY;
private static final float TOUCH_TOLERANCE = 4;
public myView(Context context) {
super(context);
setFocusable(true);
setScreenWH();
setBackgroundResource(R.drawable.back);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.background);
setUpBmp(bmp);
}
private void setUpBmp(Bitmap bmp) {
// TODO Auto-generatedmethod stub
myPaint = new Paint();
myPaint.setAlpha(0);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
myPaint.setAntiAlias(true);
myPaint.setDither(true);
myPaint.setStyle(Paint.Style.STROKE);
//myPaint.setStrokeCap(Paint.Cap.ROUND);
myPaint.setStrokeJoin(Paint.Join.ROUND);
myPaint.setStrokeWidth(20);
// 设置路径
myPath = new Path();
myBitmap = Bitmap.createBitmap(SCREENW, SCREENH,Config.ARGB_8888);
myCanvas = new Canvas();
myCanvas.setBitmap(myBitmap);
myCanvas.drawBitmap(bmp, 0, 0,null);
}
private void setScreenWH() {
// TODO Auto-generatedmethod stub
DisplayMetrics dm = new DisplayMetrics();
//dm =this.getResources().getDisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
SCREENW = screenWidth;
SCREENH = screenHeight;
}
protected void onDraw(Canvas canvas){
canvas.drawBitmap(myBitmap, 0, 0,null);
myCanvas.drawPath(myPath, myPaint);
super.onDraw(canvas);
}
private void touch_start(float x, float y){
myPath.reset();
myPath.moveTo(x, y);
myX=x;
myY=y;
}
private void touch_move(float x,float y){
float dx = Math.abs(x-myX);
float dy = Math.abs(y-myY);
if(dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE ){
myPath.quadTo(myX, myY, (x+myX)/2, (y+myY)/2);
myX=x;
myY=y;
}
}
private void touch_up(){
myPath.lineTo(myX, myY);
myCanvas.drawPath(myPath,myPaint);
myPath.reset();
}
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touch_start(x,y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}