由于今年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可以直接实现倾斜操作。