实现磁贴的效果的一种方法

由于今年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。



参考网友的总结,做一下总体总结,如下。

  1. Camera的rotate()相关方法是指定某一维度上旋转指定的角度。
  2. Matrix的rotate()相关方法实现的效果是顺时针旋转指定的角度;与Camera指定Z轴旋转效果相同,但方向相反。
  3. Camera的translate()方法根据某一维度上视点的位移实现图像的缩放,与Matrix的scale()相关方法作用效果相似,只是Matrix的scale()相关方法是直接指定缩放比例。
  4. Camera不支持倾斜操作,Matrix可以直接实现倾斜操作。

posted @ 2015-06-23 02:08  会孵蛋的鱼  阅读(608)  评论(0编辑  收藏  举报