2015-06-17 4 views
4

Итак, у меня есть ScrollView, HorizantalScrollView и BoardView для игры TicTacToe.Android: Zoom by center pinch

Когда пользователь масштабирует, а я перерисовываю масштаб в категории ячеек с помощью масштабирования ScalegestureDetector, пинч масштабирования выделяется сверху, слева от экрана, а не в центре пинчей, как я могу выделить его центру?

вот мой проект в GitHub: https://github.com/boyfox/TestTicTac.git

Проект используется com.android.support:appcompat-v7

Кто-нибудь есть решение этой проблемы?

+0

Дубликат [это] (http://stackoverflow.com/questions/13961817/android-sdk-not -scaling-at-center-of-touch)? – mpkuth

+0

http://stackoverflow.com/questions/13961817/android-sdk-not-scaling-at-centre-of-touch --- здесь не существует ответа, как я хотел, и не существует встроенной логики холста в моем случае, пожалуйста посмотрите мой тестовый проект. Вы можете найти мою ошибку из моего тестового проекта? –

ответ

3

Я думаю, что вы захотите перейти к реализации ближе к Android Dragging and Scaling examples (и к аналогичному вопросу here).

Это начало того, что вам нужно. Вы можете панорамировать вид, как и раньше, и теперь масштабируйте вид в центре жестящего пальца. Это ваш базовый BoardView с логикой, чтобы набирать очки на кликах от here. Это должно быть довольно легко нарисовать собственные значки X и O вместо кругов.

BoardView.java

public class BoardView extends View { 

    private static final int JUST_SCALED_DURATION = 100; 
    private static final int MAX_TOUCH_DURATION = 1000; 
    private static final int MAX_TOUCH_DISTANCE = 10; 
    private boolean stayedWithinTouchDistance; 
    private float firstTouchX, firstTouchY; 
    private long firstTouchTime, lastScaleTime; 

    private float posX, posY, lastTouchX, lastTouchY; 
    private ScaleGestureDetector scaleDetector; 
    private float minScaleFactor = 0.1f; 
    private float maxScaleFactor = 5.0f; 
    private float scaleFactor = 1.0f; 
    private float cellSize = 50.0f; 
    private int numCells = 50; 

    private ArrayList<Point> points; 
    private Paint linePaint, pointPaint; 

    public BoardView(Context context) { 
     this(context, null, 0); 
    } 

    public BoardView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public BoardView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     points = new ArrayList<>(); 
     linePaint = new Paint(); 
     linePaint.setAntiAlias(true); 
     linePaint.setColor(-65536); 
     linePaint.setStrokeWidth(1.0f); 
     pointPaint = new Paint(); 
     pointPaint.setAntiAlias(true); 
     pointPaint.setColor(-65536); 
     pointPaint.setStrokeWidth(1.0f); 
     posX = linePaint.getStrokeWidth(); 
     posY = linePaint.getStrokeWidth(); 
     scaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 

    private static float distancePx(float x1, float y1, float x2, float y2) { 
     float dx = x1 - x2; 
     float dy = y1 - y2; 
     return (float) Math.sqrt(dx * dx + dy * dy); 
    } 

    private float distanceDp(float distancePx) { 
     return distancePx/getResources().getDisplayMetrics().density; 
    } 

    private Point coerceToGrid(Point point) { 
     point.x = (int) ((((int) (point.x/cellSize)) * cellSize) + (cellSize/2)); 
     point.y = (int) ((((int) (point.y/cellSize)) * cellSize) + (cellSize/2)); 
     return point; 
    } 

    @Override 
    public boolean onTouchEvent(@NonNull MotionEvent event) { 
     scaleDetector.onTouchEvent(event); 
     final int action = MotionEventCompat.getActionMasked(event); 
     if (action == MotionEvent.ACTION_DOWN) { 
      stayedWithinTouchDistance = true; 
      firstTouchX = lastTouchX = event.getRawX(); 
      firstTouchY = lastTouchY = event.getRawY(); 
      firstTouchTime = System.currentTimeMillis(); 
     } else if (action == MotionEvent.ACTION_MOVE) { 
      float thisTouchX = event.getRawX(); 
      float thisTouchY = event.getRawY(); 

      boolean justScaled = System.currentTimeMillis() - lastScaleTime < JUST_SCALED_DURATION; 
      float distancePx = distancePx(firstTouchX, firstTouchY, thisTouchX, thisTouchY); 
      stayedWithinTouchDistance = stayedWithinTouchDistance && 
        distanceDp(distancePx) < MAX_TOUCH_DISTANCE; 

      if (!stayedWithinTouchDistance && !scaleDetector.isInProgress() && !justScaled) { 
       posX += thisTouchX - lastTouchX; 
       posY += thisTouchY - lastTouchY; 
       invalidate(); 
      } 

      lastTouchX = thisTouchX; 
      lastTouchY = thisTouchY; 
     } else if (action == MotionEvent.ACTION_UP) { 
      long touchDuration = System.currentTimeMillis() - firstTouchTime; 
      if (touchDuration < MAX_TOUCH_DURATION && stayedWithinTouchDistance) { 
       int[] location = {0, 0}; 
       getLocationOnScreen(location); 
       float x = ((lastTouchX - posX - location[0])/scaleFactor); 
       float y = ((lastTouchY - posY - location[1])/scaleFactor); 
       points.add(coerceToGrid(new Point((int) x, (int) y))); 
       invalidate(); 
      } 
     } 
     return true; 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      lastScaleTime = System.currentTimeMillis(); 
      float scale = detector.getScaleFactor(); 
      scaleFactor = Math.max(minScaleFactor, Math.min(scaleFactor * scale, maxScaleFactor)); 
      if (scaleFactor > minScaleFactor && scaleFactor < maxScaleFactor) { 
       float centerX = detector.getFocusX(); 
       float centerY = detector.getFocusY(); 
       float diffX = centerX - posX; 
       float diffY = centerY - posY; 
       diffX = diffX * scale - diffX; 
       diffY = diffY * scale - diffY; 
       posX -= diffX; 
       posY -= diffY; 
       invalidate(); 
       return true; 
      } 
      return false; 
     } 
    } 

    private float getScaledCellSize() { 
     return scaleFactor * cellSize; 
    } 

    private float getScaledBoardSize() { 
     return numCells * getScaledCellSize(); 
    } 

    private void drawBoard(Canvas canvas) { 
     for (int i = 0; i <= numCells; i++) { 
      float total = getScaledBoardSize(); 
      float offset = getScaledCellSize() * i; 
      canvas.drawLine(offset, 0, offset, total, linePaint); 
      canvas.drawLine(0, offset, total, offset, linePaint); 
     } 
    } 

    private void drawPoints(Canvas canvas) { 
     for (Point point : points) { 
      float x = point.x * scaleFactor; 
      float y = point.y * scaleFactor; 
      float r = getScaledCellSize()/4; 
      canvas.drawCircle(x, y, r, pointPaint); 
     } 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     float total = getScaledBoardSize(); 
     float edge = linePaint.getStrokeWidth(); 
     posX = Math.max(Math.min(edge, getWidth() - total - edge), Math.min(edge, posX)); 
     posY = Math.max(Math.min(edge, getHeight() - total - edge), Math.min(edge, posY)); 

     canvas.save(); 
     canvas.translate(posX, posY); 
     drawBoard(canvas); 
     drawPoints(canvas); 
     canvas.restore(); 
    } 

} 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <com.example.client.BoardView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

</FrameLayout> 
+0

спасибо за лучший ответ на мой вопрос !!! Я соглашусь на ваш вопрос, но после увеличения я встаю с помощью щипцов с экрана, а центральное увеличение будет перенесено в другое место. Можете ли вы объяснить, почему? –

+0

К сожалению, он по-прежнему настраивал 'posX' и' posY', как только он достигнет масштабного коэффициента min/max, чтобы он скорректировал масштаб, который не выполнялся. Я обновил ответ с помощью проверки, которая разрешает это. – mpkuth

+0

Я понимаю, когда они появляются автоматически в другое место. Если вы нанесете два пальца на экран и сделаете масштабирование, второй палец возьмется с экрана с очень маленьким кастрюлем (с очень маленьким движением), но первый палец должен остаться на экране, и вы можете увидеть проблему. –