此应用的代码地址在我的github
此应用已上线,下载地址http://shouji.baidu.com/software/23371524.html
最近看了一个水波纹效果的案例视频,大家可以看看。这个案例实现的效果就是点击或滑动屏幕,在点击和滑动过的地方就会画圆,同时画的圆会慢慢变小,慢慢变淡,直至消失。看着挺好看也挺好玩,然而前一阵子又发布了一个透明壁纸的博客。所以就想能不能把这个水波纹弄到壁纸上,于是这个水印壁纸就诞生了。
因为是新手,所以遇到了很多问题,有些甚至是很基础的问题,但是好在百度了一大堆,基本问题都解决了,总的来说自己的基础还是太差了。
好了看代码,首先要先做壁纸,壁纸的制作可以看我的上一个博客手机透明壁纸,这里就不细说了。
接下来就是在壁纸服务里加入画图的操作。
Handler mHandler = new Handler();// 重复执行的一个方法private final Runnable drawTarget = new Runnable() {public void run() {drawFrame();}};
这里面定义了一个handler,利用它来重复的调用画图方法,来不断地绘制水印。需要注意的是,在这里绘图我们是直接在surfaceview上进行绘图的,这是另外一个线程,不同于只能在主线程更新UI的那个线程。并且它在底层实现机制中实现了双缓冲机制,所以一般不会阻塞线程。更多的关于surfaceview的知识可以去百度,因为我也是百度的。
接下来就是画图方法了。
private void drawFrame() {// 获取该壁纸的SurfaceHolderfinal SurfaceHolder holder = getSurfaceHolder();Canvas c = null;try {// 获取Canvasc = holder.lockCanvas();if (c != null) {isRuning = true;c.save();Paint paint1 = new Paint();paint1.setAlpha(255);paint1.setAntiAlias(true);paint1.setStyle(Paint.Style.FILL);c.drawBitmap(res,0,0,paint1);// 在触碰点绘制图像drawTouchPoint(c);for (int i = 0; i < wList.size(); i++) {Wave w = wList.get(i);int alpha = w.p.getAlpha();if (alpha == 0) {wList.remove(i);//删除i以后,总数会减少一,否则会漏掉一个对象i--;continue;}alpha -= 10;if (alpha < 10) {alpha = 0;}w.p.setAlpha(alpha);}if (wList.size() == 0) {isRuning = false;}}} finally {if (c != null)holder.unlockCanvasAndPost(c);}mHandler.removeCallbacks(drawTarget);// 调用重绘if (mVisible && isRuning) {//重复画图mHandler.postDelayed(drawTarget,50);}}
private void drawTouchPoint(Canvas c) {for (int i = 0; i < wList.size(); i++) {Wave wave = wList.get(i);if (waterId == 0) {bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.heart_icon);}else{bitmap1 = BitmapFactory.decodeResource(getResources(),waterId);}c.drawBitmap(bitmap1,wave.cx,wave.cy,wave.p);}}
绘图的坐标点可以通过onTouchEvent方法获得,点击屏幕画出水印的效果基本完成了,接下来就是遇到的问题了。
首先这样壁纸背景只能是个单色,但是壁纸怎么能这样呢。所以我就又百度了好长时间,最后找出一个方法就是在这个基础上再在画布上画一个图,利用这张图当背景。代码如下
@Overridepublic void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {super.onSurfaceChanged(holder, format, width, height);if (path == null){bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_back);}else {bitmap = BitmapFactory.decodeFile(path);}WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);int width2 = wm.getDefaultDisplay().getWidth();int height2 = wm.getDefaultDisplay().getHeight();int width1 = bitmap.getWidth();int height1 = bitmap.getHeight();Matrix matrix = new Matrix();if (width1 >= width2 && height1 >= height2){float scaleWight1 = ((float)width2)/width1;float scaleHeight1 = ((float)height2)/height1;float Scale;if (scaleWight1 - scaleHeight1 >= 0){Scale = scaleWight1;}else {Scale = scaleHeight1;}matrix.preScale(Scale,Scale);res = Bitmap.createBitmap(bitmap,0,0,width1,height1,matrix,true);}if (width1 < width2 && height1 < height2){float scaleWight = ((float)width2)/width1;float scaleHeight = ((float)height2)/height1;float Scale2;if (scaleWight - scaleHeight >= 0){Scale2 = scaleWight;matrix.preScale(Scale2,Scale2);int y2 = (int)(height1 - height1/Scale2);Bitmap FirstBi = Bitmap.createBitmap(bitmap,0,0,width1,height1,matrix,true);int FirstWid = FirstBi.getWidth();int FirstHei = FirstBi.getHeight();res = Bitmap.createBitmap(FirstBi,0,y2/2,FirstWid,FirstHei-y2/2);}else {Scale2 = scaleHeight;matrix.preScale(Scale2,Scale2);int x2 = (int)(width1 - width1/Scale2);Bitmap FirstBit = Bitmap.createBitmap(bitmap,0,0,width1,height1,matrix,true);int FirstWidth = FirstBit.getWidth();int FirstHeight = FirstBit.getHeight();res = Bitmap.createBitmap(FirstBit,x2/2,0,FirstWidth-x2/2,FirstHeight);}}if (width1 >= width2 && height1 < height2){float scale3 = ((float)height2)/height1;matrix.preScale(scale3,scale3);int x3 = (int)(width1 - width1/scale3);Bitmap Bitmap3 = Bitmap.createBitmap(bitmap,0,0,width1,height1,matrix,true);int FirstWidth3 = Bitmap3.getWidth();int FirstHeight3 = Bitmap3.getHeight();res = Bitmap.createBitmap(Bitmap3,x3/2,0,FirstWidth3-x3/2,FirstHeight3);}if (width1 < width2 && height1 >= height2){float scale4 = ((float)width2)/width1;matrix.preScale(scale4,scale4);int y3 = (int)(height1 - height1/scale4);Bitmap Bit4 = Bitmap.createBitmap(bitmap,0,0,width1,height1,matrix,true);int FirstWidth4 = Bit4.getWidth();int FirstHeight4 = Bit4.getHeight();res = Bitmap.createBitmap(Bit4,0,y3/2,FirstWidth4,FirstHeight4-y3/2);}Canvas ca = holder.lockCanvas();Paint paint1 = new Paint();paint1.setAlpha(255);paint1.setAntiAlias(true);paint1.setStyle(Paint.Style.FILL);ca.drawBitmap(res,0,0,paint1);holder.unlockCanvasAndPost(ca);}
其实画图的方法还和上面一样,但是又多出了好多判断代码,这就是我遇到的另一个问题。那就是我们可以随便拿一张图片来当壁纸,但是我们的图片尺寸基本和自己的手机屏幕尺寸是不配合的。所以我们就得裁剪图片,使图片适应屏幕。其中matrix.prescale方法只是缩放了图片,但是对于有些图片并不能得到主要的部分,所以我们还要对图片进行偏移。我采用的是先缩放,再偏移的方法也就是上面看到的两次使用createBitmap方法了。代码写的很乱,也懒得改了,因为想这个办法想了好长时间。
最后就是我们要能自己选择手机里的图片作为壁纸,这样可用性会更高。这也就遇到了一个新的问题,那就是如何把我们选择的图片传到我们的壁纸服务中。一开始我就想的是用intent传递数据,这是标准的用法。但是利用启动服务的方法根本传不进去,因为这个壁纸服务根本不是我去启动的,而是手机内部的壁纸管理器去启动的,具体内部的实现我也不知道是怎么回事,所以这个方法就行不通了。后来在看了鸿洋的一个博客,是视频做壁纸的,他上面是用的广播来控制声音开启的。所以我就也仿着去弄,结果是能传递数据进去了,但是只有当壁纸启动起来,才能进行壁纸的更换。具体的情况大家可以试试,这是因为注册的是动态广播只有当服务启动了,才能接收广播。再后来我改了改就直接利用了静态方法来传递数据,以前还真不知道这个方法。不过百度了一下,这个方法好像容易造成传递的数据为空,所以这并不能算最好的方法,还是得慎用。但是我目前还没遇到过为空的时候。
public static void ImagePath(Context context, String data){path = data;}
最后完整代码我的github
应用已上线,下载地址http://shouji.baidu.com/software/23371524.html