ListView中的手势(Gesture)

13

你好,我需要在我的ListView中添加手势,并实现联系人应用程序相同的功能。当我向左滑动时,它应该发送一条消息;右滑则应该拨打电话。有人能帮助我如何检测这些手势吗?我已经在其他视图中实现了它,但我无法在ListView中实现。我不知道出了什么问题...我的代码如下:

/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);
    // Create an array of Strings, that will be put to our ListActivity
    String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone"};
    // Create an ArrayAdapter, that will actually make the Strings above
    // appear in the ListView
    this.setListAdapter(new ArrayAdapter<String>(this, R.id.MyList, names));
    gestureListener = new ListView.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            if (gestureDetector.onTouchEvent(event)) {
                return true;
            }
            return false;
        }
    };      
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    // Get the item that was clicked
    Object o = this.getListAdapter().getItem(position);
    String keyword = o.toString();
    Toast.makeText(this, "You selected: " + keyword, Toast.LENGTH_LONG).show();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (gestureDetector.onTouchEvent(event))
        return true;
    else
        return false;
}

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) {
                Context ctx = getApplicationContext();
                CharSequence txt = "Right to Left Swipe";
                int duration = Toast.LENGTH_LONG;
                Toast toast = Toast.makeText(ctx, txt, duration);
                toast.show();
                Toast.makeText(this.getItem(lv.pointToPosition((int)e1.getX(),(int) e1.getY())));
                // return super.onFling();                                          
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                Context ctx = getApplicationContext();
                CharSequence txt = "Left to Right Swipe";
                int duration = Toast.LENGTH_LONG;
                Toast toast = Toast.makeText(ctx, txt, duration);
                toast.show();
            }
        } catch (Exception e) {
            // nothing
        }
        return false;
    }
}

可能是如何在Android ListView中实现fling的重复问题。 - user142162
2个回答

46
根据 [#937313][1] 帖子的答案(感谢 gav 和 paiego),我编写了以下代码来识别 ListView 上的简单手势(水平滑动)。
然而,在 fling 操作之后,ListView 的 onItemClick() 监听器也将被调用!因此,您最终会得到一个 fling 和额外的 onItemClick()。我认为这是因为 Android 在每个按钮释放时都会发送一个项目点击事件,无论用户移动手指的距离有多远。为了解决这个问题,我提供了自己的方法 myOnItemClick(),而不是注册一个通常的 OnItemClickListener()。然后我重写了 SimpleOnGestureListener.onSingleTapUp() 方法,以便当手指抬起时,该方法将手动调用 myOnItemClick()。
到目前为止,这种方法对我来说运行良好。没有任何抱怨 :-)
[1]: https://dev59.com/nXNA5IYBdhLWcg3wgeAc
public class PracticeActivity extends ListActivity {

    private int REL_SWIPE_MIN_DISTANCE; 
    private int REL_SWIPE_MAX_OFF_PATH;
    private int REL_SWIPE_THRESHOLD_VELOCITY;

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

        // As paiego pointed out, it's better to use density-aware measurements. 
        DisplayMetrics dm = getResources().getDisplayMetrics();
        REL_SWIPE_MIN_DISTANCE = (int)(120.0f * dm.densityDpi / 160.0f + 0.5); 
        REL_SWIPE_MAX_OFF_PATH = (int)(250.0f * dm.densityDpi / 160.0f + 0.5);
        REL_SWIPE_THRESHOLD_VELOCITY = (int)(200.0f * dm.densityDpi / 160.0f + 0.5);

        ListView lv = getListView();
        lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, 
            m_Starbucks));

        final GestureDetector gestureDetector = new GestureDetector(new MyGestureDetector());
        View.OnTouchListener gestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                return gestureDetector.onTouchEvent(event); 
            }};
        lv.setOnTouchListener(gestureListener);

        // Long-click still works in the usual way.
        lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                String str = MessageFormat.format("Item long clicked = {0,number}", position);
                Toast.makeText(PracticeActivity.this, str, Toast.LENGTH_SHORT).show();
                return true;
            }
        });
    }

    // Do not use LitView.setOnItemClickListener(). Instead, I override 
    // SimpleOnGestureListener.onSingleTapUp() method, and it will call to this method when
    // it detects a tap-up event.
    private void myOnItemClick(int position) {
        String str = MessageFormat.format("Item clicked = {0,number}", position);
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
    }

    private void onLTRFling() {
        Toast.makeText(this, "Left-to-right fling", Toast.LENGTH_SHORT).show();
    }

    private void onRTLFling() {
        Toast.makeText(this, "Right-to-left fling", Toast.LENGTH_SHORT).show();
    }

    class MyGestureDetector extends SimpleOnGestureListener{ 

        // Detect a single-click and call my own handler.
        @Override 
        public boolean onSingleTapUp(MotionEvent e) {
            ListView lv = getListView();
            int pos = lv.pointToPosition((int)e.getX(), (int)e.getY());
            myOnItemClick(pos);
            return false;
        }

        @Override 
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
            if (Math.abs(e1.getY() - e2.getY()) > REL_SWIPE_MAX_OFF_PATH) 
                return false; 
            if(e1.getX() - e2.getX() > REL_SWIPE_MIN_DISTANCE && 
                Math.abs(velocityX) > REL_SWIPE_THRESHOLD_VELOCITY) { 
                onRTLFling(); 
            }  else if (e2.getX() - e1.getX() > REL_SWIPE_MIN_DISTANCE && 
                Math.abs(velocityX) > REL_SWIPE_THRESHOLD_VELOCITY) { 
                onLTRFling(); 
            } 
            return false; 
        } 

    } 

    private static final String[] m_Starbucks = {
        "Latte", "Cappuccino", "Caramel Macchiato", "Americano", "Mocha", "White Mocha", 
        "Mocha Valencia", "Cinnamon Spice Mocha", "Toffee Nut Latte", "Espresso",
        "Espresso Macchiato", "Espresso Con Panna"
    };
}

@wwyt,运行得很顺利!谢谢伙计。但是,有没有不用扩展ListActivity的方法?因为我还需要扩展其他类。 - Saurabh Bayani
你如何获取在ListView上滑动的项目的位置? - Franck
谢谢,这是我见过的独特实现! - macloving
当我们消耗了事件时,在onFling中为什么要返回false? - Karioki

3

这个语句是不正确的。请阅读您链接的文档。 - carlrice
1
第一个链接不再有效,也许你是指这个 https://github.com/liuzc/android-swipelistview - Mitro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接