Android点击屏幕出现水波纹效果

在http://blog.csdn.net/poison_h/article/details/43635955这篇文章,介绍了如何给按钮设置水波纹效果。

可能有人会想到可不可以点击背景的时候也出现水波纹效果呢?其实当时我也是这样想的。没有想到Google下,还真的找到了,又一次站在了巨人的肩上。虽然是站在巨人的肩上,但是也要自己懂得才行,要转变自己得知识才是最棒的

好了,先看下效果图:



1.首先我们需要重写SurfaceView:

SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
        surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
        你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。
        surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
        surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
        1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
        2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

(1)、第一步在onSizeChanged()方法中,设置背景图片,并且根据背景图片设置两个波能缓冲区大小,并初始化。

// 背景图
			bgImage = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
			bgImage = Bitmap.createScaledBitmap(bgImage, w, h, false);// 缩放而已
			backWidth = bgImage.getWidth();
			backHeight = bgImage.getHeight();

			buf2 = new short[backWidth * backHeight];
			buf1 = new short[backWidth * backHeight];

			bitmap2 = new int[backWidth * backHeight];
			bitmap1 = new int[backWidth * backHeight];

			// 将bgImage的像素拷贝到bitmap1数组中,用于渲染。。。
			bgImage.getPixels(bitmap1, 0, backWidth, 0, 0, backWidth, backHeight);
			bgImage.getPixels(bitmap2, 0, backWidth, 0, 0, backWidth, backHeight);

			for (int i = 0; i < backWidth * backHeight; ++i)
			{
				buf2[i] = 0;
				buf1[i] = 0;
			}

2.计算波能数据缓冲区

while (i < loopTime)
		{

			// 波能扩散
			buf2[k] = (short) (((buf1[k - 2] + buf1[k + 2] + buf1[k - doubleWidth] + buf1[k + doubleWidth]) >> 1) - buf2[k]);

			// 波能衰减
			buf2[k] = (short) (buf2[k] - (buf2[k] >> 5));

			// 求出该点的左上的那个点xoff,yoff
			cp = k - doubleWidth - 2;

			xoff = buf2[cp - 2] - buf2[cp + 2];
			yoff = buf2[cp - doubleWidth] - buf2[k - 2];

			tarClr = k + yoff * doubleWidth + xoff;
			if (tarClr > bitmapLen || tarClr < 0)
			{
				k += 2;
				continue;
			}
			// 复制象素
			bitmap2[k] = bitmap1[tarClr];
			k += 2;
			++i;
		}

3.最后分别对move和down事件分别进行填充不同的波能缓冲池

// 产生波源,填充前导波能缓冲池
		int endStoneX = x + stonesize;
		int endStoneY = y + stonesize;
		int squaSize = stonesize * stonesize;
		int posy = y - stonesize;
		int posx = x - stonesize;
		for (posy = y - stonesize; posy < endStoneY; ++posy)
		{
			for (posx = x - stonesize; posx < endStoneX; ++posx)
			{
				if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < squaSize)
				{
					buf1[backWidth * posy + posx] = (short) -stoneweight;
				}
			}
		}


详细代码:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class WaterWaveView extends SurfaceView implements SurfaceHolder.Callback
{
	// 背景图的宽度和高度

	private int backWidth;

	private int backHeight;

	/**
	 * buf1 和 buf2是波能缓冲区,分别代表了每个点的前一时刻和后一时刻的波幅数据
	 */
	private short[] buf1;
	private short[] buf2;

	private int[] bitmap1;
	private int[] bitmap2;
	private Bitmap bgImage = null;
	// 是否第一次加载
	private boolean firstLoad = false;

	WavingThread wavingThread = new WavingThread();
	// 显示一个surface的抽象接口,使你可以控制surface的大小和格式, 以及在surface上编辑像素,和监视surace的改变
	SurfaceHolder mSurfaceHolder = null;

	private int doubleWidth;

	private int fiveWidth;
	// 持续时间
	private int loopTime;

	private int bitmapLen;

	public WaterWaveView(Context context)
	{
		super(context);
		mSurfaceHolder = getHolder();
		mSurfaceHolder.addCallback(this);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh)
	{

		if (!firstLoad)
		{
			// 背景图
			bgImage = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
			bgImage = Bitmap.createScaledBitmap(bgImage, w, h, false);// 缩放而已
			backWidth = bgImage.getWidth();
			backHeight = bgImage.getHeight();

			buf2 = new short[backWidth * backHeight];
			buf1 = new short[backWidth * backHeight];

			bitmap2 = new int[backWidth * backHeight];
			bitmap1 = new int[backWidth * backHeight];

			// 将bgImage的像素拷贝到bitmap1数组中,用于渲染。。。
			bgImage.getPixels(bitmap1, 0, backWidth, 0, 0, backWidth, backHeight);
			bgImage.getPixels(bitmap2, 0, backWidth, 0, 0, backWidth, backHeight);

			for (int i = 0; i < backWidth * backHeight; ++i)
			{
				buf2[i] = 0;
				buf1[i] = 0;
			}
			doubleWidth = backWidth << 1;

			fiveWidth = 5 * backWidth;

			loopTime = ((backHeight - 4) * backWidth) >> 1;

			bitmapLen = backWidth * backHeight - 1;

			firstLoad = true;
		}

	}

	class WavingThread extends Thread
	{
		boolean running = true;

		public void setRunning(boolean running)
		{
			this.running = running;
		}

		@Override
		public void run()
		{
			Canvas c = null;
			while (running)
			{
				c = mSurfaceHolder.lockCanvas();
				makeRipple();
				doDraw(c);
				mSurfaceHolder.unlockCanvasAndPost(c);
			}
		}
	}

	/*******************************************************
	 * 计算波能数据缓冲区
	 *******************************************************/
	private void makeRipple()
	{
		int k = fiveWidth;
		int xoff = 0, yoff = 0;
		int cp = 0;

		int tarClr = 0;
		int i = fiveWidth;
		while (i < loopTime)
		{

			// 波能扩散
			buf2[k] = (short) (((buf1[k - 2] + buf1[k + 2] + buf1[k - doubleWidth] + buf1[k + doubleWidth]) >> 1) - buf2[k]);

			// 波能衰减
			buf2[k] = (short) (buf2[k] - (buf2[k] >> 5));

			// 求出该点的左上的那个点xoff,yoff
			cp = k - doubleWidth - 2;

			xoff = buf2[cp - 2] - buf2[cp + 2];
			yoff = buf2[cp - doubleWidth] - buf2[k - 2];

			tarClr = k + yoff * doubleWidth + xoff;
			if (tarClr > bitmapLen || tarClr < 0)
			{
				k += 2;
				continue;
			}
			// 复制象素
			bitmap2[k] = bitmap1[tarClr];
			k += 2;
			++i;
		}

		short[] tmpBuf = buf2;
		buf2 = buf1;
		buf1 = tmpBuf;

	}

	/*****************************************************
	 * 增加波源 x坐标 y坐标 波源半径 波源能量
	 *****************************************************/
	private void touchWater(int x, int y, int stonesize, int stoneweight)
	{
		// 判断坐标是否在屏幕范围内
		if (x + stonesize > backWidth)
		{
			return;
		}
		if (y + stonesize > backHeight)
		{
			return;
		}
		if (x - stonesize < 0)
		{
			return;
		}
		if (y - stonesize < 0)
		{
			return;
		}
		// 产生波源,填充前导波能缓冲池
		int endStoneX = x + stonesize;
		int endStoneY = y + stonesize;
		int squaSize = stonesize * stonesize;
		int posy = y - stonesize;
		int posx = x - stonesize;
		for (posy = y - stonesize; posy < endStoneY; ++posy)
		{
			for (posx = x - stonesize; posx < endStoneX; ++posx)
			{
				if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < squaSize)
				{
					buf1[backWidth * posy + posx] = (short) -stoneweight;
				}
			}
		}

	}

	/*****************************************************
	 * 增加波源 x坐标 y坐标 波源半径 波源能量
	 *****************************************************/
	private void trickWater(int x, int y, int stonesize, int stoneweight)
	{

		// 判断坐标是否在屏幕范围内
		if (x + stonesize > backWidth)
		{
			return;
		}
		if (y + stonesize > backHeight)
		{
			return;
		}
		if (x - stonesize < 0)
		{
			return;
		}
		if (y - stonesize < 0)
		{
			return;
		}

		// 产生波源,填充波能缓冲池
		int endStoneX = x + stonesize;
		int endStoneY = y + stonesize;
		int posy = y - stonesize;
		int posx = x - stonesize;
		for (posy = y - stonesize; posy < endStoneY; ++posy)
		{
			for (posx = x - stonesize; posx < endStoneX; ++posx)
			{
				if (posy >= 0 && posy < backHeight && posx >= 0 && posx < backWidth)
				{
					buf1[backWidth * posy + posx] = (short) -stoneweight;
				}
			}
		}

	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		if (event.getAction() == MotionEvent.ACTION_DOWN)
		{
			// 设置波源半径和波源能量
			touchWater((int) event.getX(), (int) event.getY(), 4, 160);
		} else if (event.getAction() == MotionEvent.ACTION_MOVE)
		{
			trickWater((int) event.getX(), (int) event.getY(), 2, 64);
		}
		return true;
	}

	protected void doDraw(Canvas canvas)
	{
		/**
		 * Parameters: 1.colors 2.offset 3.stride 4.x 5. y 6.width 7.height
		 * 8.hasAlpha 9. paint
		 */
		//绘制
		canvas.drawBitmap(bitmap2, 0, backWidth, 0, 0, backWidth, backHeight, false, null);

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder)
	{
		wavingThread.setRunning(true);
		wavingThread.start();

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		boolean retry = true;
		wavingThread.setRunning(false);
		// 非暴力关闭线程,直到此次该线程运行结束之前,主线程停止运行,以防止Surface被重新激活
		while (retry)
		{
			try
			{
				wavingThread.join(); // 阻塞current
										// Thread(当前执行线程)直到被调用线程(thread)完成。
				retry = false;
			} catch (InterruptedException e)
			{
			}
		}

	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
	{
		// TODO Auto-generated method stub

	}

}



最后在我MainActivity中调用:

waterWaveView = new WaterWaveView(this);
setContentView(waterWaveView);




  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值