Android: как обрабатывать жесты справа налево
Я хочу, чтобы мое приложение распознавало, когда пользователь проводит пальцем справа налево на экране телефона.
Как это сделать?
25 ответов
OnSwipeTouchListener.java:
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
result = true;
}
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
result = true;
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeTop() {
}
public void onSwipeBottom() {
}
}
Использование:
imageView.setOnTouchListener(new OnSwipeTouchListener(MyActivity.this) {
public void onSwipeTop() {
Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show();
}
public void onSwipeRight() {
Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show();
}
public void onSwipeLeft() {
Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
}
public void onSwipeBottom() {
Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show();
}
});
Этот код обнаруживает пролистывание влево и вправо, избегает устаревших вызовов API и имеет другие улучшения по сравнению с предыдущими ответами.
/**
* Detects left and right swipes across a view.
*/
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (distanceX > 0)
onSwipeRight();
else
onSwipeLeft();
return true;
}
return false;
}
}
}
Используйте это так:
view.setOnTouchListener(new OnSwipeTouchListener(context) {
@Override
public void onSwipeLeft() {
// Whatever
}
});
Если вам также нужно обрабатывать события клика, здесь внесены некоторые изменения:
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector = new GestureDetector(new GestureListener());
public boolean onTouch(final View v, final MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
result = onSwipeRight();
} else {
result = onSwipeLeft();
}
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
result = onSwipeBottom();
} else {
result = onSwipeTop();
}
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public boolean onSwipeRight() {
return false;
}
public boolean onSwipeLeft() {
return false;
}
public boolean onSwipeTop() {
return false;
}
public boolean onSwipeBottom() {
return false;
}
}
И пример использования:
background.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
toggleSomething();
}
});
background.setOnTouchListener(new OnSwipeTouchListener() {
public boolean onSwipeTop() {
Toast.makeText(MainActivity.this, "top", Toast.LENGTH_SHORT).show();
return true;
}
public boolean onSwipeRight() {
Toast.makeText(MainActivity.this, "right", Toast.LENGTH_SHORT).show();
return true;
}
public boolean onSwipeLeft() {
Toast.makeText(MainActivity.this, "left", Toast.LENGTH_SHORT).show();
return true;
}
public boolean onSwipeBottom() {
Toast.makeText(MainActivity.this, "bottom", Toast.LENGTH_SHORT).show();
return true;
}
});
Расширение на ответ Мирека, для случая, когда вы хотите использовать жесты смахивания в представлении прокрутки. По умолчанию сенсорный приемник для просмотра с прокруткой отключен, поэтому действие прокрутки не выполняется. Чтобы это исправить, вам нужно переопределить dispatchTouchEvent
метод Activity
и верните унаследованную версию этого метода после того, как вы закончите со своим собственным слушателем.
Чтобы сделать несколько изменений в коде Мирека: я добавляю геттер для gestureDetector
в OnSwipeTouchListener
,
public GestureDetector getGestureDetector(){
return gestureDetector;
}
Объявить OnSwipeTouchListener
внутри Activity как общеклассовое поле.
OnSwipeTouchListener onSwipeTouchListener;
Измените код использования соответственно:
onSwipeTouchListener = new OnSwipeTouchListener(MyActivity.this) {
public void onSwipeTop() {
Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show();
}
public void onSwipeRight() {
Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show();
}
public void onSwipeLeft() {
Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
}
public void onSwipeBottom() {
Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show();
}
});
imageView.setOnTouchListener(onSwipeTouchListener);
И переопределить dispatchTouchEvent
метод внутри Activity
:
@Override
public boolean dispatchTouchEvent(MotionEvent ev){
swipeListener.getGestureDetector().onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
Теперь действия прокрутки и прокрутки должны работать.
Чтобы иметь Click Listener
, DoubleClick Listener
, OnLongPress Listener
, Swipe Left
, Swipe Right
, Swipe Up
, Swipe Down
на одноместном View
вам нужно setOnTouchListener
, т.е.
view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {
@Override
public void onClick() {
super.onClick();
// your on click here
}
@Override
public void onDoubleClick() {
super.onDoubleClick();
// your on onDoubleClick here
}
@Override
public void onLongClick() {
super.onLongClick();
// your on onLongClick here
}
@Override
public void onSwipeUp() {
super.onSwipeUp();
// your swipe up here
}
@Override
public void onSwipeDown() {
super.onSwipeDown();
// your swipe down here.
}
@Override
public void onSwipeLeft() {
super.onSwipeLeft();
// your swipe left here.
}
@Override
public void onSwipeRight() {
super.onSwipeRight();
// your swipe right here.
}
});
}
Для этого вам нужно OnSwipeTouchListener
класс, который реализует OnTouchListener
,
public class OnSwipeTouchListener implements View.OnTouchListener {
private GestureDetector gestureDetector;
public OnSwipeTouchListener(Context c) {
gestureDetector = new GestureDetector(c, new GestureListener());
}
public boolean onTouch(final View view, final MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
onClick();
return super.onSingleTapUp(e);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
onDoubleClick();
return super.onDoubleTap(e);
}
@Override
public void onLongPress(MotionEvent e) {
onLongClick();
super.onLongPress(e);
}
// Determines the fling velocity and then fires the appropriate swipe event accordingly
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeDown();
} else {
onSwipeUp();
}
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeUp() {
}
public void onSwipeDown() {
}
public void onClick() {
}
public void onDoubleClick() {
}
public void onLongClick() {
}
}
Котлинская версия @Mirek Rusin находится здесь:
OnSwipeTouchListener.kt:
open class OnSwipeTouchListener(ctx: Context) : OnTouchListener {
private val gestureDetector: GestureDetector
companion object {
private val SWIPE_THRESHOLD = 100
private val SWIPE_VELOCITY_THRESHOLD = 100
}
init {
gestureDetector = GestureDetector(ctx, GestureListener())
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
private inner class GestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
var result = false
try {
val diffY = e2.y - e1.y
val diffX = e2.x - e1.x
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight()
} else {
onSwipeLeft()
}
result = true
}
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom()
} else {
onSwipeTop()
}
result = true
}
} catch (exception: Exception) {
exception.printStackTrace()
}
return result
}
}
open fun onSwipeRight() {}
open fun onSwipeLeft() {}
open fun onSwipeTop() {}
open fun onSwipeBottom() {}
}
Использование:
view.setOnTouchListener(object : OnSwipeTouchListener(context) {
override fun onSwipeTop() {
super.onSwipeTop()
}
override fun onSwipeBottom() {
super.onSwipeBottom()
}
override fun onSwipeLeft() {
super.onSwipeLeft()
}
override fun onSwipeRight() {
super.onSwipeRight()
}
})
open
ключевое слово было смыслом для меня...
Вам не нужны эти сложные вычисления. просто используя GestureDetector
в его OnGestureListener
внутри onFling
:
MyGestureListener.java
:
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
public class MyGestureListener implements GestureDetector.OnGestureListener{
private static final long VELOCITY_THRESHOLD = 3000;
@Override
public boolean onDown(final MotionEvent e){ return false; }
@Override
public void onShowPress(final MotionEvent e){ }
@Override
public boolean onSingleTapUp(final MotionEvent e){ return false; }
@Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY){ return false; }
@Override
public void onLongPress(final MotionEvent e){ }
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2,
final float velocityX,
final float velocityY){
if(Math.abs(velocityX) < VELOCITY_THRESHOLD
&& Math.abs(velocityY) < VELOCITY_THRESHOLD){
return false;//if the fling is not fast enough then it's just like drag
}
//if velocity in X direction is higher than velocity in Y direction,
//then the fling is horizontal, else->vertical
if(Math.abs(velocityX) > Math.abs(velocityY)){
if(velocityX >= 0){
Log.i("TAG", "swipe right");
}else{//if velocityX is negative, then it's towards left
Log.i("TAG", "swipe left");
}
}else{
if(velocityY >= 0){
Log.i("TAG", "swipe down");
}else{
Log.i("TAG", "swipe up");
}
}
return true;
}
}
использование:
GestureDetector mDetector = new GestureDetector(MainActivity.this, new MyGestureListener());
view.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(final View v, final MotionEvent event){
return mDetector.onTouchEvent(event);
}
});
Используйте SwipeListView и пусть он обрабатывает обнаружение жестов для вас.
Чтобы добавить onClick, вот что я сделал.
....
// in OnSwipeTouchListener class
private final class GestureListener extends SimpleOnGestureListener {
.... // normal GestureListener code
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
onClick(); // my method
return super.onSingleTapConfirmed(e);
}
} // end GestureListener class
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeTop() {
}
public void onSwipeBottom() {
}
public void onClick(){
}
// as normal
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
} // end OnSwipeTouchListener class
Я использую фрагменты, поэтому с помощью getActivity() для контекста. Вот как я это реализовал - и это работает.
myLayout.setOnTouchListener(new OnSwipeTouchListener(getActivity()) {
public void onSwipeTop() {
Toast.makeText(getActivity(), "top", Toast.LENGTH_SHORT).show();
}
public void onSwipeRight() {
Toast.makeText(getActivity(), "right", Toast.LENGTH_SHORT).show();
}
public void onSwipeLeft() {
Toast.makeText(getActivity(), "left", Toast.LENGTH_SHORT).show();
}
public void onSwipeBottom() {
Toast.makeText(getActivity(), "bottom", Toast.LENGTH_SHORT).show();
}
public void onClick(){
Toast.makeText(getActivity(), "clicked", Toast.LENGTH_SHORT).show();
}
});
Поскольку этот вопрос несколько устарел и очень популярен, я попытался обновить ответ и улучшить его, объединив несколько предложений в ответах и комментариях.
- Переключиться с Java на Kotlin и использовать синтаксис Kotlin Eyecandy (субъективно)
- Сделано, что onSwipe-функции возвращают независимо от того, использовали ли они событие ( )
- Присвоение результата снято с оператора if и удалена переменная (возврат напрямую)
- Альтернативы для обнаружения только вертикальных и горизонтальных пролистываний
- Альтернативы для обнаружения только вверх, вниз, вправо и влево.
- Верх и низ переименованы в Вверх и вниз для согласованности (направления, а не позиции).
Проведите пальцем во всех четырех направлениях:
open class OnSwipeTouchListener(val context: Context?) : OnTouchListener {
companion object {
private const val SwipeThreshold = 100
private const val SwipeVelocityThreshold = 100
}
private val gestureDetector = GestureDetector(context, GestureListener())
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
open fun onSwipeRight(): Boolean { return false }
open fun onSwipeLeft(): Boolean { return false }
open fun onSwipeUp(): Boolean { return false }
open fun onSwipeDown(): Boolean { return false }
private inner class GestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float,
): Boolean {
try {
val diffY = e2.y - e1.y
val diffX = e2.x - e1.x
if (abs(diffX) > abs(diffY)) {
if (abs(diffX) > SwipeThreshold && abs(velocityX) > SwipeVelocityThreshold) {
return when {
diffX > 0 -> onSwipeRight()
else -> onSwipeLeft()
}
}
} else if (abs(diffY) > SwipeThreshold && abs(velocityY) > SwipeVelocityThreshold) {
return when {
diffY > 0 -> onSwipeDown()
else -> onSwipeUp()
}
}
} catch (exception: Exception) {
exception.printStackTrace()
}
return false
}
}
}
Использование:
myView.setOnTouchListener(object : OnSwipeTouchListener(context) {
// Implement any function you need:
override fun onSwipeUp(): Boolean {
Toast.makeText(context, "UP", Toast.LENGTH_SHORT).show()
return true
}
override fun onSwipeLeft(): Boolean {
Toast.makeText(context, "LEFT", Toast.LENGTH_SHORT).show()
return true
}
})
Альтернативы onFling()
Альтернативы удаляют немного больше кода и меняют порядок проверок. Изменение порядка не обязательно, но я думаю, что это выглядит немного аккуратнее и его легче понять. Код должен быть помещен вtry
скобка. Использование остается прежним, просто реализуйте соответствующиеonSwipe
функции
Горизонтальный
Дополнительные примечания: в ( ответ ruXответ ruX ) они обсуждали удаление, чтобы также обнаружитьonClickListener
для того же вида. Однако, когда я удалил , класс больше не работал. Я точно не знаю, почему. Я посмотрел кодGestureDetector
но это очень сложно и последствия возвращаемого значенияonDown
кажется, имеет некоторые последствия. Поэтому я оставил его. Кроме того, вы можете просто использоватьSimpleOnGestureListener.onSingleTapUp()
так же, как тыonFling
и создатьonClick()
вызываемая там функция.
Информация о версии:
- compileSdkVersion 31
- minSdkВерсия 24
- targetSdkВерсия 31
Метод Эдварда Брея прекрасно работает. Если кто-то также хотел бы скопировать и вставить импорт для OnSwipeTouchListener
, Вот они:
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
Мое решение аналогично приведенному выше, но я абстрагировал обработку жестов в абстрактный класс OnGestureRegisterListener.java
, который включает жесты смахивания, нажатия и длительного нажатия.
OnGestureRegisterListener.java
public abstract class OnGestureRegisterListener implements View.OnTouchListener {
private final GestureDetector gestureDetector;
private View view;
public OnGestureRegisterListener(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
@Override
public boolean onTouch(View view, MotionEvent event) {
this.view = view;
return gestureDetector.onTouchEvent(event);
}
public abstract void onSwipeRight(View view);
public abstract void onSwipeLeft(View view);
public abstract void onSwipeBottom(View view);
public abstract void onSwipeTop(View view);
public abstract void onClick(View view);
public abstract boolean onLongClick(View view);
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
onLongClick(view);
super.onLongPress(e);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
onClick(view);
return super.onSingleTapUp(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight(view);
} else {
onSwipeLeft(view);
}
result = true;
}
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom(view);
} else {
onSwipeTop(view);
}
result = true;
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
}
И используйте это так. Обратите внимание, что вы также можете легко передать в View
параметр.
OnGestureRegisterListener onGestureRegisterListener = new OnGestureRegisterListener(this) {
public void onSwipeRight(View view) {
// Do something
}
public void onSwipeLeft(View view) {
// Do something
}
public void onSwipeBottom(View view) {
// Do something
}
public void onSwipeTop(View view) {
// Do something
}
public void onClick(View view) {
// Do something
}
public boolean onLongClick(View view) {
// Do something
return true;
}
};
Button button = findViewById(R.id.my_button);
button.setOnTouchListener(onGestureRegisterListener);
Я делаю подобные вещи, но только для горизонтальных ударов
import android.content.Context
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
abstract class OnHorizontalSwipeListener(val context: Context) : View.OnTouchListener {
companion object {
const val SWIPE_MIN = 50
const val SWIPE_VELOCITY_MIN = 100
}
private val detector = GestureDetector(context, GestureListener())
override fun onTouch(view: View, event: MotionEvent) = detector.onTouchEvent(event)
abstract fun onRightSwipe()
abstract fun onLeftSwipe()
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent) = true
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float)
: Boolean {
val deltaY = e2.y - e1.y
val deltaX = e2.x - e1.x
if (Math.abs(deltaX) < Math.abs(deltaY)) return false
if (Math.abs(deltaX) < SWIPE_MIN
&& Math.abs(velocityX) < SWIPE_VELOCITY_MIN) return false
if (deltaX > 0) onRightSwipe() else onLeftSwipe()
return true
}
}
}
И тогда его можно использовать для просмотра компонентов
private fun listenHorizontalSwipe(view: View) {
view.setOnTouchListener(object : OnHorizontalSwipeListener(context!!) {
override fun onRightSwipe() {
Log.d(TAG, "Swipe right")
}
override fun onLeftSwipe() {
Log.d(TAG, "Swipe left")
}
}
)
}
Небольшая модификация ответа @Mirek Rusin, и теперь вы можете обнаружить мультитач свайпы. Этот код на Kotlin:
class OnSwipeTouchListener(ctx: Context, val onGesture: (gestureCode: Int) -> Unit) : OnTouchListener {
private val SWIPE_THRESHOLD = 200
private val SWIPE_VELOCITY_THRESHOLD = 200
private val gestureDetector: GestureDetector
var fingersCount = 0
fun resetFingers() {
fingersCount = 0
}
init {
gestureDetector = GestureDetector(ctx, GestureListener())
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (event.pointerCount > fingersCount) {
fingersCount = event.pointerCount
}
return gestureDetector.onTouchEvent(event)
}
private inner class GestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
var result = false
try {
val diffY = e2.y - e1.y
val diffX = e2.x - e1.x
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
val gesture = when (fingersCount) {
1 -> Gesture.SWIPE_RIGHT
2 -> Gesture.TWO_FINGER_SWIPE_RIGHT
3 -> Gesture.THREE_FINGER_SWIPE_RIGHT
else -> -1
}
if (gesture > 0) {
onGesture.invoke(gesture)
}
} else {
val gesture = when (fingersCount) {
1 -> Gesture.SWIPE_LEFT
2 -> Gesture.TWO_FINGER_SWIPE_LEFT
3 -> Gesture.THREE_FINGER_SWIPE_LEFT
else -> -1
}
if (gesture > 0) {
onGesture.invoke(gesture)
}
}
resetFingers()
}
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
val gesture = when (fingersCount) {
1 -> Gesture.SWIPE_DOWN
2 -> Gesture.TWO_FINGER_SWIPE_DOWN
3 -> Gesture.THREE_FINGER_SWIPE_DOWN
else -> -1
}
if (gesture > 0) {
onGesture.invoke(gesture)
}
} else {
val gesture = when (fingersCount) {
1 -> Gesture.SWIPE_UP
2 -> Gesture.TWO_FINGER_SWIPE_UP
3 -> Gesture.THREE_FINGER_SWIPE_UP
else -> -1
}
if (gesture > 0) {
onGesture.invoke(gesture)
}
}
resetFingers()
}
result = true
} catch (exception: Exception) {
exception.printStackTrace()
}
return result
}
}}
Где Gesture.SWIPE_RIGHT и другие являются уникальным целочисленным идентификатором жеста, который я использую для определения вида жеста позже в моей деятельности:
rootView?.setOnTouchListener(OnSwipeTouchListener(this, {
gesture -> log(Gesture.parseName(this, gesture))
}))
Итак, вы видите, что жест здесь представляет собой целочисленную переменную, которая содержит значение, которое я передал ранее.
Нужны незначительные обновления. Переменные не совпадают в методе onTouch, и класс исключений не импортируется. Имеет больше смысла просто возвращать false из onFling(), вместо того, чтобы инициировать переменную, присваивать ей значение, ничего не делать с ним и просто возвращать его.
Некоторые части метода onTouch неверны. См. View / motionEvent не может быть преобразован в переменный удар
Один полезный совет, который бы сэкономил мне время, и я надеюсь, может помочь другим: когда вы используете этот метод, вам нужно только добавить тег "Implements" в ваш класс OnSwipeTouchListener. Ваша активность и вид не реализуют это. Они просто пользуются вашим классом, который уже делает!
+1 за Мирека, потому что его код все еще дал мне то, что мне было нужно для моего:)
Этот вопрос задавали много лет назад. Теперь есть лучшее решение: SmartSwipe: https://github.com/luckybilly/SmartSwipe
код выглядит так:
SmartSwipe.wrap(contentView)
.addConsumer(new StayConsumer()) //contentView stay while swiping with StayConsumer
.enableAllDirections() //enable directions as needed
.addListener(new SimpleSwipeListener() {
@Override
public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
//direction:
// 1: left
// 2: right
// 4: top
// 8: bottom
}
})
;
Использование ответа Эдварда Брея в Kotlin
view.setOnTouchListener(object: OnSwipeTouchListener(this) {
override fun onSwipeLeft() {
super.onSwipeLeft()
}
override fun onSwipeRight() {
super.onSwipeRight()
}
}
)
import android.content.Context
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
/**
* Detects left and right swipes across a view.
*/
class OnSwipeTouchListener(context: Context, onSwipeCallBack: OnSwipeCallBack?) : OnTouchListener {
private var gestureDetector : GestureDetector
private var onSwipeCallBack: OnSwipeCallBack?=null
init {
gestureDetector = GestureDetector(context, GestureListener())
this.onSwipeCallBack = onSwipeCallBack!!
}
companion object {
private val SWIPE_DISTANCE_THRESHOLD = 100
private val SWIPE_VELOCITY_THRESHOLD = 100
}
/* fun onSwipeLeft() {}
fun onSwipeRight() {}*/
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
private inner class GestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(eve1: MotionEvent?, eve2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
try {
if(eve1 != null&& eve2!= null) {
val distanceX = eve2?.x - eve1?.x
val distanceY = eve2?.y - eve1?.y
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (distanceX > 0)
onSwipeCallBack!!.onSwipeLeftCallback()
else
onSwipeCallBack!!.onSwipeRightCallback()
return true
}
}
}catch (exception:Exception){
exception.printStackTrace()
}
return false
}
}
}
Вот простой Android-код для определения направления жеста
В MainActivity.java
а также activity_main.xml
напишите следующий код:
MainActivity.java
import java.util.ArrayList;
import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.GestureStroke;
import android.gesture.Prediction;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity implements
OnGesturePerformedListener {
GestureOverlayView gesture;
GestureLibrary lib;
ArrayList<Prediction> prediction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lib = GestureLibraries.fromRawResource(MainActivity.this,
R.id.gestureOverlayView1);
gesture = (GestureOverlayView) findViewById(R.id.gestureOverlayView1);
gesture.addOnGesturePerformedListener(this);
}
@Override
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<GestureStroke> strokeList = gesture.getStrokes();
// prediction = lib.recognize(gesture);
float f[] = strokeList.get(0).points;
String str = "";
if (f[0] < f[f.length - 2]) {
str = "Right gesture";
} else if (f[0] > f[f.length - 2]) {
str = "Left gesture";
} else {
str = "no direction";
}
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();
}
}
activity_main.xml
<android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android1="http://schemas.android.com/apk/res/android"
xmlns:android2="http://schemas.android.com/apk/res/android"
android:id="@+id/gestureOverlayView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android1:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Draw gesture"
android:textAppearance="?android:attr/textAppearanceMedium" />
</android.gesture.GestureOverlayView>
Эта проблема все еще существует. AnOnTouchListener
с OnSwipeTouchListener
решает это простым способом:
myView.setOnTouchListener(
new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if(!swipe.onTouch(v, event)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// your code here
return true;
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
// your code here
return true;
}
}
return false;
}
}
);
где swipe
относится к классу, который записывает, были ли вызваны методы смахивания, а затем перенаправляет события делегату OnSwipeTouchListener
.
private class DirtyOnSwipeTouchListener extends OnSwipeTouchListener {
private boolean dirty = false;
private OnSwipeTouchListener delegate;
public DirtyOnSwipeTouchListener(Context ctx, OnSwipeTouchListener delegate) {
super(ctx);
this.delegate = delegate;
}
private void reset() {
dirty = false;
}
public void onSwipeTop() {
dirty = true;
delegate.onSwipeTop();
}
public void onSwipeRight() {
dirty = true;
delegate.onSwipeRight();
}
public void onSwipeLeft() {
dirty = true;
delegate.onSwipeLeft();
}
public void onSwipeBottom() {
dirty = true;
delegate.onSwipeBottom();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
try {
super.onTouch(v, event);
return dirty;
} finally {
dirty = false;
}
}
};
@Mirek Rusin отвечает очень хорошо. Но есть небольшая ошибка, и исправление требуется -
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
if (getOnSwipeListener() != null) {
getOnSwipeListener().onSwipeRight();
}
} else {
if (getOnSwipeListener() != null) {
getOnSwipeListener().onSwipeLeft();
}
}
result = true;
}
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
if (getOnSwipeListener() != null) {
getOnSwipeListener().onSwipeBottom();
}
} else {
if (getOnSwipeListener() != null) {
getOnSwipeListener().onSwipeTop();
}
}
result = true;
}
Какая разница? Мы устанавливаем result = true, только если мы проверили, что все повторные обращения (и SWIPE_THRESHOLD и SWIPE_VELOCITY_THRESHOLD в порядке). Это важно, если мы отбрасываем свайп, если некоторые из повторений не достигнуты, и мы должны сделать что-то в методе OnTouchEvent объекта OnSwipeTouchListener!
Я знаю, что это немного поздно с 2012 года, но я надеюсь, что это поможет кому-то, поскольку я думаю, что это более короткий и чистый код, чем большинство ответов:
view.setOnTouchListener((v, event) -> {
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
return super.onTouchEvent(event);
}
});
конечно, вы можете оставить себе только соответствующие жесты.
SRC: https://developer.android.com/training/gestures/detector
Последний импл работает только с этим:
@Override
public boolean onDown(MotionEvent e) {
return true;
}
Если вы хотите отобразить некоторые кнопки с действиями, когда элемент списка пролистан, есть много библиотек в Интернете, которые имеют такое поведение. Я реализовал библиотеку, которую нашел в интернете, и я очень доволен. Это очень просто в использовании и очень быстро. Я улучшил исходную библиотеку и добавил новый приемник щелчков для щелчка элементов. Также я добавил библиотеку шрифтов awesome ( http://fortawesome.github.io/Font-Awesome/), и теперь вы можете просто добавить новый заголовок элемента и указать имя значка из шрифта awesome.
Вот ссылка на GitHub
public class TranslatorSwipeTouch implements OnTouchListener
{
private String TAG="TranslatorSwipeTouch";
@SuppressWarnings("deprecation")
private GestureDetector detector=new GestureDetector(new TranslatorGestureListener());
@Override
public boolean onTouch(View view, MotionEvent event)
{
return detector.onTouchEvent(event);
}
private class TranslatorGestureListener extends SimpleOnGestureListener
{
private final int GESTURE_THRESHOULD=100;
private final int GESTURE_VELOCITY_THRESHOULD=100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent event1,MotionEvent event2,float velocityx,float velocityy)
{
try
{
float diffx=event2.getX()-event1.getX();
float diffy=event2.getY()-event1.getY();
if(Math.abs(diffx)>Math.abs(diffy))
{
if(Math.abs(diffx)>GESTURE_THRESHOULD && Math.abs(velocityx)>GESTURE_VELOCITY_THRESHOULD)
{
if(diffx>0)
{
onSwipeRight();
}
else
{
onSwipeLeft();
}
}
}
else
{
if(Math.abs(diffy)>GESTURE_THRESHOULD && Math.abs(velocityy)>GESTURE_VELOCITY_THRESHOULD)
{
if(diffy>0)
{
onSwipeBottom();
}
else
{
onSwipeTop();
}
}
}
}
catch(Exception e)
{
Log.d(TAG, ""+e.getMessage());
}
return false;
}
public void onSwipeRight()
{
//Toast.makeText(this.getClass().get, "swipe right", Toast.LENGTH_SHORT).show();
Log.i(TAG, "Right");
}
public void onSwipeLeft()
{
Log.i(TAG, "Left");
//Toast.makeText(MyActivity.this, "swipe left", Toast.LENGTH_SHORT).show();
}
public void onSwipeTop()
{
Log.i(TAG, "Top");
//Toast.makeText(MyActivity.this, "swipe top", Toast.LENGTH_SHORT).show();
}
public void onSwipeBottom()
{
Log.i(TAG, "Bottom");
//Toast.makeText(MyActivity.this, "swipe bottom", Toast.LENGTH_SHORT).show();
}
}
}