2015-07-22 1 views
5

Я начал изучать рисунок холста с Android, и я хотел бы сделать простое приложение.Рисунок на холсте с пользовательским взаимодействием немного лагги

В приложении запускается так называемая «змея», начинающаяся на экране, , когда пользователь забирает экран, «змея» меняет направление.

Я achived это легко, но есть небольшая проблема:

Когда пользователь нажимает на экран, змея иногда меняет направление на тот момент, иногда только через несколько миллисекунд. Таким образом, пользователь может четко понимать, что взаимодействие не так отзывчивое, как должно,Точный момент поворота змеи довольно непредсказуем, даже если вы концентрируетесь очень сильно. Должен быть какой-то другой способ сделать это лучше, чем я.

Пожалуйста, проверьте мой код, я использую обработчик с Runnable для перемещения змеи. (рисунок на холсте, и каждая установка его в качестве фона в целях времени, то есть каждый раз, когда установка с setContentView моей деятельности

Код:.

public class MainActivity extends Activity implements View.OnClickListener { 

    Paint paint = new Paint(); 
    Canvas canvas; 


    View contentView; ///<The view to set by setContentView 

    int moveSize; ///<Sze in px to move drawer to draw another square 


    int leftIndex; ///<Indexes for square drawing 
    int topIndex; 
    int rightIndex; 
    int bottomIndex; 

    int maxBoundsX; ///<The max number of squares in X axis 
    int maxBoundsY; ///<The max number of squares in Y axis 

    int moveSpeedInMillis = 25; ///<One movement square per milliseconds 

    Bitmap bitmapToDrawOn; ///<We draw the squares to this bitmap 

    Direction currentDirection = Direction.RIGHT; ///< RIGHT,LEFT,UP,DOWN directions. default is RIGHT 

    Handler handler = new Handler(); ///<Handler for running a runnable that actually 'moves' the snake by drawing squares to shifted positions 
    Runnable runnable = new Runnable() { 

     @Override 
     public void run() { 

      Log.i("runnable", "ran"); 

      //Drawing a square to the current destination 
      drawRectPls(currentDirection); 
      //After some delay we call again and again and again 
      handler.postDelayed(runnable, moveSpeedInMillis); 
     } 
    }; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 


     /*getting area properties like moveSize, and bounds*/ 

     moveSize = searchForOptimalMoveSize(); 

     maxBoundsX = ScreenSizer.getScreenWidth(this)/moveSize; 
     maxBoundsY = ScreenSizer.getScreenHeight(this)/moveSize; 

     Log.i("moveSize", "moveSize: " + moveSize); 
     Log.i("maxBounds: ", "x: " + maxBoundsX + " ; " + "y: " + maxBoundsY); 


     /*setting start pos*/ 

     //We start on the lower left part of the screen 

     leftIndex = moveSize * (-1); 
     rightIndex = 0; 
     bottomIndex = moveSize * maxBoundsY; 
     topIndex = moveSize * (maxBoundsY - 1); 


     //Setting contentView, bitmap, and canvas 

     contentView = new View(this); 
     contentView.setOnClickListener(this); 

     bitmapToDrawOn = Bitmap.createBitmap(ScreenSizer.getScreenWidth(this), ScreenSizer.getScreenHeight(this), Bitmap.Config.ARGB_8888); 
     canvas = new Canvas(bitmapToDrawOn); 

     contentView.setBackground(new BitmapDrawable(getResources(), bitmapToDrawOn)); 
     setContentView(contentView); 

     /*starts drawing*/ 
     handler.post(runnable); 


    } 

    /** 
    * Draws a square to the next direction 
    * 
    * @param direction the direction to draw the next square 
    */ 
    private void drawRectPls(Direction direction) { 

     if (direction.equals(Direction.RIGHT)) { 
      leftIndex += moveSize; 
      rightIndex += moveSize; 
     } else if (direction.equals(Direction.UP)) { 
      topIndex -= moveSize; 
      bottomIndex -= moveSize; 
     } else if (direction.equals(Direction.LEFT)) { 
      leftIndex -= moveSize; 
      rightIndex -= moveSize; 
     } else if (direction.equals(Direction.DOWN)) { 
      topIndex += moveSize; 
      bottomIndex += moveSize; 
     } 


     addRectToCanvas(); 
     contentView.setBackground(new BitmapDrawable(getResources(), bitmapToDrawOn)); 
     Log.i("drawRect", "direction: " + currentDirection); 

    } 


    private void addRectToCanvas() { 
     paint.setColor(Color.argb(255, 100, 100, 255)); 
     canvas.drawRect(leftIndex, topIndex, rightIndex, bottomIndex, paint); 
    } 

    /** 
    * Upon tapping the screen the the snake is changing direction, one way simple interaction 
    */ 
    @Override 
    public void onClick(View v) { 

     if (v.equals(contentView)) { 

      if (currentDirection.equals(Direction.RIGHT)) { 
       currentDirection = Direction.UP; 
      } else if (currentDirection.equals(Direction.UP)) { 
       currentDirection = Direction.LEFT; 
      } else if (currentDirection.equals(Direction.LEFT)) { 
       currentDirection = Direction.DOWN; 
      } else if (currentDirection.equals(Direction.DOWN)) { 
       currentDirection = Direction.RIGHT; 
      } 
     } 
    } 


    /** 
    * Just getting the size of a square. Searching for an integer that divides both screen's width and height 
    * @return 
    */ 
    private int searchForOptimalMoveSize() { 
     int i; 
     for (i = 16; i <= 64; i++) { 
      Log.i("iter", "i= " + i); 
      if (ScreenSizer.getScreenWidth(this) % i == 0) { 
       Log.i("iter", ScreenSizer.getScreenWidth(this) + "%" + i + " =0 !"); 
       if (ScreenSizer.getScreenHeight(this) % i == 0) { 
        Log.i("iter", ScreenSizer.getScreenHeight(this) + "%" + i + " =0 !"); 
        return i; 
       } 
      } 
     } 
     return -1; 
    } 


    /** 
    * Stops the handler 
    */ 
    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     handler.removeCallbacks(runnable); 
    } 
} 

EDIT:

Я изменил свой код, теперь вид содержит все данные и я использую onDraw и недействительны методы, подобные Phi Липп предложил.

Результат немного лучше, но Я все еще чувствую, что взаимодействие с пользователем приводит к изменению направления лагги.

Возможно, что-то я должен делать с помощью нитей?

public class SpiralView extends View implements View.OnClickListener { 

    int leftIndex; ///<Indexes for square drawing 
    int topIndex; 
    int rightIndex; 
    int bottomIndex; 

    int speedInMillis = 50; 

    int moveSize; ///<Sze in px to move drawer to draw another square 

    int maxBoundsX; ///<The max number of squares in X axis 
    int maxBoundsY; ///<The max number of squares in Y axis 


    Paint paint = new Paint(); 
    Direction currentDirection = Direction.RIGHT; ///< RIGHT,LEFT,UP,DOWN directions. default is RIGHT 


    public void setUp(int moveSize, int maxBoundsX, int maxBoundsY) { 
     this.moveSize = moveSize; 
     this.maxBoundsX = maxBoundsX; 
     this.maxBoundsY = maxBoundsY; 
     this.leftIndex = moveSize * (-1); 
     this.rightIndex = 0; 
     this.bottomIndex = moveSize * (maxBoundsY); 
     this.topIndex = moveSize * (maxBoundsY - 1); 

    } 

    public SpiralView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     setOnClickListener(this); 


    } 

    /** 
    * Draws a square to the next direction 
    * 
    * @param direction the direction to draw the next square 
    */ 
    private void drawOnPlease(Direction direction, Canvas canvas) { 

     if (direction.equals(Direction.RIGHT)) { 
      leftIndex += moveSize; 
      rightIndex += moveSize; 
     } else if (direction.equals(Direction.UP)) { 
      topIndex -= moveSize; 
      bottomIndex -= moveSize; 
     } else if (direction.equals(Direction.LEFT)) { 
      leftIndex -= moveSize; 
      rightIndex -= moveSize; 
     } else if (direction.equals(Direction.DOWN)) { 
      topIndex += moveSize; 
      bottomIndex += moveSize; 
     } 


     Log.i("drawRect", "direction: " + currentDirection); 
     Log.i("drawRect", "indexes: "+topIndex+" , "+rightIndex+" ," +bottomIndex+" , "+leftIndex); 

     addRectToCanvas(canvas); 

    } 

    private void addRectToCanvas(Canvas canvas) { 
     paint.setColor(Color.argb(255, 100, 100, 255)); 

     canvas.drawRect(leftIndex, topIndex, rightIndex, bottomIndex, paint); 
    } 


    @Override 
    protected void onDraw(Canvas canvas) { 

     try { 

      drawOnPlease(currentDirection, canvas); 

      synchronized (this) { 
       wait(speedInMillis); 
      } 


      invalidate(); 

     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 

    @Override 
    public void onClick(View v) { 

     if (currentDirection.equals(Direction.RIGHT)) { 
      currentDirection = Direction.UP; 
     } else if (currentDirection.equals(Direction.UP)) { 
      currentDirection = Direction.LEFT; 
     } else if (currentDirection.equals(Direction.LEFT)) { 
      currentDirection = Direction.DOWN; 
     } else if (currentDirection.equals(Direction.DOWN)) { 
      currentDirection = Direction.RIGHT; 
     } 

     //..? 
     invalidate(); 

    } 

} 
+0

Invalidate() внутри onDraw() - плохая идея - это вызовет рекурсивные вызовы onDraw(). Просто удалите его, и, вероятно, с вами все будет в порядке. – Andrey

+0

@ ruan65 Тогда что будет рисовать змею периодически? –

+0

'wait' на нитке ui ** всегда ** плохая идея – Blackbelt

ответ

1

Магическое число - 16 миллисекунд для андроида, чтобы перерисовать вид без рамки.

Оформить заказ этого видео от разработчиков Android, которые это объясняют. https://www.youtube.com/watch?v=CaMTIgxCSqU&index=25&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE

Особенно проверить это видео https://youtu.be/vkTn3Ule4Ps?t=54

Это объясняет, как использовать холст отсечение, чтобы не рисовать всю поверхность в каждом такте, гайка рисовать только то, что необходимо, чтобы быть сделать.

1

Не используйте обработчик для рисования с помощью холста. Вместо этого вы должны создать пользовательский вид и использовать метод onDraw (Canvas canvas). В этом методе вы можете нарисовать объект Canvas, как вы уже делали. Вызвав метод invalidate(), вы вызываете новый вызов onDraw(). В функции onTouch() или OnClick() вы также вызвать новый OnDraw вызов по телефону недействительными()

class SnakView extends View { 
    @Override 
    protected void onDraw(Canvas canvas) { 
    // draw on canvas 
    invalidate(); 
    } 

    @Override 
    public void onClick(View v) { 
    // handle the event 
    invalidate(); 
    } 
} 
+0

И как мне« ждать »в onDraw если я не могу использовать Handler? –

0

Вы можете попробовать и добавить android:hardwareAccelerated="true" в манифесте, в или.

Это будет работать для устройств с 3.0+.

Кроме того, ваш целевой уровень API должен быть 11.

Тогда он будет работать более гладко.

+0

аппаратное ускорение включено по умолчанию, начиная с Android 4, поэтому я сомневаюсь, что это устранит проблему OP. – aga