O
O
OlegTar2013-12-19 14:32:35
Android
OlegTar, 2013-12-19 14:32:35

Android: Canvas strange behavior?

I am making an application for Android.
I made it my own View component. In it, a 10x10 field is drawn on the canvas.
Moreover, the user sees only part of the field, he can see the rest of it by scrolling with his finger. The user can highlight any square by pressing and releasing his finger on it, but this is beside the point.
4d47a0ee09e71c55796956512de90c44.jpg
The user can increase the field with his fingers:
6d4ac3ce1b0558f6d8d9166ed4cf648d.jpg
The user can also decrease the field, and that's the problem.
34f86ba2762a227efabb34cb7bc91f47.jpg
When the ScaleFactor becomes around less than 0.5, the canvas starts moving towards the lower right corner of the component. It is the canvas itself, the component itself, as it stood within its former boundaries, that it still stands.
The image is enlarged/reduced using the Canvas.Scale command.
Attention question:Is it possible to remove this offset to the lower right corner?
The application is written in C# (xamarin). Although I write the code in C #, the answers of those who write in Android traditionally in Java will be useful to me.
Here is the component code, commented as best I could:

The code
public class Table : View
  {
    private Paint p;//линия квадратиков
    private Paint fill_p;//цвет подкрашенного квадратика
    private Paint border_p;//граница видимой части канваса оранжевого цвета

    private float scale_factor = 1.0f;//Скэйл Фактор для канваса
    private readonly ScaleGestureDetector _scaleDetector;


    private string debug_text;
    private MotionEventActions last_action;

    private float x;//точка касания, нужна для определения какой квадратик подсвечивать
    private float y;

    private float last_x;//предыдущие точки касания элемента, нужны для скрола
    private float last_y;	

    private int margin = 50;
    private int cols   = 10;
    private int rows   = 10;

    public Table (Context context) :
      base (context)
    {
      Initialize ();

      p = new Paint ();
      p.Color = Color.White;
      p.TextSize = 25;

      fill_p = new Paint ();
      fill_p.SetStyle (Paint.Style.Fill);
      fill_p.Color = new Color(223, 136, 2);

      border_p = new Paint ();
      border_p.SetStyle (Paint.Style.Stroke);
      border_p.Color = new Color(223, 136, 2);

      _scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this));		
    }

    public override bool OnTouchEvent(MotionEvent ev)
    {
      _scaleDetector.OnTouchEvent (ev);

      MotionEventActions action = ev.Action & MotionEventActions.Mask;
      //int pointerIndex;
      Console.WriteLine (action.ToString ());



      switch (action) {
        /*запоминаем, где человек нажал квадратик, если следующее действие будет Move, то скроллим поле.
        Если следующее движение будет Up, то мы подсвечим квадратик.
        */
        case MotionEventActions.Down:
          last_x = ev.GetX ();
          last_y = ev.GetY ();
        break;

      case MotionEventActions.Move:
        /*Скроллим*/
        float x1 = ev.GetX ();
        float y1 = ev.GetY ();
        float delta_x = -x1 + last_x;
        float delta_y = -y1 + last_y;					

        this.ScrollBy ((int)delta_x, (int)delta_y);
        Console.WriteLine ("scrollX = {0}, scrollY = {1}", this.ScrollX, this.ScrollY);

        /*Проверяем чтобы нельзя было скролить дальше, чем надо*/
        if (this.ScrollX < 0) {
          this.ScrollX = 0;
        }
        if (this.ScrollY < 0) {
          this.ScrollY = 0;
        }

                       /*недопускаем, чтобы можно было перескролить вправо и вниз*/
        int visible_width = margin * cols + 1;
        int visible_height = margin * cols + 1;

        visible_width = (int)(visible_width * scale_factor);
        visible_height = (int)(visible_height * scale_factor);

        int bound_x = (visible_width - this.LayoutParameters.Width);
        int bound_y = (visible_height - this.LayoutParameters.Height);

        Console.WriteLine ("visible_width = {0}", visible_width);

        if (this.ScrollX > bound_x) {
          this.ScrollX = bound_x;
        }

        if (this.ScrollY > bound_y) {
          this.ScrollY = bound_y;
        }

        break;

        /*Запоминаем координаты для подсвечивания нужного квадратика*/
        case MotionEventActions.Up:
        if (last_action == MotionEventActions.Down) {
          x = (ev.GetX () + this.ScrollX) / scale_factor;
          y = (ev.GetY () + this.ScrollY)/ scale_factor;					
          debug_text = String.Format ("x = {0}, y = {0}", x, y);
          Console.WriteLine (debug_text);
          Invalidate ();
        }
        break;
      }
      last_action = action;

      last_x = ev.GetX();
      last_y = ev.GetY();
            
      return true;
    }

    protected override void OnDraw (Android.Graphics.Canvas canvas)
    {
      /*Рисуем поле*/
      base.OnDraw (canvas);
      canvas.Save();
      /*сделал на всякий случай Смещение на 0, 0. Не помогло*/
      canvas.Translate (0, 0);
      /*Увеличиваем/уменьшаем*/
      canvas.Scale (scale_factor, scale_factor);

      /*Рисуем клетчатое поле*/
      int height = rows * margin;
      int width  = cols * margin;


      for (int i = 0; i < cols + 1; i++) {
        canvas.DrawLine (i * margin, 0, i * margin, height, p);
      }
      for (int i = 0; i < rows + 1; i++) {
        canvas.DrawLine (0, i * margin, width, i * margin, p);
      }

                  /*Находим отступы сверху и сбоку, чтобы номера писать ровно по центру квадратика
                  так как буквы имеют одинаковую ширину вычисляем эти значения только для строки "00"*/
      string text = "00";
      Rect bounds = new Rect();
      p.GetTextBounds (text, 0, text.Length, bounds);
      int padding_x = (margin - bounds.Width ())/2;
      int padding_y = (margin - bounds.Height ())/2; 

      /*Подсвечиваем выбранный квадратик*/
      int x_int = (int) Math.Floor(x / margin);
      int y_int = (int) Math.Floor(y / margin);
      Console.WriteLine ("x_int = {0}, y_int = {0}", x_int, y_int);

      canvas.DrawRect (new RectF (x_int * margin + 1, y_int * margin + 1, x_int * margin + margin - 1, y_int * margin + margin - 1), fill_p);				

             /*Рисуем номера*/
      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          string number = ((i * 10) + j).ToString("D2");

          canvas.DrawText (number, j * margin + padding_x, i * margin + margin - padding_y, p);
        }
      }
      canvas.Restore();


      /*Рисуем оранжевую рамку вокруг поля*/
      canvas.DrawLine (0, 0, this.Width - 1 + this.ScrollX, 0, border_p);//-
      canvas.DrawLine (0, 0, 0, this.Height + this.ScrollY, border_p);//|
      canvas.DrawLine (this.Width - 1 + this.ScrollX, 0, this.Width - 1 + this.ScrollX, this.Height - 1 + this.ScrollY, border_p);// |
      canvas.DrawLine (0, this.Height - 1 + this.ScrollY, this.Width - 1 + this.ScrollX, this.Height - 1 + this.ScrollY, border_p);//_

      Console.WriteLine (scale_factor);		
    }

    public Table (Context context, IAttributeSet attrs) :
      base (context, attrs)
    {
      Initialize ();
    }

    public Table (Context context, IAttributeSet attrs, int defStyle) :
      base (context, attrs, defStyle)
    {
      Initialize ();
    }

    void Initialize ()
    {
    }

    //Обработчик щипка для Zoom'а
    private class MyScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
    {
      private Table view;

      public MyScaleListener(Table view)
      {
        this.view = view;
      }

      public override bool OnScale(ScaleGestureDetector detector)
      {
        view.scale_factor *= detector.ScaleFactor;
        if (view.scale_factor > 5.0f)
        {
          view.scale_factor = 5.0f;
        }
        if (view.scale_factor < 0.1f)
        {
          view.scale_factor = 0.1f;
        }
        Console.WriteLine ("view.scale_factor = {0}", view.scale_factor);			
      
        view.Invalidate ();
        view.RequestLayout ();
        return true;
      }
    }
  }

Answer the question

In order to leave comments, you need to log in

1 answer(s)
X
xotta6bl4, 2013-12-23
@OlegTar

habrahabr.ru/post/151492 Here I once described working with canvas in Java.

//унаследовались от ScaleGestureDetector.SimpleOnScaleGestureListener, чтобы не писать пустую реализацию ненужных
    //методов интерфейса OnScaleGestureListener
    private class MyScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        //обрабатываем "щипок" пальцами
        @Override
        public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
            float scaleFactor=scaleGestureDetector.getScaleFactor();//получаем значение зума относительно предыдущего состояния
            //получаем координаты фокальной точки - точки между пальцами
            float focusX=scaleGestureDetector.getFocusX();
            float focusY=scaleGestureDetector.getFocusY();
            //следим чтобы канвас не уменьшили меньше исходного размера и не допускаем увеличения больше чем в 2 раза
            if(mScaleFactor*scaleFactor>1 && mScaleFactor*scaleFactor<2){
                mScaleFactor *= scaleGestureDetector.getScaleFactor();
                canvasSize =viewSize*mScaleFactor;//изменяем хранимое в памяти значение размера канваса
                //используется при расчетах
                //по умолчанию после зума канвас отскролит в левый верхний угол.
                //Скролим канвас так, чтобы на экране оставалась
                //область канваса, над которой был жест зума
                //Для получения данной формулы достаточно школьных знаний математики (декартовы координаты).
                int scrollX=(int)((getScrollX()+focusX)*scaleFactor-focusX);
                scrollX=Math.min( Math.max(scrollX, 0), (int) canvasSize -viewSize);
                int scrollY=(int)((getScrollY()+focusY)*scaleFactor-focusY);
                scrollY=Math.min( Math.max(scrollY, 0), (int) canvasSize -viewSize);
                scrollTo(scrollX, scrollY);
            }
            //вызываем перерисовку принудительно
            invalidate();
            return true;
        }
    }

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question