title: android rotate imageview onfling
body: <p>I am creating a rotating knob it works fine when I rotate it slowly in clockwise or anticlockwise direction.but if I want to select a precise value then it become hard.Because I am adding a margin of 3 on each call to onscroll(ACtion_Move). What should I do .</p>
  
  <p>I think I need a precise angle between previous and current point.But it slow down the rotation.And Lets say if the user fling the wheel to right or left direction then what should I add to angle to rotate it smoothly in respective direction.</p>
  
  <p>Thanks in advance for you time.</p>
  
  <pre><code>public class RotaryKnobView extends ImageView {
  
  private float angle = 0f;
  private float theta_old = 0f;
  
  // width of imageview
  private float width;
  // height of imageview
  private float height;
  private final static int TOTAL_WEIGHT = 26;
  private final static double TOTAL_ANGLE = 360;
  private final static double MARGIN_BT_WEIGHT = (TOTAL_ANGLE / TOTAL_WEIGHT);
  private final static double STARTING_WEIGHT = 5.0d;
  private WeightPicker_Activity mContext = null;
  private RotaryKnobListener listener;
  private static double Weight_Picked = 0d;
  private static final String TAG = "RotaryKnobView";
  private Bitmap mBitmap = null;
  private GestureDetector mDetector = null;
  
  public interface RotaryKnobListener {
      public void onKnobChanged(int arg);
  
      public void onWeightPicked(double weightPicked);
  
  }
  
  public void setKnobListener(RotaryKnobListener l) {
      listener = l;
  }
  
  public RotaryKnobView(Context context) {
      super(context);
      this.mContext = (WeightPicker_Activity) context;
      initialize();
  }
  
  public RotaryKnobView(Context context, AttributeSet attrs) {
      super(context, attrs);
      this.mContext = (WeightPicker_Activity) context;
      initialize();
  }
  
  public RotaryKnobView(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
      this.mContext = (WeightPicker_Activity) context;
      initialize();
  }
  
  private float getTheta(float x, float y) {
      float sx = x - (width / 2.0f);
      float sy = y - (height / 2.0f);
  
      float length = (float) Math.sqrt(sx * sx + sy * sy);
      float nx = sx / length;
      float ny = sy / length;
      float theta = (float) Math.atan2(ny, nx);
  
      final float rad2deg = (float) (180.0 / Math.PI);
      float theta2 = theta * rad2deg;
  
      return (theta2 &lt; 0) ? theta2 + 360.0f : theta2;
  }
  
  public void setBitmap(Bitmap mBitmap) {
      this.mBitmap = mBitmap;
      this.setImageBitmap(this.mBitmap);
  }
  
  public void getBitmapWidth() {
      this.mBitmap.getWidth();
  }
  
  public void getBitmapHeight() {
      this.mBitmap.getHeight();
  }
  
  private boolean isRotationStarted = false;
  
  public void initialize() {
  
      // this.setImageResource(R.drawable.final_new_a);
      mDetector = new GestureDetector(mContext, new MyGestureRecognizer());
      setOnTouchListener(new OnTouchListener() {
          public boolean onTouch(View v, MotionEvent event) {
  
              // reset angle theta after one complete rotation
  
              int action = event.getAction();
              int actionCode = action &amp; MotionEvent.ACTION_MASK;
              if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
                  float x = event.getX(0);
                  float y = event.getY(0);
                  theta_old = getTheta(x, y);
                  Log.i("theta_old", theta_old + "");
              } else if (actionCode == MotionEvent.ACTION_MOVE) {
                  invalidate();
  
                  float x = event.getX(0);
                  float y = event.getY(0);
  
                  float theta = getTheta(x, y);
                  float delta_theta = theta - theta_old;
  
                  theta_old = theta;
  
                  // here we determine the direction of rotation
                  int direction = (delta_theta &gt; 0) ? 1 : -1;
  
                  if (direction &gt; 0) {
                      // Log.i("Direction &gt; 1", direction + "");
  
                  } else {
                      // Log.i("Direction &lt; 1", direction + "");
  
                  }
  
                  angle += 3 * direction;
                  mixMixCondition();
                  // Log.i("Current Weight",
                  // "Weight"
                  // + (((-1) * (angle / MARGIN_BT_WEIGHT)) + STARTING_WEIGHT)
                  // + "");
                  Weight_Picked = (((-1) * (angle / MARGIN_BT_WEIGHT)) + STARTING_WEIGHT);
                  Log.i("angle", "angle" + angle);
  
                  notifyListener(direction);
                  notifyListener(Weight_Picked);
                  RotaryKnobView.this.mContext.zoomImageView
                          .setImageBitmap(cropImage(
                                  RotaryKnobView.this.mContext.mWheelRelativeLayout
                                          .getWidth() / 2, 0));
  
              }
              mDetector.onTouchEvent(event);
              return true;
          }
  
      });
  }
  
  private void mixMixCondition() {
  
      // stop to rotate behind 5.(min value)
      if (angle &gt;= 0) {
          angle = 0;
      }
      // stop to rotate behind 30.(max value)
      // for anticlockwise rotation
      if (angle &lt; (-360 + MARGIN_BT_WEIGHT)) {
          angle = (float) (-360 + MARGIN_BT_WEIGHT);
          // Toast.makeText(RotaryKnobView.this.mContext,
          // "Complete anticlockwise", Toast.LENGTH_SHORT).show();
      }
  
      // for clockwise rotation
      if (angle &gt; 360) {
          angle = 0;
          // Toast.makeText(RotaryKnobView.this.mContext,
          // "Complete Clockwise",
          // Toast.LENGTH_SHORT).show();
      }
  
  }
  
  private void notifyListener(int arg) {
      if (null != listener)
          listener.onKnobChanged(arg);
  }
  
  private void notifyListener(double weight_picked) {
      if (null != listener) {
          listener.onWeightPicked(weight_picked);
      }
  }
  
  protected void onDraw(Canvas c) {
      c.rotate(angle, this.getWidth() / 2, this.getHeight() / 2);
      width = this.getWidth();
      height = this.getHeight();
  
      // LogInfo.showLogInfo(TAG, "Width = " + width);
      // LogInfo.showLogInfo(TAG, "Height" + height);
      // LogInfo.showLogInfo("ondrawImageView", "ondrawImageView");
      this.mContext.zoomImageView.setImageBitmap(cropImage(
              mContext.mWheelRelativeLayout.getWidth() / 2, 0));
      super.onDraw(c);
  
  }
  
  public Bitmap cropImage(int x, int y) {
      Matrix matrix = new Matrix();
      this.mContext.mWheelRelativeLayout.buildDrawingCache();
      this.mContext.mainBitmap = this.mContext.mWheelRelativeLayout
              .getDrawingCache();
      Bitmap croppedBitmap = Bitmap.createBitmap(this.mContext.mainBitmap,
              x - 50, y + 15, 100, 130, matrix, true);
      // Bitmap croppedBitmap2 = Bitmap.createBitmap(croppedBitmap, x-30,
      // y+15, 100, 120, matrix,true);
      this.mContext.mWheelRelativeLayout.invalidate();
      return croppedBitmap;
  }
  
  /**
   * @return The angle of the unit circle with the image view's center
   */
  private double getAngle(double xTouch, double yTouch) {
      double x = xTouch - (this.getWidth() / 2d);
      double y = this.getHeight() - yTouch - (this.getHeight() / 2d);
  
      switch (getQuadrant(x, y)) {
      case 1:
          return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
  
      case 2:
      case 3:
          return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
  
      case 4:
          return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
  
      default:
          // ignore, does not happen
          return 0;
      }
  }
  
  /**
   * @return The selected quadrant.
   */
  private static int getQuadrant(double x, double y) {
      if (x &gt;= 0) {
          return y &gt;= 0 ? 1 : 4;
      } else {
          return y &gt;= 0 ? 2 : 3;
      }
  }
  
  private class MyGestureRecognizer extends SimpleOnGestureListener {
  
      @Override
      public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
              float velocityY) {
          LogInfo.showLogInfo("onFling", "VelocityX" + velocityX);
          LogInfo.showLogInfo("onFling", "VelocityY" + velocityY);
          LogInfo.showLogInfo("onFling", "onFling");
  
          return super.onFling(e1, e2, velocityX, velocityY);
      }
  
      @Override
      public boolean onScroll(MotionEvent e1, MotionEvent e2,
              float distanceX, float distanceY) {
          return super.onScroll(e1, e2, distanceX, distanceY);
      }
  
     }
       }
      }
  </code></pre>