Обнаружение жеста бега в макете сетки

avatar
gav
1 июня 2009 в 23:35
409449
18
1139

Я хочу, чтобы fling распознавание жестов работало в моем приложении для Android.

У меня есть GridLayout, который содержит 9 ImageView s. Источник можно найти здесь: Схема сетки Ромена Гайса.

Этот файл, который я беру, взят из приложения Romain Guy Photostream и был немного адаптирован.

Для простой ситуации с щелчком мне нужно всего лишь установить 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 (адаптировано из фотопотока).

Я искал эти источники:

У меня пока ничего не работает, и я надеялся на какие-нибудь указатели.

Источник
Bishwash
10 марта 2020 в 10:39
0

Как решить эту проблему? Пожалуйста, ответьте coderhelper.com/questions/60464912/…

Ответы (18)

avatar
gav
11 мая 2020 в 07:14
831

Спасибо 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);
}

Танец после «броска» не обязателен, но приветствуется.

Cdsboy
6 декабря 2009 в 22:35
112

Спасибо за этот код! Это было очень полезно. Однако, пытаясь заставить работать жесты, я столкнулся с одним очень неприятным моментом. В моем SimpleOnGestureListener я должен переопределить onDown для регистрации любого из моих жестов. Он может просто вернуть истину, но я должен быть определен. PS: Я не знаю, это моя версия API или мое оборудование, но я использую 1.5 на HTC Droid Eris.

lomza
12 мая 2011 в 13:12
0

Я пробовал ваш код, и не имеет значения, смахиваю я или щелкаю (с помощью мыши, потому что я работаю в эмуляторе), я всегда получаю тост, который я определил в методе onClick, поэтому эмулятор обнаруживает только щелчки без смахиваний. Почему это так?

Jonathan
26 июля 2011 в 14:23
0

Я попробовал этот код, но он не сработал. по-прежнему не мог прокручивать вообще, когда я применяю прослушиватель onClick к одному из дочерних представлений в моем представлении галереи

IgorGanapolsky
19 декабря 2011 в 17:01
0

Iomza: вы пробовали ставить операторы прерывания и выполнять пошаговый код?

IgorGanapolsky
19 декабря 2011 в 17:01
0

Престижность за использование внутреннего класса! Очень чистый подход.

user517491
14 февраля 2012 в 11:47
0

Что это за функция onClick, она выдает ошибки .. пожалуйста, помогите

Mikey
5 июля 2012 в 06:23
0

как получить значение этого SWIPE_MAX_OFF_PATH?

superuser
22 августа 2013 в 23:55
0

Если я хочу иметь фрагмент поверх моей активности, я бы сделал fragment.setOnClickListener (SelectFilterActivity.this); fragment.setOnTouchListener (gestureListener);

android developer
3 октября 2013 в 12:00
0

Предположим, мне тоже нужна функция обработки касаний (например, для перетаскивания), что мне делать?

Jibran Khan
12 января 2014 в 16:55
0

Получил, что он работает для DoubleTap, следуя реализации SimpleGestureListener.

linuxjava
20 марта 2014 в 12:15
0

Мой setOnClickListener не работал. Поэтому мне пришлось переопределить onSingleTapConfirmed, и он работал, как ожидалось.

Pararth
8 августа 2014 в 05:38
0

это классический пример с базовыми опциями свайпа, спасибо за элементарное! доктор ватсон!

Ralphilius
24 апреля 2015 в 13:28
0

Используя этот код, но он не работает с моим дочерним фрагментом. Было бы здорово, если бы вы могли взглянуть на него coderhelper.com/questions/29848754/…

Erum
28 июля 2015 в 06:05
0

@Sarvesh, может кто-нибудь, пожалуйста, скажет мне, как включить смахивание влево или вправо в кнопке, когда кнопка находится в позиции окна просмотра 0, и я не могу одновременно смахивать по кнопке и смахивать по странице?

ERJAN
22 сентября 2015 в 12:33
0

Тем не менее, никто не объяснил, что делает этот onclick () и что внутри него

JoxTraex
11 марта 2020 в 02:37
0

Вам, вероятно, следует указать на Code Shogun, поскольку я не вижу ссылки на кредитную ссылку в вашем сообщении. Откуда это взялось?

avatar
Sujay U N
30 мая 2017 в 18:51
5

Если вы не хотите создавать отдельный класс или усложнять код,
Вы можете просто создать переменную 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;
        }
    });
});
avatar
IntelliJ Amiya
23 июля 2015 в 05:50
4

Жесты - это тонкие движения, запускающие взаимодействие между сенсорным экраном и пользователем. Он длится время от первого прикосновения к экрану до момента, когда последний палец оторвется от поверхности.

Android предоставляет нам класс под названием GestureDetector, с помощью которого мы можем обнаруживать распространенные жесты, такие как касание вниз и вверх, пролистывание по вертикали и горизонтали (бросок), длинное и короткое нажатие, двойное касание и т. Д. . и прикрепить к ним слушателей.

Сделайте так, чтобы наш класс Activity реализовал GestureDetector.OnDoubleTapListener (для обнаружения жестов двойного касания) и GestureDetector.OnGestureListener интерфейсы <.For дополнительные абстрактные методы info. вы можете посетить https://developer.android.com/training/gestures/detector.html. Предоставлено

Для демонстрационного теста. GestureDetectorDemo

avatar
Hai Zhang
7 декабря 2014 в 07:49
14

Это комбинированный ответ из двух ответов вверху, если кому-то нужна рабочая реализация.

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://coderhelper.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() {}
}
Elia12345
13 августа 2015 в 09:45
0

Спасибо за действительно хорошую реализацию. Дополнительно я бы посоветовал проверить absDeltaY > minSwipeDelta, absVelocityY > minSwipeVelocity, absVelocityY < maxSwipeVelocity только в том случае, если minSwipeDelta ! = <569865110651109367> <569865110659361>, <569103659369> , maxSwipeVelocity ! = getScaledMaximumFlingVelocity, то есть проверять, только ли эти так называемые «значения по умолчанию» (я имею в виду getScaledTouchSlop, getScaledMinimumFlingVelocity, getScaledMaximumFlingVelocity или значения scaledMaximumFling изменены в соответствии с вашим ownlocity.

Elia12345
13 августа 2015 в 09:55
0

Дело в том, что согласно исходному коду упомянутые значения "по умолчанию" уже проверены GestureDetector, а OnFling срабатывает только в случае их подтверждения (кстати, срабатывание происходит только в случае ACTION_UP, а не ACTION_MOVE или ACTION_POINTER_UP, т.е. только в результате полностью реализованного жеста). (Я не проверял другие версии API, поэтому комментарии приветствуются).

avatar
Sagar Shah
6 августа 2014 в 09:27
8

Я знаю, что слишком поздно отвечать, но все же я публикую Обнаружение смахивания для ListView , что Как использовать Слушатель касаний смахивания в элементе ListView .

Ссылка: 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");
    }
qix
22 октября 2014 в 01:28
0

Я считаю, что MAX_OFF_PATH = 5 * vc.getScaledPagingTouchSlop() более удобен для движения большого пальца, естественно перемещающегося по небольшой дуге.

avatar
Gal Rom
31 июля 2014 в 05:26
2

Введя более общий класс, я взял класс Томаса и добавил интерфейс, который отправляет события в вашу активность или фрагмент. он зарегистрирует слушателя в конструкторе, поэтому убедитесь, что вы реализуете интерфейс, иначе будет исключено исключение ClassCastException. интерфейс возвращает один из четырех последних определенных 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);
    }

}
avatar
djdance
28 декабря 2013 в 12:13
3

Всем: не забывайте о случае MotionEvent.ACTION_CANCEL:

он вызывает 30% свайпов без ACTION_UP

и в данном случае равно ACTION_UP

avatar
Viswanath Lekshmanan
5 апреля 2013 в 14:28
21

Имеется встроенный интерфейс, который можно использовать напрямую для всех жестов:
Вот объяснение для пользователя базового уровня: enter image description here Есть 2 импорта, будьте осторожны при выборе, они разные. enter image description hereenter image description here

Stan
25 ноября 2013 в 08:54
1

И что делать дальше? Как настроить этого слушателя на определенное представление? А что, если это представление является частью фрагмента?

avatar
Phil
24 января 2013 в 17:49
12

Вы можете использовать библиотеку 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://coderhelper.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);
    }
}

Просмотр перехватчика смахивания

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;
    }
}
Lo-Tan
6 марта 2013 в 16:38
1

Я попытался реализовать это в представлении, содержащем интерактивные элементы. Когда смахивание начинается с интерактивного элемента (например, представления списка, в котором зарегистрирован прослушиватель onItemClick), onTouchEvent никогда не вызывается. Таким образом, пользователь не может начать смахивание по интерактивному элементу, что для меня неудачно, и я все еще пытаюсь понять, как обойти это, поскольку наши интерактивные элементы занимают довольно много места для просмотра, и нам все еще нужна поддержка смахивания. для всего вида. Если свайп не начинается над интерактивным элементом, он работает отлично.

Phil
6 марта 2013 в 17:00
0

@ Lo-Tan, это происходит потому, что ваш интерактивный элемент является дочерним представлением и, таким образом, находится сверху из SwipeInterceptorView, поэтому его щелчок обрабатывается в первую очередь. Вы можете исправить это, реализовав свой собственный механизм щелчков, реализовав onTouchListener, или в качестве обходного пути вы можете прослушивать длинные щелчки вместо щелчков (см. View.setOnLongClickListener).

Lo-Tan
6 марта 2013 в 17:03
0

Я на самом деле пытаюсь это сделать прямо сейчас. Или возможно отменить событие щелчка, если они начнут перетаскивать :) Большое спасибо.

Edward Falk
21 июля 2015 в 02:52
0

Одно из решений - прикрепить детектор смахивания ко всем представлениям в вашем приложении. Другой - реализовать onInterceptTouchEvent в вашем SwipeInterceptorView.

avatar
Exterminator13
18 июля 2012 в 01:00
37

Моя версия решения, предложенная Томасом Фанкхаузером и Мареком Себерой (не поддерживает вертикальные смахивания):

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;
    }

}
abdfahim
21 ноября 2013 в 05:37
0

может кто-нибудь, пожалуйста, скажите мне, как вызвать класс. ActivitySwipeDetector swipe = новый ActivitySwipeDetector (это); очевидно, выдает ошибку, поскольку такого конструктора нет. Следует ли указать ActivitySwipeDetector swipe = new ActivitySwipeDetector (this, null);

Anton Kashpor
7 апреля 2014 в 11:46
0

@AbdullahFahim ActivitySwipeDetector (это, YourActivity.this);

avatar
georgiecasey
23 апреля 2012 в 19:54
28

Этот вопрос довольно старый, и в июле 2011 года Google выпустил пакет совместимости, версия 3), который включает ViewPager, который работает с Android 1.6 и выше. Ответы на этот вопрос GestureListener выглядят не очень элегантно на Android. Если вы ищете код, используемый для переключения между фотографиями в Галерее Android или переключения представлений в новом приложении Play Market, то это определенно ViewPager.

Вот несколько ссылок для получения дополнительной информации:

almalkawi
31 июля 2012 в 16:05
0

Одна из проблем ViewPager заключается в том, что вы не можете контролировать параметры расстояния и скорости для жеста метания.

Anthony
23 августа 2015 в 13:27
0

ViewPager не используется в галерее.

avatar
MappaM
9 февраля 2012 в 21:33
19

В Интернете (и на этой странице) есть предложение использовать ViewConfiguration. getScaledTouchSlop () , чтобы получить масштабированное по устройству значение для SWIPE_MIN_DISTANCE.

getScaledTouchSlop() предназначен для расстояния " прокрутки порога", а не смахивания. Пороговое расстояние прокрутки должно быть меньше порогового расстояния «качания между страницами». Например, эта функция возвращает 12 пикселей на моем Samsung GS2, а примеры, приведенные на этой странице, имеют размер около 100 пикселей.

С уровнем API 8 (Android 2.2, Froyo) у вас есть getScaledPagingTouchSlop() , предназначенный для перелистывания страниц. На моем устройстве он возвращает 24 (пикселей). Так что, если вы находитесь на уровне API <8, я думаю, что «2 * getScaledTouchSlop()» должно быть «стандартным» порогом смахивания. Но пользователи моего приложения с маленькими экранами сказали мне, что их слишком мало ... Как и в моем приложении, вы можете прокручивать по вертикали и менять страницу по горизонтали. При предложенном значении они иногда меняют страницу вместо прокрутки.

avatar
Marek Sebera
10 января 2012 в 16:11
96

Я немного изменил и отремонтировал решение от 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 , и вы можете узнать, в каком View было вызвано Swipe Event . <870166387024>.

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

Marek Sebera
5 февраля 2012 в 19:31
0

Я немного изменил его снова, см. v.performClick();, который используется, чтобы не использовать событие для OnClickListener, если установлено в том же представлении

Chocolava
5 января 2013 в 14:41
0

Привет, я новичок, поэтому этот вопрос может быть очевидным или тривиальным, но, пожалуйста, ответьте. Та часть, где вы написали, используется как: ActivitySwipeDetector swipe = new ActivitySwipeDetector (this); Этот оператор будет частью MainActivity, верно? Тогда «это» будет действием MainActivity. В то время как конструктор принимает экземпляр SwipeInterface. Пожалуйста, помогите мне здесь. Большое спасибо.

Marek Sebera
5 января 2013 в 16:50
0

@Chocolava создайте новый вопрос, комментарий - не лучшее место, чтобы задавать подобные вопросы.

Duc Tran
6 января 2013 в 22:31
0

@MarekSebera, это не работает с ScrollView и ListView? как с ними справиться?

Marek Sebera
6 января 2013 в 22:34
0

@silentbang, опять же, здесь не место задавать такие вопросы. создайте новую ветку вопросов.

avatar
Garret Wilson
2 ноября 2011 в 23:14
13

Здесь много полезной информации. К сожалению, большая часть этого кода обработки переброса разбросана по разным сайтам в различных состояниях завершения, хотя можно было бы подумать, что это важно для многих приложений.

Я потратил время на создание прослушивателя меток, который проверяет выполнение соответствующих условий. Я добавил прослушиватель перелистывания страниц, который добавляет дополнительные проверки, чтобы убедиться, что переходы соответствуют пороговому значению для перелистывания страниц. Оба этих слушателя позволяют легко ограничивать броски по горизонтальной или вертикальной оси. Вы можете увидеть, как он используется в представлении для скользящих изображений. Я подтверждаю, что люди здесь провели большую часть исследований - я только что собрал их в полезную библиотеку.

Эти последние несколько дней представляют собой мой первый опыт программирования на Android; ожидайте и гораздо больше.

Gaurav Arora
10 декабря 2012 в 07:41
0

Я хочу реализовать жест смахивания двумя пальцами. Пожалуйста, помогите мне!

avatar
Thomas Fankhauser
21 апреля 2011 в 10:24
149

Я делаю это немного иначе и написал дополнительный класс детектора, который реализует View.onTouchListener

onCreate просто добавьте его в самый нижний макет следующим образом:

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

, где id.lowestLayout - это id.xxx для представления самого низкого уровня в иерархии макета, а lowerLayout объявлен как 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;
}

}

Мне очень нравится!

nemesisfixx
20 сентября 2011 в 22:14
1

На самом деле это значительно упростило мне применение функций жестов и потребовало "меньшего количества проводов": D Спасибо, @Thomas

Someone Somewhere
28 октября 2011 в 17:28
5

Это похоже на изящный служебный класс, но я думаю, что ваши четыре метода on ... swipe () должны быть интерфейсами.

Marek Sebera
5 декабря 2011 в 00:20
2

этих возвратов там быть не должно (строка «мы не потребляем событие»), не так ли? Отключает функцию вертикальной прокрутки.

Piotr
28 декабря 2011 в 12:15
0

Разве не сохраняется ссылка на активность, вызывающую утечку памяти?

user1118741
2 января 2012 в 11:07
0

Да, действительно хорошо работает ... хороший код для реализации события ontouch в android

Jon O
14 апреля 2012 в 14:25
0

@JeffreyBlattman Не могли бы вы доработать и / или отредактировать сообщение, чтобы исправить их?

Jeffrey Blattman
14 апреля 2012 в 16:02
5

в частности, метод onTouch (). во-первых, если дельта X недостаточно велика, она возвращается без проверки дельты Y. В результате никогда не обнаруживается смахивание влево-вправо. во-вторых, он также не должен возвращать истину, если он падает, не обнаружив свайпа. в-третьих, он не должен возвращать истину при отключении. это предотвращает работу любого другого слушателя, такого как onClick.

Jeffrey Blattman
14 апреля 2012 в 16:03
2

@Piotr, это не проблема, если объект, содержащий ссылку, имеет ту же область видимости, что и само действие. проблема возникает, когда вы сохраняете ссылку на действие в месте, которое имеет большую область действия, чем действие ... например, из статического члена.

bad_keypoints
7 января 2013 в 11:24
0

Я пытаюсь использовать его в приложении камеры для жестов, связанных с операциями камеры. Я не получаю никаких зарегистрированных событий. Мой внешний макет - RelativeLayout, в XML которого я помещаю его ID = relative_layout. Теперь я поместил три строки в onCreate (): paste.org/59785

bad_keypoints
7 января 2013 в 12:17
0

на самом деле это моя полная настройка проекта. ge.tt/9P9t7sU/v/0 можешь запустить и посмотреть, работает ли он у тебя?

Alessandro Roaro
14 января 2014 в 20:15
0

Этот фрагмент кода очень полезен, но передача действия в качестве параметра с последующим вызовом его методов - не лучший вариант. Лучшим выбором было бы установить обратные вызовы смахивания как абстрактные методы и позволить действию реализовать их.

Edward Falk
21 июля 2015 в 02:38
0

Только одно примечание: если у действия есть виджеты, которые будут принимать ввод самостоятельно, жесты смахивания, которые запускаются на таких виджетах, не будут работать. См. coderhelper.com/questions/31504065/…

SkorpEN
7 мая 2020 в 13:15
0

В моем случае MotionEvent.ACTION_CANCEL так же, как MotionEvent.ACTION_UP, заставляет его работать должным образом.

avatar
Xion
20 апреля 2011 в 17:26
213

В одном из ответов выше упоминается обработка разной плотности пикселей, но предлагается вычислить параметры смахивания вручную. Стоит отметить, что вы действительно можете получить масштабированные разумные значения из системы, используя класс 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)

Я заметил, что использование этих значений приводит к более согласованному "ощущению" перехода между приложением и остальной системой.

Thomas Ahle
13 июля 2011 в 12:05
12

Я использую swipeMinDistance = vc.getScaledPagingTouchSlop() и swipeMaxOffPath = vc.getScaledTouchSlop().

WonderCsabo
16 ноября 2013 в 17:15
9

getScaledTouchSlop дает мне очень небольшой результат смещения, неудобно. Например, всего 24 пикселя на экране высотой 540 пикселей, что очень сложно удержать его в пределах досягаемости пальцем. : S

avatar
Noah
12 апреля 2011 в 21:14
14

Также в качестве незначительного улучшения.

Основная причина блока try / catch заключается в том, что e1 может быть нулевым для начального перемещения. в дополнение к try / catch включите проверку на null и return. аналогично следующему

if (e1 == null || e2 == null) return false;
try {
...
} catch (Exception e) {}
return false;
avatar
paiego
18 февраля 2011 в 09:45
67

Код детектора жестов смахивания выше очень полезен! Однако вы можете сделать это решение независимым от плотности раствора, используя следующие относительные значения (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);
Thane Anthem
4 апреля 2011 в 11:06
8

+1 за то, что поднял этот вопрос. Обратите внимание, что DensityMetrics.de densityDpi был введен в API 4. Для обратной совместимости с API 1 вместо этого используйте DensityMetrics.de density. Затем это изменяет расчет так, чтобы он был просто SWIPE_MIN_DISTANCE * dm.de density.

IgorGanapolsky
19 декабря 2011 в 15:44
0

Откуда у вас число 160.0f?

paiego
7 января 2012 в 01:54
0

developer.android.com/guide/practices/screens_support.html Пиксель, не зависящий от плотности (dp) Преобразование единиц dp в пиксели экрана выполняется просто: px = dp * (dpi / 160)

Sandy
4 февраля 2012 в 02:51
0

Я искал это повсюду. НИ ОДИН пример onFling () в Интернете не имеет этого, что приведет к плохому UX. Спасибо!

paiego
29 октября 2012 в 18:39
0

160.0f исходит из 160 DPI, который является стандартной плотностью, на которой основан DP (пиксели, не зависящие от плотности). public static final int DENSITY_MEDIUM Добавлен в API уровня 4 Стандартный квантованный DPI для экранов средней плотности. Постоянное значение: 160 (0x000000a0)

Tushar
15 января 2013 в 01:59
0

@ThaneAnthem Я думаю, вы имели в виду DisplayMetrics, а не DensityMetrics. :П

Edward Falk
21 июля 2015 в 02:41
0

Также см. ViewConfiguration, который предоставляет ряд стандартных значений с поправкой на плотность пикселей.