0

다음 코드 단편을 기반으로, onFling 제스처가 Button의 GridView에 대해 인식되지 않는 이유가 궁금했습니다 (개인적인 이유로 Button을 다른보기 대신 사용함) :onFling 제스처가 Button의 GridView에 대해 인식되지 않습니다.

가 여기 내 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); 
} 

... 여기 내 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; 
    } 
} 

... 그리고 분명히 슬쩍는 onFling 만에 인정받을 것 GridView가 wrap_content로 설정되어있을 때 화면의 아래쪽 절반, GridView가 match_parent로 설정된 경우 스 와이프가 전혀 작동하지 않습니다.

enter image description here

이 많은 감사 : 여기에 다음과 같이 슬쩍 만 동봉 광장에 작업에 wrap_content로 설정 그리드입니다.

+1

버튼이 터치 이벤트를 소비했기 때문에 버튼에서 손가락을 떼었을 때 'onTouchEvent()'이 (가) 호출되지 않았습니다. 상위 뷰 클래스 (예 : gridview)를 재정의하고 onInterceptTouchEvent()를 사용하여 터치 이벤트를 가로 채기 위해 관심을 표시해야 할 수 있습니다. http://stackoverflow.com/questions/9181529/detect-fling-gesture-over-clickable-items – headuck

+0

@headuck 댓글을 주셔서 감사합니다. 비록 당신이 위의 스 니펫을 기반으로 GridView를 재정의 할 샘플 코드를 제게 제공 할 수 있습니까? 나는 그것을 크게 고맙게 생각한다. – DaveNOTDavid

답변

1

동작의 onTouchEvent()에 도달하기 전에 단추가 터치 이벤트를 사용했기 때문에 제스처 감지기는 단추를 통해 이벤트를 수신하지 않습니다. 버튼의 부모보기 클래스를 재정의해야 할 수 있습니다 (예 : GridView, 터치 이벤트를 가로 채기.

GestureDetectGridView의 예는 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); 
    } 

} 

이것을 사용하는 대신이 클래스를 사용하는 레이아웃/활동 GridView을 변경하고 (이 클래스로 이동 된) 활동에서 제스처 검출 코드를 제거한다. 뷰 밖의 이벤트를 처리하려면 콜백 등을 사용해야 할 수도 있습니다.