Esse blog sempre é "alimentado" com dúvidas de alunos e colegas meus que eu me atrevo a querer resolver. Hoje pela manhã, chegou um amigo e me perguntou se eu já tinha feito algo com Multi-Touch no Android. Falei que já tinha feito no iPhone, então fui à caça. Achei um ótimo exemplo no site do próprio Android. Quem quiser pode baixar via SVN aqui.
Esse exemplo demonstra a utilização do "efeito pinça" que aumenta o diminui um objeto na tela fazendo movimentos com os dedos similares aos de uma pinça.
O que é preciso é criar um objeto da classe android.view.ScaleGestureDetector passando em seu construtor o contexto (Context) e um objeto que implementa a interface ScaleGestureDetector.OnScaleGestureListener. Esse objeto será notificado quando o "evento de pinça" for disparado.
public class TouchExampleView extends View {
private static final int INVALID_POINTER_ID = -1;
private Drawable mIcon;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public TouchExampleView(Context context) {
this(context, null, 0);
}
public TouchExampleView(Context context,
AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchExampleView(Context context,
AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// A imagem será o ícone da aplicação
mIcon = context.getResources().
getDrawable(R.drawable.icon);
// Define os limites da imagem
mIcon.setBounds(0, 0,
mIcon.getIntrinsicWidth(),
mIcon.getIntrinsicHeight());
// Cria o objeto que capturará o "evento de pinça"
mScaleDetector = new ScaleGestureDetector(
context, new ScaleListener());
}
@Override
// Esse método servirá pra mover a imagem na tela
// e delegar o multi-touch pro ScaleGestureDetector
public boolean onTouchEvent(MotionEvent ev) {
// Delega o evento pro ScaleGestureDetector
// que verificará se o multitouch foi utilizado
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
// Ao tocar na tela, salva o local tocado
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex =
ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Ao mover, checa se o ScaleGestureDetector
// não está processando algum gesto
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
// Salva o deslocamento feito ao mover
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
// nenhum dedo da tela, ponto inválido
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
// nenhum dedo da tela, ponto inválido
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
// Ao retirar um dos dedos, atualize o ponto
// com o dedo que ficou na tela
final int pointerIndex =
(ev.getAction() &
MotionEvent.ACTION_POINTER_ID_MASK)
>> MotionEvent.ACTION_POINTER_ID_SHIFT;
final int pointerId =
ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex =
pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId =
ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// Reposicona a matriz do canvas
canvas.translate(mPosX, mPosY);
// Define a escala (zoom) do canvas
canvas.scale(mScaleFactor, mScaleFactor);
// Redesenha a imagem
mIcon.draw(canvas);
canvas.restore();
}
// classe que ouve o evento de multi-touch
// e calcula a escala
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener{
@Override
public boolean onScale(ScaleGestureDetector d) {
mScaleFactor *= d.getScaleFactor();
// Não permite que o objeto fique
// muito grande nem muito pequeno
mScaleFactor =
Math.max(0.5f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
Algumas considerações sobre esse post:
- Para testar esse exemplo você precisará do dispositivo real, afinal você não tem dois cliques de mouse :)
- O suporte a multi touch depende do aparelho;
- API de MultiTouch está disponível apenas para aparelhos com Android 2.0 ou superior.
Abaixo temos a aplicação executando no Motorola FlipOut.
É isso pessoal! Estou com muita coisa pra fazer, estou tentando terminar o mestrado, trabalhando e ainda dando aulas. Quaisquer dúvidas, postem seus comentários abaixo ou consulte o post orginal do blog do android.
Editado pra tentar atender o primeiro comentário desse post.
public class MinhaView extends View {
private Paint bluePaint;
private ArrayList<Point> points;
public MinhaView(Context c) {
this(c, null);
}
public MinhaView(Context c, AttributeSet attrs) {
super(c, attrs);
points = new ArrayList<Point>();
bluePaint = new Paint();
bluePaint.setARGB(255, 0, 0, 255);
setFocusable(true);
}
public boolean onTouchEvent(MotionEvent event){
// Se tocou na tela ou está movendo o dedo
if (event.getAction() ==
MotionEvent.ACTION_DOWN ||
event.getAction() ==
MotionEvent.ACTION_MOVE) {
// Pegue os pontos na tela e alimente a lista
// Normalmente são 1 ou 2 pontos
for(int i=1; i <= event.getPointerCount(); i++){
Point p = new Point(
(int) event.getX(event.getPointerId(i)),
(int) event.getY(event.getPointerId(i)));
points.add(p);
}
// Solicita que a view seja redesenhada
invalidate();
}
return true;
}
public void draw(Canvas canvas) {
super.draw(canvas);
// percorre a lista de pontos
while (points.size() > 0) {
Point p = points.remove(0);
canvas.drawCircle(p.x, p.y, 50, bluePaint);
}
// Limpa a lista pra evitar lixo
points.clear();
}
}
4br4ç05,
nglauber
3 comentários:
Olá!
Tudo bem?
Sou novo nesse tipo de programação e gostaria de saber como faço para criar e mover dois objetos independentes com o touch.
Obrigado
Luc
Olá caro amigo,
Gostaria de saber se tem alguma atualização para Android que habilite o dual-touch para aplicações que necessitam do recurso?
Ou isso pode ser feito via programação modificando o Kernel do Sistema?
oi Rafael,
Isso vai depender primeiramente do seu hardware suportar.
4br4ç05,
nglauber
Postar um comentário