Autore Topic: Drag&Drop e LongClick insieme. Come fare?  (Letto 733 volte)

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Drag&Drop e LongClick insieme. Come fare?
« il: 28 Settembre 2017, 19:29:26 CEST »
0
Premetto che per il codice sono partito da qualcosa trovato in un tutorial e mi sono trascinato dietro la struttura del programma.

Gestisco il Drag&Drop(View.OnDragListenere e View.OnTouchListener) delle immagini in una griglia quadrata e inoltre vorrei anche abilitare il LongClick (View.OnLongClickListener)sulle immagini e questo non riesco a farlo!!
Forse il problema è legato alla gerarchia degli eventi, ma non ho trovato niente a riguardo.

Ho una GridLayout a cui aggiungo 4x4 LinearLayout a a cui ad ognuna aggiungo una ImageView.(Funziona ma non sono proprio sicuro che sia del tutto corretto..)
Associo anche i  relativi gestori degli  eventi...

Codice (Java): [Seleziona]
public class GameActivity extends AppCompatActivity {
   .....
   private int[][] hidden, game, bindingBox, palette, lockTable;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
        GridLayout gLayout = (GridLayout) findViewById(R.id.gridL);
        ....
        for(int i=0; i< (4);i++)
             for(int j=0; j< (4);j++) {
                ....    
                LinearLayout linearL = new LinearLayout(this);
                linearL.setOnDragListener(new MyDragListener());
                gLayout.addView(linearL);
                ..... <parametri ecc...>
                ImageView imageV = new ImageView(this);
                ..... <parametri ecc...>
                imageV.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.tessera));
                imageV.setOnLongClickListener(new MyLongClick());
                imageV.setOnTouchListener(new MyTouchListener());
                linearL.addView(imageV);
        }
}
private final class MyTouchListener implements View.OnTouchListener {
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                ClipData data = ClipData.newPlainText("", "");
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
                view.startDrag(data, shadowBuilder, view, 0);
                view.setVisibility(View.INVISIBLE);
                return true;
            } else {
                return false;
            }
        }
    }
class MyLongClick implements View.OnLongClickListener
    {
        @Override
        public boolean onLongClick(View v) {
            ImageView imV = (ImageView ) v;
            LinearLayout container = (LinearLayout) imV.getParent();
            int A = container.getId();
            CoordinataXY cXY = resolveBoxandTile(A,bindingBox);
            if(lockTable[cXY.x][cXY.y] == 1) lockTable[cXY.x][cXY.y] = 0;
            else lockTable[cXY.x][cXY.y] = 1;
            return false;
        }
}
class MyDragListener implements View.OnDragListener {
    @Override
    public boolean onDrag(View v, DragEvent event) {
            int action = event.getAction();
            int tot=0;
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                  // do nothing
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                  //  v.setBackgroundDrawable(enterShape);
                    break;
                case DragEvent.ACTION_DRAG_EXITED:
                  //  v.setBackgroundDrawable(normalShape);
                    break;
                case DragEvent.ACTION_DROP:
                    // Dropped, reassign View to ViewGroup
                    if(lockTable[contenitorePart.x][contenitorePart.y] == 0) {
                         View view = (View) event.getLocalState();
                         ViewGroup owner = (ViewGroup) view.getParent();
                         LinearLayout container = (LinearLayout) v;
                         owner.removeView(view);
                         container.addView(view);
                         view.setVisibility(View.VISIBLE);
                         View v2 = container.getChildAt(0);
                         container.removeView(v2);
                         owner.addView(v2);
                         v2.setVisibility(View.VISIBLE);
                   }
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                 //   v.setBackgroundDrawable(normalShape);
                default:
                    break;
            }
            return true;
        }
Quello che succede è che l'evento viene sempre intercettato da OnTouchListener che instrada il Drag&Drop: il LongClick viene bypassato!
« Ultima modifica: 28 Settembre 2017, 19:34:25 CEST da Sime »

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #1 il: 29 Settembre 2017, 09:46:54 CEST »
0
Questo succede perché MyTouchListener intercetta e "consuma" ACTION_DOWN.

Invece di gestire OnLongClickListener puoi estendere onTouch in modo da sollevare l'evento longClick dopo 1 secondo di ACTION_DOWN (o resetti il timer ad ACTION_MOVE ed ACTION_UP):


Codice (Java): [Seleziona]
final Handler handler = new Handler();
Runnable mLongPressed = new Runnable() {
    public void run() {
        /* Qui gestisci l'evento LongClick */
    }  
};

public boolean onTouch(View view, MotionEvent motionEvent){
    if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        handler.postDelayed(mLongPressed, 1000); //Genero l'evento in 1 secondo
        ClipData data = ClipData.newPlainText("", "");
        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        view.startDrag(data, shadowBuilder, view, 0);
        view.setVisibility(View.INVISIBLE);
        return true;
    }
    if ((motionEvent.getAction() == MotionEvent.ACTION_MOVE)||(motionEvent.getAction() == MotionEvent.ACTION_UP)) {
        handler.removeCallbacks(mLongPressed); //Cancello l'evento
    }
    return false;
}

Riferimento: https://stackoverflow.com/a/11679788/466938
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #2 il: 30 Settembre 2017, 17:56:41 CEST »
0
Quello che ho verificato nel mio programma è che vengono sempre eseguiti entrambi, sia il view.startDrag che il LongClick in entrabi i casi: sia che faccia un normale click che uno lungo e temporanealmente sempre nella stessa sequenza, prima il startDrag e poi il LongClick :-o

Quello che vorrei è che fossero mutuamente esclusivi. E' possibile?


Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #3 il: 02 Ottobre 2017, 11:09:45 CEST »
0
Questo è strano... se fai click corto il metodo MyTouchListener.onTouch dovrebbe rilevare un ACTION_UP e quindi cancellare l'evento posticipato. Per altro la startDrag dovrebbe essere rilevata solo se l'utente inizia un'operazione di trascinamento (ACTION_DOWN seguita da ACTION_MOVE), ma in questo caso la ACTION_MOVE dovrebbe anch'essa cancellare l'evento posticipato...

Prova a mettere una riga di log prima del codice con il commento "Cancello l'evento":

Codice (Java): [Seleziona]
    if ((motionEvent.getAction() == MotionEvent.ACTION_MOVE)||(motionEvent.getAction() == MotionEvent.ACTION_UP)) {
        Log.i("MyTouchListener.onTouch", "Annullo l'evento LongClick");
        handler.removeCallbacks(mLongPressed); //Cancello l'evento
    }
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #4 il: 03 Ottobre 2017, 09:15:09 CEST »
0
Il log appare nel logcat mai in maniera non coerente alle attese. Compare sia nel click lungo che a volte in un click corto.
Mi rimane difficile fare il bebug, provo con una altra soluzione, magari un doppio click.

Grazie per l'aiuto

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #5 il: 03 Ottobre 2017, 09:52:18 CEST »
0
Mmmh... forse dragListener e touchListener vanno in conflitto.

Prova a mettere handler.removeCallbacks(mLongPressed); anche in dragListener, all'evento ACTION_DRAG_STARTED.

Codice (Java): [Seleziona]
class MyDragListener implements View.OnDragListener {
    @Override
    public boolean onDrag(View v, DragEvent event) {
            int action = event.getAction();
            int tot=0;
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                  handler.removeCallbacks(mLongPressed);
                    break;

In alternativa utilizza due flag, uno per rilevare il movimento (ACTION_DRAG_STARTED, ACTION_MOVE) ed uno per identificare il longclick.

- Se è stato rilevato un drag, non esegue il codice in mLongPressed
- Se si è verificato un longclick (e quindi non c'è stato drag), non esegue il codice in ACTION_DROP

Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #6 il: 03 Ottobre 2017, 16:56:12 CEST »
0
Rispetto alla prima soluzione però dovrei duplicare il codice perchè

Runnable mLongPressed = new Runnable() { ... }

non è raggiungibile dalla classe MyDragListener



Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #7 il: 03 Ottobre 2017, 17:19:31 CEST »
0
Dichiara mLongPressed nell'activity anziché in MyTouchListener
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #8 il: 03 Ottobre 2017, 18:01:13 CEST »
0
Ci avevo già provato, mi da errore classe sconosciuta:

Unknown class: mLongPressed

Infatti non è possibile neanche fare:
Codice (Java): [Seleziona]
Runnable mLongPressed;
mLongPressed = new Runnable() {...}

stesso errore

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #9 il: 03 Ottobre 2017, 18:14:52 CEST »
0
Hai provato a scriverlo tutto in una riga?

Codice (Java): [Seleziona]
Runnable mLongPressed = new Runnable() {...}
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Sime

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei P8 Lite
  • Sistema operativo:
    Windows 7
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #10 il: 03 Ottobre 2017, 19:02:26 CEST »
0
In MyTouchListener ho fatto così!

Mentre
 
Codice (Java): [Seleziona]
final Handler handler = new Handler();
l'ho messo in activity, come ho detto non si può fare per Runnable mLongPressed;

A meno di non duplicare il codice, ma sarebbero due thread diversi, giusto?

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 804
  • Respect: +168
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 10 x64
Re:Drag&Drop e LongClick insieme. Come fare?
« Risposta #11 il: 05 Ottobre 2017, 10:41:01 CEST »
0

Come sarebbe "non si può fare"?

Se hai bisogno del riferimento alla View cliccata, puoi aggiungerlo estendendo Runnable:

Codice (Java): [Seleziona]
        final Handler handler = new Handler();

        MyRunnable mLongPressed = new MyRunnable();

        private class MyRunnable implements Runnable {

                public View v;

                public void run() {
                        ImageView imV = (ImageView ) v;
                        LinearLayout container = (LinearLayout) imV.getParent();
                        int A = container.getId();
                        CoordinataXY cXY = resolveBoxandTile(A,bindingBox);
                        if(lockTable[cXY.x][cXY.y] == 1) lockTable[cXY.x][cXY.y] = 0;
                        else lockTable[cXY.x][cXY.y] = 1;
                }
        };

        private final class MyTouchListener implements View.OnTouchListener {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                        if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                                mLongPressed.v = view; //Definisco il target
                                handler.postDelayed(mLongPressed, 1000); //Genero l'evento in 1 secondo
                                ClipData data = ClipData.newPlainText("", "");
                                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
                                view.startDrag(data, shadowBuilder, view, 0);
                                view.setVisibility(View.INVISIBLE);
                                return true;
                        }else if ((motionEvent.getAction() == MotionEvent.ACTION_MOVE)||(motionEvent.getAction() == MotionEvent.ACTION_UP)) {
                                handler.removeCallbacks(mLongPressed); //Cancello l'evento
                        }
                        return false;
                }
        }

        private class MyDragListener implements View.OnDragListener {
                @Override
                public boolean onDrag(View v, DragEvent event) {
                        int action = event.getAction();
                        int tot = 0;
                        switch (event.getAction()) {
                                case DragEvent.ACTION_DRAG_STARTED:
                                        handler.removeCallbacks(mLongPressed);
                                        //Annulla l'evento longclick
                                        break;
                                case DragEvent.ACTION_DRAG_ENTERED:
                                        //  v.setBackgroundDrawable(enterShape);
                                        break;
                                case DragEvent.ACTION_DRAG_EXITED:
                                        //  v.setBackgroundDrawable(normalShape);
                                        break;
                                case DragEvent.ACTION_DROP:
                                        // Dropped, reassign View to ViewGroup
                                        if (lockTable[contenitorePart.x][contenitorePart.y] == 0) {
                                                View view = (View) event.getLocalState();
                                                ViewGroup owner = (ViewGroup) view.getParent();
                                                LinearLayout container = (LinearLayout) v;
                                                owner.removeView(view);
                                                container.addView(view);
                                                view.setVisibility(View.VISIBLE);
                                                View v2 = container.getChildAt(0);
                                                container.removeView(v2);
                                                owner.addView(v2);
                                                v2.setVisibility(View.VISIBLE);
                                        }
                                        break;
                                case DragEvent.ACTION_DRAG_ENDED:
                                        //   v.setBackgroundDrawable(normalShape);
                                default:
                                        break;
                        }
                        return true;
                }
        }
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.