实现磁贴的效果的一种方法
由于今年win8的那种磁贴效果比较流行,所以我们的项目决定使用这种磁贴。经过在网上的搜索查询,有一位大神的效果看起来比较炫酷,所以特下载下来进行了钻研,在基本弄懂了实现方法后,特在此总结,以备以后的使用。
有以下几点需要说明:
1>磁贴的图片是自己用ps根据手机的像素和hpi扣出来的,是一种纯色块。
2>磁贴的缩放特效是Matrix实现的。
3>磁贴的旋转特效是Camera和Matrix实现的。(Camera是Graphics包里边的类,请注意)
4>磁贴是自定义的ImageView,需要在XML中用自定义的。
5>getHeight()之类的方法是得到ImageView的大小,即当前色块的大小。
6>event.getX()之类的方法的父框架是当前色块,得到的x和y坐标永远在当前色块,(0,0)在色块的左上角。
7>按下屏幕是缩放或者旋转,松开是恢复原状态。
7>其余需要注意的地方在代码中或者贴出代码后在说。
1.先说明一下缩放特效的实现方法。
@Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: float X = event.getX(); //得到的是相对于图片来说的x y (0,0)为左上角 float Y = event.getY(); RolateX = Width / 2 - X; RolateY = Height / 2 - Y; XbigY = Math.abs(RolateX) > Math.abs(RolateY) ? true : false; isScale = X > Width / 3 && X < Width * 2 / 3 && Y > Height / 3&& Y < Height * 2 / 3; //分成9塊,在中間的一塊 if (isScale) { handler.sendEmptyMessage(Constant.First_Scale); } else { rolateHandler.sendEmptyMessage(Constant.First_Rotate); } break; case MotionEvent.ACTION_UP: if (isScale) { handler.sendEmptyMessage(Constant.Recover_Scale); } else { rolateHandler.sendEmptyMessage(Constant.Recover_Rotate); } break; } return true; }
做一下说明:
1>这个是把键盘分成9块,如果按的是中间的那一块,则是缩放,其余的八块根据x和y的大小则是不同的旋转效果。
2>开两个Handler线程,分别处理旋转和缩放。
private synchronized void BeginScale(Matrix matrix, float scale) { matrix.postScale(scale, scale, halfWidth, halfHeight); //前俩参数为x y轴的拉伸变化,大于1为拉伸。 后俩参数制定缩放的中心 setImageMatrix(matrix); }
如注释所示,伸缩特效的实现方法比较简单,前两个参数是x和y轴的拉伸变化,大于1是拉伸,小于1的伸缩,后两个参数指定中心点,这里的中心点是色块的中点。
private Handler handler = new Handler() { private Matrix matrix = new Matrix(); private float s; int count = 0; @Override public void handleMessage(Message msg) { super.handleMessage(msg); matrix.set(getImageMatrix()); switch (msg.what) { case Constant.First_Scale: if(!isFinish) //防止还未恢复成原状客户又点击 { return; }else { isFinish = false; count = 0; s = (float) Math.sqrt(Math.sqrt(minScale)); BeginScale(matrix, s); handler.sendEmptyMessage(Constant.Middle_Scale); } break; case Constant.Middle_Scale: BeginScale(matrix, s); if (count < MaxScaleCount) { //设置次数是在每次缩放的基础上,继续缩放,形成缩放比较深的效果 handler.sendEmptyMessage(Constant.Middle_Scale); } else { isFinish = true; } count++; break; case Constant.Recover_Scale: if (!isFinish) { handler.sendEmptyMessage(Constant.Recover_Scale); } else { isFinish = false; count = 0; s = (float) Math.sqrt(Math.sqrt(1.0f / minScale)); //经过几次的拉伸变回原来的大小 BeginScale(matrix, s); handler.sendEmptyMessage(Constant.Middle_Scale); } break; } } };
这是处理伸缩特效的线程,如注释,有几点需要说明:
1>为了防止客户连续点击磁贴,设置一个标志位isFinish,如果磁贴伸缩并且恢复成原形状,则这个标志位为true,否则为false,在第一次点击中,可以很好的防止磁贴变形。
2>为了形成比较深的缩放效果,设置了多层缩放,即在第一次点击以后,发消息给中间缩放,在第一次的基础上,继续进行缩放。
3>当用户松开屏幕后,发消息给处理恢复磁贴的部分,在最后的缩放过程一步一步按照缩放的过程进行恢复。
下面贴出旋转特效的代码。
private synchronized void BeginRolate(Matrix matrix, float rolateX, float rolateY) { camera.save(); camera.rotateX(RolateY > 0 ? rolateY : -rolateY); // 旋转的角度 camera.rotateY(RolateX < 0 ? rolateX : -rolateX); camera.getMatrix(matrix); camera.restore(); if (RolateX > 0 && rolateX != 0) //第一个参数大于零表示向右,小于向左。第二个参数大于零向下,小于零向上 首先将坐标平移到该点,在进行旋转 { matrix.preTranslate(-Width, -halfHeight); //向左下压 matrix.postTranslate(Width, halfHeight); } else if (RolateY > 0 && rolateY != 0) //向上边压 { matrix.preTranslate(-halfWidth, -Height); matrix.postTranslate(halfWidth, Height); } else if (RolateX < 0 && rolateX != 0) //向右压 { matrix.preTranslate(-0, -halfHeight); matrix.postTranslate(0, halfHeight); } else if (RolateY < 0 && rolateY != 0) //向下压 { matrix.preTranslate(-halfWidth, -0); matrix.postTranslate(halfWidth, 0); } setImageMatrix(matrix); }
有几点需要说明:
1>旋转特效是Camera负责旋转,而Matrix负责平移,在此处是将旋转的中心点平移到合适的位置。
2>preTranslate(x,y)和postTranslate(x,y)负责平移,如果x>0,表示向右平移,x<0表示向左平移。y>0向下平移,y<0向上平移。
3>preTranslate(x,y)相当于右乘矩阵,即在旋转之前进行的平移。
4>postTranslate(x,y)相当于左乘矩阵,即在旋转之后进行的平移。
5>Camera的坐标系是左手坐标系,可以参考一下上一篇博客。
6>根据不同的条件判断是左压,右压,上压还是下压。
下面贴出处理旋转的线程。
private Handler rolateHandler = new Handler() { private Matrix matrix = new Matrix(); private float count =0; @Override public void handleMessage(Message msg) { super.handleMessage(msg); matrix.set(getImageMatrix()); switch (msg.what) { case Constant.First_Rotate: count = 0; BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count)); rolateHandler.sendEmptyMessage(Constant.Middle_Rotate); break; case Constant.Middle_Rotate: BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count)); if (count < rotateDegree) { rolateHandler.sendEmptyMessage(Constant.Middle_Rotate); } else { isFinish = true; } count+=2; break; case Constant.Middle_Recover_Rotate: BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count)); if (count > 0) { rolateHandler.sendEmptyMessage(Constant.Middle_Recover_Rotate); } else { isFinish = true; } count-=2; break; case Constant.Recover_Rotate: count = rotateDegree; BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count)); rolateHandler.sendEmptyMessage(Constant.Middle_Recover_Rotate); break; } } };
这里需要说明几点:
1>count表明的是旋转角度,作用和缩放一样,是为了形成比较深的效果。
2>恢复的时候也是相当于一帧一帧的恢复,从10逐渐变为0。
参考网友的总结,做一下总体总结,如下。
- Camera的rotate()相关方法是指定某一维度上旋转指定的角度。
- Matrix的rotate()相关方法实现的效果是顺时针旋转指定的角度;与Camera指定Z轴旋转效果相同,但方向相反。
- Camera的translate()方法根据某一维度上视点的位移实现图像的缩放,与Matrix的scale()相关方法作用效果相似,只是Matrix的scale()相关方法是直接指定缩放比例。
- Camera不支持倾斜操作,Matrix可以直接实现倾斜操作。