Android编程实现扭曲图像的绘制功能示例

作者:猪哥 2017-09-05

为了实现动画效果,使用drawBitmapMess方法对图像进行扭曲,使用定时器以100毫秒的频率按圆形轨迹扭曲图像。

扭曲的关键是生成verts数组。本例一开始会先生成verts数组的初始值:有一定水平和垂直间距的网点坐标。然后通过warp方法按一定的数学方法变化verts数组中的坐标。关键部分的代码如下:

定义基本变量:MyView是用于显示扭曲的图像的自定义view,angle是圆形轨迹的当前角度:

privatestaticBitmap bitmap;
privateMyView myView;
privateintangle =0;        // 圆形轨迹当前的角度
privateHandler handler =newHandler()
{
   publicvoidhandleMessage(Message msg)
   {
     switch(msg.what)
     {
       case1:
         Random random =newRandom();
         // 计算图形中心点坐标
         intcenterX = bitmap.getWidth() /2;
         intcenterY = bitmap.getHeight() /2;
         doubleradian = Math.toRadians((double) angle);
         // 通过圆心坐标、半径和当前角度计算当前圆周的某点横坐标
         intcurrentX = (int) (centerX +100* Math.cos(radian));
         // 通过圆心坐标、半径和当前角度计算当前圆周的某点纵坐标
         intcurrentY = (int) (centerY +100* Math.sin(radian));
         // 重绘View,并在圆周的某一点扭曲图像
         myView.mess(currentX, currentY);
         angle +=2;
         if(angle >360)
           angle =0;
         break;
     }
     super.handleMessage(msg);
   }
};
privateTimerTask timerTask =newTimerTask()
{
   publicvoidrun()
   {
     Message message =newMessage();
     message.what =1;
     handler.sendMessage(message);
   }


以下是自定义view,MyView的具体内容:

privatestaticclassMyViewextendsView
{
    privatestaticfinalint;
    privatestaticfinalint;
    privatestaticfinalintCOUNT = (WIDTH +1) * (HEIGHT +1);
    privatefinalfloat[] verts =newfloat[COUNT *2];
    privatefinalfloat[] orig =newfloat[COUNT *2];
    privatefinalMatrix matrix =newMatrix();
    privatefinalMatrix m =newMatrix();
    // 设置verts数组的值
    privatestaticvoidsetXY(float[] array,intindex,floatx,floaty)
    {
      array[index *2+0] = x;
      array[index *2+1] = y;
    }
    publicMyView(Context context)
    {
      super(context);
      setFocusable(true);
      bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
      floatw = bitmap.getWidth();
      floath = bitmap.getHeight();
      intindex =0;
      // 生成verts和orig数组的初始值,这两个数组的值是一样的,只是在扭曲的过程中需要修改verts
      // 的值,而修改verts的值要将原始的值保留在orig数组中
      for(inty =0; y <= HEIGHT; y++)
      {
        floatfy = h * y / HEIGHT;
        for(intx =0; x <= WIDTH; x++)
        {
          floatfx = w * x / WIDTH;
          setXY(verts, index, fx, fy);
          setXY(orig, index, fx, fy);
          index +=1;
        }
      }
      matrix.setTranslate(10,10);
      setBackgroundColor(Color.WHITE);
    }
    @Override
    protectedvoidonDraw(Canvas canvas)
    {
      canvas.concat(matrix);
      canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts,0,null,0,null);
    }
    // 用于扭曲图像的方法,在该方法中根据当前扭曲的点(扭曲区域的中心点),也就是cx和cy参数,
    // 来不断变化verts数组中的坐标值
    privatevoidwarp(floatcx,floatcy)
    {
      finalfloatK =100000; // 该值越大,扭曲得越严重(扭曲的范围越大)
      float[] src = orig;
      float[] dst = verts;
      // 按一定的数学规则生成verts数组中的元素值
      for(inti =0; i < COUNT *2; i +=2)
      {
        floatx = src[i +0];
        floaty = src[i +1];
        floatdx = cx - x;
        floatdy = cy - y;
        floatdd = dx * dx + dy * dy;
        floatd = FloatMath.sqrt(dd);
        floatpull = K / ((float) (dd *d));
        if(pull >=1)
        {
          dst[i +0] = cx;
          dst[i +1] = cy;
        }
        else
        {
          dst[i +0] = x + dx * pull;
          dst[i +1] = y + dy * pull;
        }
      }
    }
    // 用于MyView外部控制图像扭曲的方法。该方法在handleMessage方法中被调用
    publicvoidmess(intx,inty)
    {
      float[] pt ={ x, y };
      m.mapPoints(pt);
      // 重新生成verts数组的值
      warp(pt[0], pt[1]);
      invalidate();
    }
  }
}


以下是Activity的onCreate方法:

protectedvoidonCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    myView =newMyView(this);
    setContentView(myView);
    Timer timer =newTimer();
    // 开始定时器
    timer.schedule(timerTask,0,100);
}


下面来看看扭曲后的效果,不同时刻,图片呈现出不同的扭曲效果:

相关文章

精彩推荐