พุ่งการตรวจจับท่าทางบนเค้าโครงตาราง


1105

ฉันต้องการให้ระบบflingตรวจจับท่าทางทำงานในแอปพลิเคชัน Android ของฉัน

สิ่งที่ฉันมีคือGridLayoutที่มี 9 ImageViewวิ แหล่งที่สามารถพบได้ที่นี่: Romain ชายของตารางเค้าโครง

ไฟล์ที่ฉันถ่ายนั้นมาจากแอปพลิเคชั่น Photostreamของ Romain Guy และได้รับการดัดแปลงเพียงเล็กน้อยเท่านั้น

สำหรับสถานการณ์การคลิกง่ายฉันจำเป็นต้องตั้งค่าเฉพาะonClickListenerสำหรับแต่ละImageViewฉันจะเพิ่มให้เป็นหลักซึ่งการดำเนินการactivity ดูเหมือนว่าอนันต์ซับซ้อนมากขึ้นที่จะใช้สิ่งที่ตระหนักถึงView.OnClickListener flingฉันคิดว่านี่เป็นเพราะมันอาจจะขยายviewsหรือไม่

  • หากกิจกรรมของฉันดำเนินการ OnGestureListenerฉันไม่ทราบวิธีตั้งค่าให้เป็นผู้ฟังท่าทางสัมผัสสำหรับGridหรือImageมุมมองที่ฉันเพิ่ม

    public class SelectFilterActivity extends Activity implements
       View.OnClickListener, OnGestureListener { ...
    
  • หากกิจกรรมของฉันดำเนินการ OnTouchListenerฉันจะไม่มี onFlingวิธีการoverride(มีสองเหตุการณ์เป็นพารามิเตอร์ที่อนุญาตให้ฉันพิจารณาว่าการพุ่งนั้นน่าสังเกต)

    public class SelectFilterActivity extends Activity implements
        View.OnClickListener, OnTouchListener { ...
    
  • ถ้าฉันสร้างที่กำหนดเองViewเช่นGestureImageViewที่ขยายImageViewฉันไม่ทราบวิธีที่จะบอกกิจกรรมที่flingเกิดขึ้นจากมุมมอง ไม่ว่าในกรณีใดฉันลองและไม่ได้เรียกวิธีการเมื่อฉันสัมผัสหน้าจอ

ฉันแค่ต้องการตัวอย่างที่ชัดเจนของการทำงานข้ามมุมมองนี้ ฉันควรแนบไฟล์นี้listenerเมื่อไรและอย่างไร ฉันต้องสามารถตรวจสอบการคลิกเพียงครั้งเดียวได้เช่นกัน

// Gesture detection
mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        int dx = (int) (e2.getX() - e1.getX());
        // don't accept the fling if it's too short
        // as it may conflict with a button push
        if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.absvelocityY)) {
            if (velocityX > 0) {
                moveRight();
            } else {
                moveLeft();
            }
            return true;
        } else {
            return false;
        }
    }
});

เป็นไปได้ไหมที่จะวางมุมมองที่โปร่งใสเหนือส่วนบนของหน้าจอเพื่อจับภาพหน้าจอ

หากฉันเลือกที่จะไม่inflateดูมุมมองภาพลูกของฉันจาก XML ฉันสามารถส่งผ่านGestureDetectorพารามิเตอร์เป็นตัวสร้างไปยังคลาสย่อยใหม่ImageViewที่ฉันสร้างขึ้นได้หรือไม่

นี้เป็นกิจกรรมที่ง่ายมากที่ฉันพยายามที่จะได้รับflingการตรวจสอบการทำงานของ: SelectFilterActivity (ดัดแปลงมาจาก Photostream)

ฉันเคยดูที่แหล่งเหล่านี้:

ไม่มีอะไรได้ผลสำหรับฉันจนถึงตอนนี้และฉันก็หวังสำหรับพอยน์เตอร์


วิธีแก้ปัญหานี้ กรุณาตอบstackoverflow.com/questions/60464912/…
Bishwash

คำตอบ:


818

ขอบคุณCode Shogunซึ่งฉันปรับรหัสให้เหมาะกับสถานการณ์ของฉัน

ให้กิจกรรมของคุณดำเนินการOnClickListenerตามปกติ:

public class SelectFilterActivity extends Activity implements OnClickListener {

  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;
  private GestureDetector gestureDetector;
  View.OnTouchListener gestureListener;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /* ... */

    // Gesture detection
    gestureDetector = new GestureDetector(this, new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
      public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
      }
    };

  }

  class MyGestureDetector extends SimpleOnGestureListener {
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
      try {
        if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
          return false;
        // right to left swipe
        if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
          Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
        } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
          Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
        }
      } catch (Exception e) {
         // nothing
      }
      return false;
    }

    @Override
    public boolean onDown(MotionEvent e) {
      return true;
    }
  }
}

แนบฟังท่าทางสัมผัสของคุณกับมุมมองทั้งหมดที่คุณเพิ่มไปยังเค้าโครงหลัก

// Do this for each view added to the grid
imageView.setOnClickListener(SelectFilterActivity.this); 
imageView.setOnTouchListener(gestureListener);

ดูอย่างน่าเกรงขามในขณะที่วิธีการแทนที่ของคุณถูกโจมตีทั้งonClick(View v)กิจกรรมและonFlingฟังท่าทางสัมผัส

public void onClick(View v) {
  Filter f = (Filter) v.getTag();
  FilterFullscreenActivity.show(this, input, f);
}

การฟ้อนรำ 'fling' เป็นทางเลือก แต่สนับสนุน


109
ขอบคุณสำหรับรหัสนี้! มันมีประโยชน์มาก อย่างไรก็ตามฉันวิ่งไปจับที่น่าผิดหวังอย่างมากในขณะที่พยายามทำให้ท่าทางทำงาน ใน SimpleOnGestureListener ของฉันฉันต้องแทนที่ onDown เพื่อให้ท่าทางการลงทะเบียน มันสามารถกลับมาจริง แต่ฉันต้องกำหนด PS: ฉันไม่รู้ว่าการแก้ไข API หรือฮาร์ดแวร์ของฉัน แต่ฉันใช้ 1.5 ใน HTC Droid Eris
Cdsboy

ฉันลองใช้รหัสของคุณและไม่สำคัญว่าฉันจะรูดหรือคลิก (ด้วยเมาส์ของฉันทำให้ฉันทำงานในโปรแกรมจำลอง) ฉันจะได้รับ Toast ที่ฉันกำหนดไว้ในวิธีการ onClick ดังนั้นโปรแกรมจำลองการทำงานจะตรวจจับการคลิกโดยไม่ต้อง swipes ทำไมถึงเป็นเช่นนั้น
lomza

ฉันลองใช้รหัสนี้ แต่ใช้ไม่ได้ ยังคงไม่สามารถเลื่อนเลยเมื่อฉันใช้ฟัง onClick กับหนึ่งในมุมมองของเด็กในมุมมองแกลเลอรี่ของฉัน
Jonathan

Iomza: คุณลองใส่ตัวแบ่งคำสั่งแล้วก้าวผ่านโค้ดของคุณหรือไม่?
IgorGanapolsky

รุ่งโรจน์สำหรับการใช้ชั้นใน! วิธีการที่สะอาดมาก
IgorGanapolsky

211

หนึ่งในคำตอบข้างต้นกล่าวถึงการจัดการความหนาแน่นของพิกเซลที่แตกต่างกัน แต่แนะนำให้คำนวณพารามิเตอร์การปัดด้วยมือ เป็นที่น่าสังเกตว่าคุณสามารถรับค่าที่เหมาะสมจากระบบโดยใช้ViewConfigurationคลาส:

final ViewConfiguration vc = ViewConfiguration.get(getContext());
final int swipeMinDistance = vc.getScaledPagingTouchSlop();
final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity();
final int swipeMaxOffPath = vc.getScaledTouchSlop();
// (there is also vc.getScaledMaximumFlingVelocity() one could check against)

ฉันสังเกตเห็นว่าการใช้ค่าเหล่านี้ทำให้ "ความรู้สึก" ของการเหวี่ยงมีความสอดคล้องกันมากขึ้นระหว่างแอปพลิเคชันและระบบที่เหลือ


11
ใช้ฉันและswipeMinDistance = vc.getScaledPagingTouchSlop() swipeMaxOffPath = vc.getScaledTouchSlop()
โทมัส Ahle

8
getScaledTouchSlopให้ผลลัพธ์ออฟเซ็ตน้อยมากอย่างเชื่องช้า ตัวอย่างเช่นเพียง 24 พิกเซลบนหน้าจอสูง 540 ซึ่งเป็นเรื่องยากมากที่จะให้มันอยู่ในช่วงนิ้ว : S
WonderCsabo

148

ฉันทำมันแตกต่างกันเล็กน้อยและเขียนคลาสตัวตรวจจับพิเศษที่ใช้งาน View.onTouchListener

onCreateเป็นเพียงเพิ่มลงในเค้าโครงที่ต่ำที่สุดดังนี้:

ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout);
lowestLayout.setOnTouchListener(activitySwipeDetector);

โดย id.lowestLayout คือ id.xxx สำหรับมุมมองที่ต่ำที่สุดในลำดับชั้นโครงร่างและ LowestLayout ถูกประกาศเป็น RelativeLayout

และจากนั้นก็มีคลาสของเครื่องตรวจจับการปัดกวาดกิจกรรมจริง:

public class ActivitySwipeDetector implements View.OnTouchListener {

static final String logTag = "ActivitySwipeDetector";
private Activity activity;
static final int MIN_DISTANCE = 100;
private float downX, downY, upX, upY;

public ActivitySwipeDetector(Activity activity){
    this.activity = activity;
}

public void onRightSwipe(){
    Log.i(logTag, "RightToLeftSwipe!");
    activity.doSomething();
}

public void onLeftSwipe(){
    Log.i(logTag, "LeftToRightSwipe!");
    activity.doSomething();
}

public void onDownSwipe(){
    Log.i(logTag, "onTopToBottomSwipe!");
    activity.doSomething();
}

public void onUpSwipe(){
    Log.i(logTag, "onBottomToTopSwipe!");
    activity.doSomething();
}

public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

       // swipe horizontal?
        if(Math.abs(deltaX) > Math.abs(deltaY))
        {
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX > 0) { this.onRightSwipe(); return true; }
                if(deltaX < 0) { this.onLeftSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }
        // swipe vertical?
        else 
        {
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onDownSwipe(); return true; }
                if(deltaY > 0) { this.onUpSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }

            return true;
        }
    }
    return false;
}

}

ใช้งานได้ดีจริงๆสำหรับฉัน!


1
ที่จริงแล้วมันทำให้มันง่ายขึ้นมากสำหรับฉันที่จะใช้ฟังก์ชั่นท่าทางและต้องเดินสาย "น้อย": D ขอบคุณ @Thomas
nemesisfixx

5
ดูเหมือนว่าคลาสยูทิลิตี้ที่เรียบร้อย - แต่ฉันคิดว่าวิธีการทั้งสี่ของคุณใน ... swipe () วิธีการควรเป็นส่วนต่อประสาน
คนอยู่ที่ไหนสักแห่ง

2
ผลตอบแทนเหล่านี้ไม่ควรอยู่ที่นั่น (บรรทัด "เราไม่ใช้เหตุการณ์") ใช่ไหม มันปิดการใช้งานคุณสมบัติการเลื่อนแนวตั้ง
Marek Sebera

5
โดยเฉพาะวิธีการ onTouch () ก่อนถ้าเดลต้า X ไม่ใหญ่พอก็จะส่งคืนโดยไม่ตรวจสอบเดลต้า Y ผลลัพธ์ที่ได้คือไม่พบการปัดนิ้วซ้าย - ขวา ประการที่สองมันไม่ควรจะกลับมาเป็นจริงถ้ามันผ่านการค้นหาโดยไม่ต้องปัด ประการที่สามมันไม่ควรจะกลับมาจริงเมื่อลงมือทำ สิ่งนี้จะป้องกันผู้ฟังคนอื่น ๆ เช่น onClick ไม่ให้ทำงาน
Jeffrey Blattman

1
@Piotr ไม่ใช่ปัญหาตราบใดที่ออบเจ็กต์ที่อ้างอิงมีขอบเขตเดียวกับกิจกรรม ปัญหาเกิดขึ้นเมื่อคุณทำการอ้างอิงถึงกิจกรรมในสถานที่ที่มีขอบเขตขนาดใหญ่กว่ากิจกรรม ... เช่นจากสมาชิกแบบคงที่
Jeffrey Blattman

94

ฉันแก้ไขและแก้ไขเล็กน้อยจากThomas Fankhauser

ทั้งระบบประกอบด้วยไฟล์สองไฟล์คือSwipeInterfaceและActivitySwipeDetector


SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void bottom2top(View v);

    public void left2right(View v);

    public void right2left(View v);

    public void top2bottom(View v);

}

เครื่องตรวจจับ

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;

    public ActivitySwipeDetector(SwipeInterface activity){
        this.activity = activity;
    }

    public void onRightToLeftSwipe(View v){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.right2left(v);
    }

    public void onLeftToRightSwipe(View v){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.left2right(v);
    }

    public void onTopToBottomSwipe(View v){
        Log.i(logTag, "onTopToBottomSwipe!");
        activity.top2bottom(v);
    }

    public void onBottomToTopSwipe(View v){
        Log.i(logTag, "onBottomToTopSwipe!");
        activity.bottom2top(v);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            // swipe horizontal?
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
            }

            // swipe vertical?
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; }
                if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                v.performClick();
            }
        }
        }
        return false;
    }

}

มันถูกใช้แบบนี้:

ActivitySwipeDetector swipe = new ActivitySwipeDetector(this);
LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout);
swipe_layout.setOnTouchListener(swipe);

และในการดำเนินการActivityคุณจำเป็นต้องใช้วิธีการต่าง ๆ จากSwipeInterfaceและคุณสามารถค้นหาว่ามีการเรียกดูเหตุการณ์การกวาดนิ้วใด

@Override
public void left2right(View v) {
    switch(v.getId()){
        case R.id.swipe_layout:
            // do your stuff here
        break;
    }       
}

ผมแก้ไขเล็กน้อยอีกครั้งให้ดูv.performClick();ซึ่งจะใช้ในการไม่ใช้เหตุการณ์ที่ OnClickListener ตั้งอยู่บนมุมมองเดียวกันถ้า
มาเร็ค Sebera

สวัสดีฉันเป็นผู้เริ่มต้นทั้งหมดดังนั้นคำถามนี้อาจชัดเจนหรือไม่สำคัญ แต่โปรดตอบ ส่วนที่คุณเขียนมันถูกใช้เป็น: ActivitySwipeDetector swipe = new ActivitySwipeDetector (นี้); ข้อความนี้จะเป็นส่วนหนึ่งของ MainActivity ใช่ไหม จากนั้น "นี่" จะเป็นกิจกรรมของ MainActivity ในขณะที่ตัวสร้างใช้อินสแตนซ์ของ SwipeInterface กรุณาช่วยฉันออกจากที่นี่ ขอบคุณมาก.
Chocolava

@Chocolava สร้างคำถามใหม่ความคิดเห็นไม่ใช่สถานที่ที่ดีที่จะถามเช่นนี้
Marek Sebera

@MarekSebera สิ่งนี้ใช้ไม่ได้กับ ScrollView & ListView ใช่ไหม วิธีการจัดการกับพวกเขา?
Duc Tran

@silentbang อีกครั้งนี่ไม่ใช่ที่สำหรับถามคำถาม โปรดสร้างหัวข้อคำถามใหม่
Marek Sebera

65

รูดรหัสตรวจจับท่าทางข้างต้นมีประโยชน์มาก! อย่างไรก็ตามคุณอาจต้องการทำให้ความหนาแน่นของโซลูชันนี้ไม่เชื่อเรื่องพระเจ้าโดยใช้ค่าสัมพัทธ์ต่อไปนี้(REL_SWIPE)แทนค่าสัมบูรณ์(SWIPE_)

DisplayMetrics dm = getResources().getDisplayMetrics();

int REL_SWIPE_MIN_DISTANCE = (int)(SWIPE_MIN_DISTANCE * dm.densityDpi / 160.0f);
int REL_SWIPE_MAX_OFF_PATH = (int)(SWIPE_MAX_OFF_PATH * dm.densityDpi / 160.0f);
int REL_SWIPE_THRESHOLD_VELOCITY = (int)(SWIPE_THRESHOLD_VELOCITY * dm.densityDpi / 160.0f);

8
+1 เมื่อนำสิ่งนี้ขึ้นมา โปรดทราบว่ามีการแนะนำ DensityMetrics.densityDpi ใน API 4 สำหรับความเข้ากันได้แบบย้อนหลังกับ API 1 ให้ใช้ DensityMetrics.density แทน นี่จะเปลี่ยนการคำนวณเป็นเพียง SWIPE_MIN_DISTANCE * dm.density
Thane Anthem

คุณได้รับหมายเลข 160.0f ที่ไหน
IgorGanapolsky

developer.android.com/guide/practices/screens_support.htmlพิกเซลความหนาแน่นอิสระ (dp) การแปลงหน่วย dp เป็นพิกเซลหน้าจอนั้นง่าย: px = dp * (dpi / 160)
paiego

ฉันกำลังมองหาสิ่งนี้ทั้งหมด ไม่มีตัวอย่างของ onFling () บนอินเทอร์เน็ตที่มีสิ่งนี้ซึ่งจะนำไปสู่ ​​UX ที่ไม่ดี ขอบคุณ!
Sandy

160.0f นั้นมาจาก 160 DPI ซึ่งเป็นความหนาแน่นมาตรฐานตามที่ DP (พิกเซลอิสระหนาแน่น) DENSITY_MEDIUM int สุดท้ายคงที่สาธารณะเพิ่มใน API ระดับ 4 DPI quantized มาตรฐานสำหรับหน้าจอความหนาแน่นปานกลาง ค่าคงที่: 160 (0x000000a0)
paiego

35

วิธีแก้ไขปัญหาของฉันที่เสนอโดยThomas FankhauserและMarek Sebera (ไม่รองรับ swipes แนวตั้ง):

SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void onLeftToRight(View v);

    public void onRightToLeft(View v);
}

ActivitySwipeDetector.java

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    private float downX, downY;
    private long timeDown;
    private final float MIN_DISTANCE;
    private final int VELOCITY;
    private final float MAX_OFF_PATH;

    public ActivitySwipeDetector(Context context, SwipeInterface activity){
        this.activity = activity;
        final ViewConfiguration vc = ViewConfiguration.get(context);
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density;
        VELOCITY = vc.getScaledMinimumFlingVelocity();
        MAX_OFF_PATH = MIN_DISTANCE * 2;            
    }

    public void onRightToLeftSwipe(View v){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.onRightToLeft(v);
    }

    public void onLeftToRightSwipe(View v){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.onLeftToRight(v);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            Log.d("onTouch", "ACTION_DOWN");
            timeDown = System.currentTimeMillis();
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            Log.d("onTouch", "ACTION_UP");
            long timeUp = System.currentTimeMillis();
            float upX = event.getX();
            float upY = event.getY();

            float deltaX = downX - upX;
            float absDeltaX = Math.abs(deltaX); 
            float deltaY = downY - upY;
            float absDeltaY = Math.abs(deltaY);

            long time = timeUp - timeDown;

            if (absDeltaY > MAX_OFF_PATH) {
                Log.i(logTag, String.format("absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH));
                return v.performClick();
            }

            final long M_SEC = 1000;
            if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) {
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            } else {
                Log.i(logTag, String.format("absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE)));
                Log.i(logTag, String.format("absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC)));
            }

        }
        }
        return false;
    }

}

ใครช่วยบอกฉันหน่อยได้ไหมว่าจะเรียกชั้นเรียนได้อย่างไร ActivitySwipeDetector swipe = new ActivitySwipeDetector (สิ่งนี้); เห็นได้ชัดว่าให้ข้อผิดพลาดเนื่องจากไม่มีตัวสร้างดังกล่าว ฉันควรให้ ActivitySwipeDetector swipe = new ActivitySwipeDetector (สิ่งนี้เป็นโมฆะ);
abdfahim

@AbdullahFahim ActivitySwipeDetector (นี่ YourActivity.this);
Anton Kashpor

25

คำถามนี้เก่ามากและในเดือนกรกฎาคม 2011 Google เปิดตัวแพ็คเกจความเข้ากันได้การแก้ไข 3)ซึ่งรวมถึงการViewPagerทำงานกับ Android 1.6 ขึ้นไป GestureListenerคำตอบโพสต์สำหรับคำถามนี้ไม่รู้สึกสง่างามมากบน Android หากคุณกำลังมองหารหัสที่ใช้ในการสลับไปมาระหว่างภาพในแกลลอรี่ Android หรือเปลี่ยนมุมมองในแอป Play ViewPagerตลาดใหม่แล้วก็แน่นอน

นี่คือลิงค์สำหรับข้อมูลเพิ่มเติม:


ปัญหาหนึ่งของ ViewPager คือคุณไม่สามารถควบคุมพารามิเตอร์ระยะทางและความเร็วสำหรับท่าทางการเหวี่ยง
almalkawi

ViewPager ไม่ได้ใช้ในแกลเลอรี่
แอนโธนี

18

มีอินเทอร์เฟซในตัวที่คุณสามารถใช้ได้โดยตรงกับทุกท่าทาง:
นี่คือคำอธิบายสำหรับผู้ใช้ระดับพื้นฐาน: ป้อนคำอธิบายรูปภาพที่นี่ มี 2 ​​การนำเข้าที่ควรระวังในการเลือกว่าทั้งสองอย่างแตกต่างกัน ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


1
และขั้นตอนต่อไปคืออะไร? วิธีตั้งค่าฟังให้เป็นมุมมองเฉพาะ? และถ้ามุมมองนี้เป็นส่วนหนึ่งของส่วนได้อย่างไร
สแตน

16

มีข้อเสนอทางเว็บ (และหน้านี้) เพื่อใช้ ViewConfiguration getScaledTouchSlop ()SWIPE_MIN_DISTANCEจะมีค่าอุปกรณ์ปรับขนาดสำหรับ

getScaledTouchSlop()มีไว้สำหรับระยะ " เลื่อนเกณฑ์" ไม่รูด ระยะห่างเกณฑ์การเลื่อนจะต้องเล็กกว่าระยะทางเกณฑ์ "การสลับระหว่างหน้า" ตัวอย่างเช่นฟังก์ชั่นนี้ส่งคืน 12 พิกเซลใน Samsung GS2 ของฉันและตัวอย่างที่ยกมาในหน้านี้คือประมาณ 100 พิกเซล

ด้วย API ระดับ 8 (Android 2.2, Froyo) คุณจะได้รับgetScaledPagingTouchSlop()สำหรับการปัดหน้า บนอุปกรณ์ของฉันจะส่งคืน 24 (พิกเซล) ดังนั้นหากคุณอยู่ในระดับ API <8 ฉันคิดว่า "2 * getScaledTouchSlop()" ควรเป็นเกณฑ์การปัด "มาตรฐาน" แต่ผู้ใช้แอปพลิเคชันของฉันที่มีหน้าจอขนาดเล็กบอกฉันว่ามันน้อยเกินไป ... ในแอปพลิเคชันของฉันคุณสามารถเลื่อนในแนวตั้งและเปลี่ยนหน้าในแนวนอน ด้วยค่าที่เสนอบางครั้งพวกเขาเปลี่ยนหน้าแทนการเลื่อน


13

ยังเป็นการปรับปรุงเล็กน้อย

เหตุผลหลักสำหรับการลอง / จับบล็อกคือ e1 อาจเป็นโมฆะสำหรับการเคลื่อนไหวครั้งแรก นอกเหนือจากการลอง / จับให้รวมการทดสอบค่า Null และ Return คล้ายกับต่อไปนี้

if (e1 == null || e2 == null) return false;
try {
...
} catch (Exception e) {}
return false;

12

มีข้อมูลที่ยอดเยี่ยมมากมายที่นี่ น่าเสียดายที่โค้ดประมวลผลฟลิ้งก์จำนวนมากนี้กระจายอยู่ทั่วไซต์ต่าง ๆ ในสถานะที่สมบูรณ์แม้ว่าจะคิดว่านี่เป็นสิ่งจำเป็นสำหรับแอปพลิเคชั่นมากมาย

ฉันใช้เวลาในการสร้างผู้ฟังที่ตรวจสอบว่าตรงตามเงื่อนไขที่เหมาะสม ฉันได้เพิ่มผู้ฟังการขว้างปาหน้าที่เพิ่มการตรวจสอบเพิ่มเติมเพื่อให้แน่ใจว่าการปัดหน้าตรงตามเกณฑ์สำหรับการปัดหน้า ผู้ฟังทั้งสองนี้ช่วยให้คุณสามารถ จำกัด การเหวี่ยงไปยังแกนนอนหรือแนวตั้งได้อย่างง่ายดาย คุณสามารถดูวิธีการที่จะนำมาใช้ในมุมมองสำหรับการเลื่อนภาพ ฉันรับรู้ว่าคนที่นี่ได้ทำการวิจัยเกือบทั้งหมดแล้ว - ฉันเพิ่งรวบรวมมันไว้ในห้องสมุดที่ใช้งานได้

ไม่กี่วันที่ผ่านมาแสดงถึงการแทงครั้งแรกของฉันเมื่อเขียนโค้ดบน Android คาดหวังมากขึ้นที่จะมา


ฉันต้องการใช้การเลื่อนนิ้วด้วย 2 นิ้ว กรุณาช่วยฉันออกไป!
Gaurav Arora

12

นี่เป็นคำตอบที่รวมกันของสองคำตอบที่ด้านบนหากใครต้องการการนำไปใช้งาน

package com.yourapplication;

import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public abstract class OnSwipeListener implements View.OnTouchListener {

    private final GestureDetector gestureDetector;

    public OnSwipeListener(Context context){
        gestureDetector = new GestureDetector(context, new OnSwipeGestureListener(context));
        gestureDetector.setIsLongpressEnabled(false);
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    private final class OnSwipeGestureListener extends GestureDetector.SimpleOnGestureListener {

        private final int minSwipeDelta;
        private final int minSwipeVelocity;
        private final int maxSwipeVelocity;

        private OnSwipeGestureListener(Context context) {
            ViewConfiguration configuration = ViewConfiguration.get(context);
            // We think a swipe scrolls a full page.
            //minSwipeDelta = configuration.getScaledTouchSlop();
            minSwipeDelta = configuration.getScaledPagingTouchSlop();
            minSwipeVelocity = configuration.getScaledMinimumFlingVelocity();
            maxSwipeVelocity = configuration.getScaledMaximumFlingVelocity();
        }

        @Override
        public boolean onDown(MotionEvent event) {
            // Return true because we want system to report subsequent events to us.
            return true;
        }

        // NOTE: see http://stackoverflow.com/questions/937313/android-basic-gesture-detection
        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,
                               float velocityY) {

            boolean result = false;
            try {
                float deltaX = event2.getX() - event1.getX();
                float deltaY = event2.getY() - event1.getY();
                float absVelocityX = Math.abs(velocityX);
                float absVelocityY = Math.abs(velocityY);
                float absDeltaX = Math.abs(deltaX);
                float absDeltaY = Math.abs(deltaY);
                if (absDeltaX > absDeltaY) {
                    if (absDeltaX > minSwipeDelta && absVelocityX > minSwipeVelocity
                            && absVelocityX < maxSwipeVelocity) {
                        if (deltaX < 0) {
                            onSwipeLeft();
                        } else {
                            onSwipeRight();
                        }
                    }
                    result = true;
                } else if (absDeltaY > minSwipeDelta && absVelocityY > minSwipeVelocity
                        && absVelocityY < maxSwipeVelocity) {
                    if (deltaY < 0) {
                        onSwipeTop();
                    } else {
                        onSwipeBottom();
                    }
                }
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    public void onSwipeLeft() {}

    public void onSwipeRight() {}

    public void onSwipeTop() {}

    public void onSwipeBottom() {}
}

ขอบคุณสำหรับการดำเนินการที่ดีจริงๆ นอกจากนี้ผมจะแนะนำในการตรวจสอบabsDeltaY > minSwipeDelta, absVelocityY > minSwipeVelocity, absVelocityY < maxSwipeVelocityเฉพาะในกรณีที่หากminSwipeDelta ! = getScaledTouchSlop , minSwipeVelocity ! = getScaledMinimumFlingVelocity , maxSwipeVelocity ! = getScaledMaximumFlingVelocityคือการตรวจสอบเฉพาะในกรณีเหล่านี้เรียกว่า“เริ่มต้น” (ผมหมายถึง getScaledTouchSlop, getScaledMinimumFlingVelocity, getScaledMaximumFlingVelocity) ค่าปรับขนาดหรือมีการเปลี่ยนแปลงได้ตาม ความปรารถนาของตัวเอง
Elia12345

ประเด็นก็คือตามรหัสแหล่งที่มาค่า "เริ่มต้น" ที่กล่าวถึงแล้วจะถูกตรวจสอบโดย GestureDetector และ OnFling จะถูกกระตุ้นเมื่อพวกเขาได้รับการยืนยันเท่านั้น (โดยวิธีการกระตุ้นจะเกิดขึ้นเฉพาะในกรณีที่ACTION_UPไม่ใช่ACTION_MOVEหรือACTION_POINTER_UPเช่น ผลลัพธ์ของการรับรู้ท่าทางอย่างเต็มที่) (ฉันไม่ได้ตรวจสอบเวอร์ชัน API อื่น ๆ ดังนั้นความเห็นจะได้รับการชื่นชม)
Elia12345

11

คุณสามารถใช้ไลบรารีdroidQueryเพื่อจัดการการพุ่งการคลิกการคลิกแบบยาวและเหตุการณ์ที่กำหนดเอง การติดตั้งนั้นสร้างขึ้นจากคำตอบก่อนหน้าของฉันด้านล่าง แต่droidQueryมีไวยากรณ์ที่เรียบและเรียบง่าย:

//global variables    private boolean isSwiping = false;
private SwipeDetector.Direction swipeDirection = null;
private View v;//must be instantiated before next call.

//swipe-handling code
$.with(v).swipe(new Function() {
    @Override
    public void invoke($ droidQuery, Object... params) {
        if (params[0] == SwipeDetector.Direction.START)
            isSwiping = true;
        else if (params[0] == SwipeDetector.Direction.STOP) {
            if (isSwiping) {                    isSwiping = false;
                if (swipeDirection != null) {
                    switch(swipeDirection) {
                        case DOWN :                                //TODO: Down swipe complete, so do something
                            break;
                        case UP :
                            //TODO: Up swipe complete, so do something
                            break;
                        case LEFT :
                            //TODO: Left swipe complete, so do something
                            break;
                        case RIGHT :
                            //TODO: Right swipe complete, so do something
                            break;
                        default :                                break;
                    }
                }                }
        }
        else {
            swipeDirection = (SwipeDetector.Direction) params[0];
        }
    }
});

คำตอบเดิม

คำตอบนี้ใช้การรวมกันของส่วนประกอบจากคำตอบอื่น ๆ ที่นี่ มันประกอบด้วยSwipeDetectorชั้นเรียนซึ่งมีส่วนต่อประสานภายในสำหรับฟังเหตุการณ์ ฉันยังมี RelativeLayoutการแสดงวิธีการแทนที่ViewของonTouchวิธีการที่จะช่วยให้ทั้งสองเหตุการณ์รูดและเหตุการณ์ตรวจพบอื่น ๆ (เช่นการคลิกหรือคลิกยาว)

SwipeDetector

package self.philbrown;

import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * Detect Swipes on a per-view basis. Based on original code by Thomas Fankhauser on StackOverflow.com,
 * with adaptations by other authors (see link).
 * @author Phil Brown
 * @see <a href="http://stackoverflow.com/questions/937313/android-basic-gesture-detection">android-basic-gesture-detection</a>
 */
public class SwipeDetector implements View.OnTouchListener
{
    /**
     * The minimum distance a finger must travel in order to register a swipe event.
     */
    private int minSwipeDistance;

    /** Maintains a reference to the first detected down touch event. */
    private float downX, downY;

    /** Maintains a reference to the first detected up touch event. */
    private float upX, upY;

    /** provides access to size and dimension contants */
    private ViewConfiguration config;

    /**
     * provides callbacks to a listener class for various swipe gestures.
     */
    private SwipeListener listener;

    public SwipeDetector(SwipeListener listener)
    {
        this.listener = listener;
    }


    /**
     * {@inheritDoc}
     */
    public boolean onTouch(View v, MotionEvent event)
    {
        if (config == null)
        {
                config = ViewConfiguration.get(v.getContext());
                minSwipeDistance = config.getScaledTouchSlop();
        }

        switch(event.getAction())
        {
        case MotionEvent.ACTION_DOWN:
            downX = event.getX();
            downY = event.getY();
            return true;
        case MotionEvent.ACTION_UP:
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            // swipe horizontal?
            if(Math.abs(deltaX) > minSwipeDistance)
            {
                // left or right
                if (deltaX < 0)
                {
                        if (listener != null)
                        {
                                listener.onRightSwipe(v);
                                return true;
                        }
                }
                if (deltaX > 0)
                {
                        if (listener != null)
                        {
                                listener.onLeftSwipe(v);
                                return true;
                        }
                }
            }

            // swipe vertical?
            if(Math.abs(deltaY) > minSwipeDistance)
            {
                // top or down
                if (deltaY < 0)
                {
                        if (listener != null)
                        {
                                listener.onDownSwipe(v);
                                return true;
                        }
                }
                if (deltaY > 0)
                {
                        if (listener != null)
                        {
                                listener.onUpSwipe(v);
                                return true;
                        }
                }
            }
        }
        return false;
    }

    /**
     * Provides callbacks to a registered listener for swipe events in {@link SwipeDetector}
     * @author Phil Brown
     */
    public interface SwipeListener
    {
        /** Callback for registering a new swipe motion from the bottom of the view toward its top. */
        public void onUpSwipe(View v);
        /** Callback for registering a new swipe motion from the left of the view toward its right. */
        public void onRightSwipe(View v);
        /** Callback for registering a new swipe motion from the right of the view toward its left. */
        public void onLeftSwipe(View v);
        /** Callback for registering a new swipe motion from the top of the view toward its bottom. */
        public void onDownSwipe(View v);
    }
}

ปัดมุมมอง Interceptor

package self.philbrown;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

import com.npeinc.module_NPECore.model.SwipeDetector;
import com.npeinc.module_NPECore.model.SwipeDetector.SwipeListener;

/**
 * View subclass used for handling all touches (swipes and others)
 * @author Phil Brown
 */
public class SwipeInterceptorView extends RelativeLayout
{
    private SwipeDetector swiper = null;

    public void setSwipeListener(SwipeListener listener)
    {
        if (swiper == null)
            swiper = new SwipeDetector(listener);
    }

    public SwipeInterceptorView(Context context) {
        super(context);
    }

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

    public SwipeInterceptorView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e)
    {
        boolean swipe = false, touch = false;
        if (swiper != null)
            swipe = swiper.onTouch(this, e);
        touch = super.onTouchEvent(e);
        return swipe || touch;
    }
}

1
ฉันลองใช้สิ่งนี้ในมุมมองที่มีองค์ประกอบที่คลิกได้ เมื่อการปัดเริ่มต้นจากองค์ประกอบที่คลิกได้ (เช่นมุมมองรายการที่มีการลงทะเบียนฟัง itItClClick) onTouchEvent จะไม่ถูกเรียกใช้ ดังนั้นผู้ใช้จึงไม่สามารถเริ่มต้นการกวาดนิ้วผ่านองค์ประกอบที่คลิกได้ซึ่งโชคร้ายสำหรับฉันและฉันยังคงพยายามหาวิธีแก้ไขปัญหานี้เนื่องจากองค์ประกอบที่คลิกได้ของเราใช้พื้นที่ในการดูค่อนข้างน้อยและเรายังต้องการการสนับสนุนการกวาดนิ้ว สำหรับมุมมองทั้งหมด หากการปัดนิ้วไม่ได้เริ่มต้นจากองค์ประกอบที่คลิกได้แสดงว่ามันทำงานได้อย่างสมบูรณ์
Lo-Tan

@ Lo-Tan นี้เกิดขึ้นเนื่องจากรายการที่คลิกได้เป็นมุมมองของเด็กและดังนั้นจึงเป็นที่ด้านบนของSwipeInterceptorViewดังนั้นการคลิกของมันจะถูกจัดการแรก คุณสามารถแก้ไขปัญหานี้ได้โดยใช้กลไกการคลิกของคุณเองโดยการนำไปใช้onTouchListenerหรือเพื่อแก้ไขปัญหาคุณสามารถรับฟังการคลิกที่ยาวนานแทนการคลิก (ดูView.setOnLongClickListener)
Phil

จริง ๆ แล้วฉันกำลังพยายามที่ในขณะนี้ หรือเป็นไปได้ที่จะยกเลิกกิจกรรมคลิกถ้าพวกเขาเริ่มต้นการลาก :) ขอบคุณมาก
Lo-Tan

ทางออกหนึ่งคือแนบตัวตรวจจับการปัดเข้ากับทุกมุมมองในแอปของคุณ อีกประการหนึ่งคือการใช้onInterceptTouchEventใน SwipeInterceptorView ของคุณ
Edward Falk

7

ฉันรู้ว่ามันสายเกินไปที่จะตอบ แต่ฉันยังคงโพสต์Swipe Detection สำหรับ ListViewซึ่งจะใช้Swipe Touch Listener ใน ListView Itemได้อย่างไร

Refrence: Exterminator13 (คำตอบหนึ่งในหน้านี้)

ทำหนึ่งActivitySwipeDetector.class

package com.example.wocketapp;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public class ActivitySwipeDetector implements View.OnTouchListener 
{
    static final String logTag = "SwipeDetector";
    private SwipeInterface activity;
    private float downX, downY;
    private long timeDown;
    private final float MIN_DISTANCE;
    private final int VELOCITY;
    private final float MAX_OFF_PATH;

    public ActivitySwipeDetector(Context context, SwipeInterface activity)
    {
        this.activity = activity;
        final ViewConfiguration vc = ViewConfiguration.get(context);
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density;
        VELOCITY = vc.getScaledMinimumFlingVelocity();
        MAX_OFF_PATH = MIN_DISTANCE * 2;
    }

    public void onRightToLeftSwipe(View v) 
    {
        Log.i(logTag, "RightToLeftSwipe!");
        activity.onRightToLeft(v);
    }

    public void onLeftToRightSwipe(View v) 
    {
        Log.i(logTag, "LeftToRightSwipe!");
        activity.onLeftToRight(v);
    }

    public boolean onTouch(View v, MotionEvent event) 
    {
        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
            {
                Log.d("onTouch", "ACTION_DOWN");
                timeDown = System.currentTimeMillis();
                downX = event.getX();
                downY = event.getY();
                v.getParent().requestDisallowInterceptTouchEvent(false);
                return true;
            }

        case MotionEvent.ACTION_MOVE:
            {
                float y_up = event.getY();
                float deltaY = y_up - downY;
                float absDeltaYMove = Math.abs(deltaY);

                if (absDeltaYMove > 60) 
                {
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                } 
                else
                {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }
            }

            break;

            case MotionEvent.ACTION_UP: 
            {
                Log.d("onTouch", "ACTION_UP");
                long timeUp = System.currentTimeMillis();
                float upX = event.getX();
                float upY = event.getY();

                float deltaX = downX - upX;
                float absDeltaX = Math.abs(deltaX);
                float deltaY = downY - upY;
                float absDeltaY = Math.abs(deltaY);

                long time = timeUp - timeDown;

                if (absDeltaY > MAX_OFF_PATH) 
                {
                    Log.e(logTag, String.format(
                            "absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY,
                            MAX_OFF_PATH));
                    return v.performClick();
                }

                final long M_SEC = 1000;
                if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) 
                {
                     v.getParent().requestDisallowInterceptTouchEvent(true);
                    if (deltaX < 0) 
                    {
                        this.onLeftToRightSwipe(v);
                        return true;
                    }
                    if (deltaX > 0) 
                    {
                        this.onRightToLeftSwipe(v);
                        return true;
                    }
                }
                else 
                {
                    Log.i(logTag,
                            String.format(
                                    "absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b",
                                    absDeltaX, MIN_DISTANCE,
                                    (absDeltaX > MIN_DISTANCE)));
                    Log.i(logTag,
                            String.format(
                                    "absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b",
                                    absDeltaX, time, VELOCITY, time * VELOCITY
                                            / M_SEC, (absDeltaX > time * VELOCITY
                                            / M_SEC)));
                }

                 v.getParent().requestDisallowInterceptTouchEvent(false);

            }
        }
        return false;
    }
    public interface SwipeInterface 
    {

        public void onLeftToRight(View v);

        public void onRightToLeft(View v);
    }

}

เรียกมันจากคลาสกิจกรรมของคุณเช่นนี้:

yourLayout.setOnTouchListener(new ActivitySwipeDetector(this, your_activity.this));

และอย่าลืมใช้ SwipeInterfaceซึ่งจะให้สองวิธี @override:

    @Override
    public void onLeftToRight(View v) 
    {
        Log.e("TAG", "L to R");
    }

    @Override
    public void onRightToLeft(View v) 
    {
        Log.e("TAG", "R to L");
    }

ฉันพบว่ามีMAX_OFF_PATH = 5 * vc.getScaledPagingTouchSlop()ความสะดวกสบายมากขึ้นสำหรับการปัดนิ้วหัวแม่มือโดยธรรมชาติเดินทางในแนวโค้งเล็กน้อย
qix

4

ท่าทางคือการเคลื่อนไหวที่ละเอียดอ่อนเพื่อกระตุ้นการมีปฏิสัมพันธ์ระหว่างหน้าจอสัมผัสและผู้ใช้ มันใช้เวลาระหว่างการสัมผัสครั้งแรกบนหน้าจอจนถึงจุดที่นิ้วสุดท้ายออกจากพื้นผิว

Android ให้เรามีระดับที่เรียกว่าGestureDetectorใช้ซึ่งเราสามารถตรวจจับท่าทางที่เหมือนกันเช่นการเคาะขึ้นและลงรูดในแนวตั้งและแนวนอน (ขว้าง) ยาวและระยะสั้นกดก๊อกสอง ฯลฯ และแนบผู้ฟังไว้กับพวกเขา

ทำให้คลาสกิจกรรมของเราใช้ GestureDetector.OnDoubleTapListener (สำหรับการตรวจจับท่าทางด้วยการแตะสองครั้ง) และGestureDetector.OnGestureListener อินเตอร์เฟสและใช้วิธีการแบบนามธรรมทั้งหมดสำหรับข้อมูลเพิ่มเติม คุณสามารถเยี่ยมชมhttps://developer.android.com/training/gestures/detector.html มารยาท

สำหรับการทดสอบสาธิต GestureDetectorDemo


4

หากคุณไม่ต้องการสร้างคลาสแยกต่างหากหรือสร้างรหัสที่ซับซ้อน
คุณสามารถสร้างตัวแปร GestureDetector ภายใน OnTouchListener และทำให้รหัสของคุณง่ายขึ้น

namVyuVar สามารถเป็นชื่อใด ๆ ของมุมมองที่คุณต้องตั้งค่าผู้ฟัง

namVyuVar.setOnTouchListener(new View.OnTouchListener()
{
    @Override
    public boolean onTouch(View view, MotionEvent MsnEvtPsgVal)
    {
        flingActionVar.onTouchEvent(MsnEvtPsgVal);
        return true;
    }

    GestureDetector flingActionVar = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener()
    {
        private static final int flingActionMinDstVac = 120;
        private static final int flingActionMinSpdVac = 200;

        @Override
        public boolean onFling(MotionEvent fstMsnEvtPsgVal, MotionEvent lstMsnEvtPsgVal, float flingActionXcoSpdPsgVal, float flingActionYcoSpdPsgVal)
        {
            if(fstMsnEvtPsgVal.getX() - lstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Right to Left fling

                return false;
            }
            else if (lstMsnEvtPsgVal.getX() - fstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Left to Right fling

                return false;
            }

            if(fstMsnEvtPsgVal.getY() - lstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Bottom to Top fling

                return false;
            }
            else if (lstMsnEvtPsgVal.getY() - fstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac)
            {
                // TskTdo :=> On Top to Bottom fling

                return false;
            }
            return false;
        }
    });
});

3

ถึงทั้งหมด: อย่าลืมเกี่ยวกับcase MotionEACT.ACTION_CANCEL:

มันเรียกใช้ 30 swipes โดยไม่มี ACTION_UP

และเท่ากับ ACTION_UP ในกรณีนี้


2

ฉันเพิ่มคลาสทั่วไปมากขึ้นฉันเลือกคลาสของโทมัสและเพิ่มอินเทอร์เฟซที่ส่งกิจกรรมไปยังกิจกรรมหรือชิ้นส่วนของคุณ มันจะลงทะเบียนฟังบนนวกรรมิกเพื่อให้แน่ใจว่าคุณใช้อินเตอร์เฟซหรือ ClassCastException จะ thorwn อินเตอร์เฟสส่งคืนหนึ่งในสี่ int สุดท้ายที่กำหนดไว้ในคลาสและจะส่งคืนมุมมองที่เปิดใช้งาน

import android.app.Activity;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class SwipeDetector implements View.OnTouchListener{

    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;
    public final static int RIGHT_TO_LEFT=1;
    public final static int LEFT_TO_RIGHT=2;
    public final static int TOP_TO_BOTTOM=3;
    public final static int BOTTOM_TO_TOP=4;
    private View v;

    private onSwipeEvent swipeEventListener;


    public SwipeDetector(Activity activity,View v){
        try{
            swipeEventListener=(onSwipeEvent)activity;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",activity.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }
    public SwipeDetector(Fragment fragment,View v){
        try{
            swipeEventListener=(onSwipeEvent)fragment;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",fragment.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }


    public void onRightToLeftSwipe(){   
        swipeEventListener.SwipeEventDetected(v,RIGHT_TO_LEFT);
    }

    public void onLeftToRightSwipe(){   
        swipeEventListener.SwipeEventDetected(v,LEFT_TO_RIGHT);
    }

    public void onTopToBottomSwipe(){   
        swipeEventListener.SwipeEventDetected(v,TOP_TO_BOTTOM);
    }

    public void onBottomToTopSwipe(){
        swipeEventListener.SwipeEventDetected(v,BOTTOM_TO_TOP);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            //HORIZONTAL SCROLL
            if(Math.abs(deltaX) > Math.abs(deltaY))
            {
                if(Math.abs(deltaX) > MIN_DISTANCE){
                    // left or right
                    if(deltaX < 0) 
                    {
                        this.onLeftToRightSwipe();
                        return true;
                    }
                    if(deltaX > 0) {
                        this.onRightToLeftSwipe();
                        return true; 
                    }
                }
                else {
                    //not long enough swipe...
                    return false; 
                }
            }
            //VERTICAL SCROLL
            else 
            {
                if(Math.abs(deltaY) > MIN_DISTANCE){
                    // top or down
                    if(deltaY < 0) 
                    { this.onTopToBottomSwipe();
                    return true; 
                    }
                    if(deltaY > 0)
                    { this.onBottomToTopSwipe(); 
                    return true;
                    }
                }
                else {
                    //not long enough swipe...
                    return false;
                }
            }

            return true;
        }
        }
        return false;
    }
    public interface onSwipeEvent
    {
        public void SwipeEventDetected(View v , int SwipeType);
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.