0

Basé sur les extraits de code suivants, je me demandais pourquoi le mouvement onFling n'est pas reconnu pour un GridView de Boutons (j'utilise Boutons au lieu d'autres Vues pour des raisons personnelles):Le mouvement onFling n'est pas reconnu pour un GridView de boutons

Voici mon MainActivity:

public class MainActivity extends AppCompatActivity { 
    private GridView grid; 
    GestureDetector gDetector; 

    private static final int SWIPE_MIN_DISTANCE = 120; 
    private static final int SWIPE_MAX_OFF_PATH = 250; 
    private static final int SWIPE_THRESHOLD_VELOCITY = 200; 

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

     grid = (GridView)findViewById(R.id.grid); 

     // 4X4 grid. 
     grid.setNumColumns(4); 

     ArrayList<Button> mButtons = new ArrayList<Button>(); 
     Button button = null; 

     for (int i = 0; i < 16; i++) { 
      button = new Button(this); 
      button.setText(i + ""); 
      mButtons.add(button); 
     } 

     grid.setAdapter(new CustomAdapter(this, mButtons)); 

     gDetector = new GestureDetector(this, new SimpleOnGestureListener() { 
      @Override 
      public boolean onDown(MotionEvent event) { 
       return true; 
      } 

      @Override 
      public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
           float velocityY) { 
       final int position = grid.pointToPosition(Math.round(e1.getX()), Math.round(e1.getY())); 

       if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) { 
        if (Math.abs(e1.getX() - e2.getX()) > SWIPE_MAX_OFF_PATH 
         || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { 
         return false; 
        } 
        if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(MainActivity.this, "top at index " + position, Toast.LENGTH_SHORT).show(); 
        } else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(MainActivity.this, "bottom at index " + position, Toast.LENGTH_SHORT).show(); 
        } 
       } else { 
        if (Math.abs(velocityX) < SWIPE_THRESHOLD_VELOCITY) { 
         return false; 
        } 
        if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(MainActivity.this, "left at index " + position, Toast.LENGTH_SHORT).show(); 
        } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(MainActivity.this, "right at index " + position, Toast.LENGTH_SHORT).show(); 
        } 
       } 

       return super.onFling(e1, e2, velocityX, velocityY); 
      } 
     }); 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    return gDetector.onTouchEvent(event); 
} 

... voici mon CustomAdapter:

public class CustomAdapter extends BaseAdapter { 
    private ArrayList<Button> mButtons = null; 
    private Context ctx; 

    public CustomAdapter(Context ctx, ArrayList<Button> button) { 
     this.ctx = ctx; 
     this.mButtons = button; 
    } 

    @Override 
    public int getCount() { 
     return mButtons.size(); 
    } 

    @Override 
    public Object getItem(int position) {return (Object) mButtons.get(position);} 

    @Override 
    public long getItemId(int position) { 

     // Position and id are synonymous. 
     return position; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     Button button; 

     // Assigns a view to convertView should it be null, otherwise, casts convertView to the 
     // correct View type. 
     if (convertView == null) { 
      button = mButtons.get(position); 
     } else { 
      button = (Button) convertView; 
     } 

     return button; 
    } 
} 

... et apparemment le coup, onFling, ne se serait reconnu sur la la moitié inférieure de l'écran lorsque GridView est défini sur wrap_content, alors que le balayage ne fonctionnerait pas du tout lorsque GridView est défini sur match_parent. Voici la grille fixée à wrap_content avec le swipe ne travaillant sur la place fermée comme suit:

enter image description here

beaucoup apprécié.

+1

Votre 'onTouchEvent()' dans l'activité n'a pas été appelé pour les boutons qui dépassent, car les boutons ont consommé les événements tactiles. Vous devrez peut-être remplacer une classe de vue parent (par exemple, gridview) et indiquer votre intérêt à intercepter l'événement tactile à l'aide de onInterceptTouchEvent(). Voir http://stackoverflow.com/questions/9181529/detect-fling-gesture-over-clickable-items – headuck

+0

@headuck Merci pour le commentaire. Bien que pourriez-vous me fournir un exemple de code qui remplacerait GridView en fonction des extraits ci-dessus? Je l'apprécierais grandement. – DaveNOTDavid

Répondre

1

Étant donné que les boutons ont consommé les événements tactiles avant d'atteindre onTouchEvent() de l'activité, le détecteur de mouvement ne reçoit jamais les événements sur les boutons. Vous devrez peut-être remplacer une classe de vue parent des boutons, par ex. GridView, pour intercepter les événements tactiles. Voici un exemple d'une classe GestureDetectGridView qui s'étend GridView.

public class GestureDetectGridView extends GridView { 
    private GestureDetector gDetector; 
    private boolean flingConfirmed = false; 
    private float mTouchX; 
    private float mTouchY; 

    private static final int SWIPE_MIN_DISTANCE = 120; 
    private static final int SWIPE_MAX_OFF_PATH = 250; 
    private static final int SWIPE_THRESHOLD_VELOCITY = 200; 

    // Boiler plate view constructors 
    public GestureDetectGridView(Context context) { 
     super(context); 
     init(context); 
    } 

    public GestureDetectGridView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(context); 
    } 

    public GestureDetectGridView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(context); 
    } 

    @TargetApi(21) 
    public GestureDetectGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
     super(context, attrs, defStyleAttr, defStyleRes); 
     init(context); 
    } 

    // Sets up gesture detector, moved from your original MainActivity 

    private void init(final Context context) { 
     gDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { 
      @Override 
      public boolean onDown(MotionEvent event) { 
       return true; 
      } 

      @Override 
      public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
            float velocityY) { 
       final int position = GestureDetectGridView.this.pointToPosition(Math.round(e1.getX()), Math.round(e1.getY())); 

       if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) { 
        if (Math.abs(e1.getX() - e2.getX()) > SWIPE_MAX_OFF_PATH 
          || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { 
         return false; 
        } 
        if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(context, "top at index " + position, Toast.LENGTH_SHORT).show(); 
        } else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(context, "bottom at index " + position, Toast.LENGTH_SHORT).show(); 
        } 
       } else { 
        if (Math.abs(velocityX) < SWIPE_THRESHOLD_VELOCITY) { 
         return false; 
        } 
        if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(context, "left at index " + position, Toast.LENGTH_SHORT).show(); 
        } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE) { 
         Toast.makeText(context, "right at index " + position, Toast.LENGTH_SHORT).show(); 
        } 
       } 
       return super.onFling(e1, e2, velocityX, velocityY); 
      } 
     }); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     int action = ev.getActionMasked(); 
     gDetector.onTouchEvent(ev); 
     // Determine whether we need to start intercepting all touch events 
     // such that the buttons would no longer receive further touch events 
     // Return true if the fling gesture is confirmed 
     if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
      flingConfirmed = false; 
     } else if (action == MotionEvent.ACTION_DOWN) { 
      mTouchX = ev.getX(); 
      mTouchY = ev.getY(); 
     } else { 
      // short cut just in case 
      if (flingConfirmed) { 
       return true; 
      } 
      float dX = (Math.abs(ev.getX() - mTouchX)); 
      float dY = (Math.abs(ev.getY() - mTouchY)); 
      if ((dX > SWIPE_MIN_DISTANCE) || (dY > SWIPE_MIN_DISTANCE)) { 
       flingConfirmed = true; 
       return true; 
      } 
     } 
     return super.onInterceptTouchEvent(ev); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     return gDetector.onTouchEvent(ev); 
    } 

} 

Pour l'utiliser, modifier le GridView de la mise en/activité à utiliser à la place de cette classe, et supprimer les codes de détection de geste de l'activité (qui ont été déplacés dans cette classe). Vous devrez peut-être utiliser des rappels, etc. pour gérer les événements en dehors de la vue.