[英]How to detect swipe direction between left/right and up/down
My Question: How do I detect when a user moves their finger up/down vs left/right (and how do I know which direction of those groups their finger moved)?我的问题:我如何检测用户何时向上/向下和向左/向右移动手指(以及我如何知道他们的手指移动的那些组的哪个方向)?
My Situation: I want to change the brightness of my app when they move their finger up and down (up = brighter, down = darker), and I want to switch between activities and/or views based on their left/right swipe.我的情况:我想在他们上下移动手指时更改我的应用程序的亮度(向上 = 更亮,向下 = 更暗),并且我想根据他们的左/右滑动在活动和/或视图之间切换。
I wrote a simple class for this: it's well documented so I wont explain it here我为此编写了一个简单的类:它有很好的文档记录,所以我不会在这里解释
public class OnSwipeListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2)
// Let e1 be the initial event
// e2 can be located at 4 different positions, consider the following diagram
// (Assume that lines are separated by 90 degrees.)
//
//
// \ A /
// \ /
// D e1 B
// / \
// / C \
//
// So if (x2,y2) falls in region:
// A => it's an UP swipe
// B => it's a RIGHT swipe
// C => it's a DOWN swipe
// D => it's a LEFT swipe
//
float x1 = e1.getX();
float y1 = e1.getY();
float x2 = e2.getX();
float y2 = e2.getY();
Direction direction = getDirection(x1,y1,x2,y2);
return onSwipe(direction);
}
/** Override this method. The Direction enum will tell you how the user swiped. */
public boolean onSwipe(Direction direction){
return false;
}
/**
* Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method
* returns the direction that an arrow pointing from p1 to p2 would have.
* @param x1 the x position of the first point
* @param y1 the y position of the first point
* @param x2 the x position of the second point
* @param y2 the y position of the second point
* @return the direction
*/
public Direction getDirection(float x1, float y1, float x2, float y2){
double angle = getAngle(x1, y1, x2, y2);
return Direction.fromAngle(angle);
}
/**
*
* Finds the angle between two points in the plane (x1,y1) and (x2, y2)
* The angle is measured with 0/360 being the X-axis to the right, angles
* increase counter clockwise.
*
* @param x1 the x position of the first point
* @param y1 the y position of the first point
* @param x2 the x position of the second point
* @param y2 the y position of the second point
* @return the angle between two points
*/
public double getAngle(float x1, float y1, float x2, float y2) {
double rad = Math.atan2(y1-y2,x2-x1) + Math.PI;
return (rad*180/Math.PI + 180)%360;
}
public enum Direction{
up,
down,
left,
right;
/**
* Returns a direction given an angle.
* Directions are defined as follows:
*
* Up: [45, 135]
* Right: [0,45] and [315, 360]
* Down: [225, 315]
* Left: [135, 225]
*
* @param angle an angle from 0 to 360 - e
* @return the direction of an angle
*/
public static Direction fromAngle(double angle){
if(inRange(angle, 45, 135)){
return Direction.up;
}
else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){
return Direction.right;
}
else if(inRange(angle, 225, 315)){
return Direction.down;
}
else{
return Direction.left;
}
}
/**
* @param angle an angle
* @param init the initial bound
* @param end the final bound
* @return returns true if the given angle is in the interval [init, end).
*/
private static boolean inRange(double angle, float init, float end){
return (angle >= init) && (angle < end);
}
}
}
To use simply extend the OnSwipeListener
and override the onSwipe
method使用简单地扩展
OnSwipeListener
并覆盖onSwipe
方法
You simply have to extend SimpleOnGestureListener class,你只需要扩展 SimpleOnGestureListener 类,
Declare this in your class,在你的课堂上声明这一点,
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;
As an example for horizontal swipe you can see the below code,作为水平滑动的示例,您可以看到以下代码,
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) {
onLeftSwipe();
}
// left to right swipe
else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
onRightSwipe();
}
} catch (Exception e) {
}
return false;
}
}
You can do this similarly for vertical swipe purpose.您可以类似地为垂直滑动目的执行此操作。
Fernandour answer is perfect, I am writing this answer about how to use it with Activity
and Fragment
as many people are looking for it. Fernandour 的答案是完美的,我正在写这个关于如何在
Activity
和Fragment
使用它的答案,因为很多人都在寻找它。
public class MyActivity extends Activity implements View.OnTouchListener{
private RelativeLayout someLayout;
//take any layout on which you want your gesture listener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gestureDetector=new GestureDetector(this,new OnSwipeListener(){
@Override
public boolean onSwipe(Direction direction) {
if (direction==Direction.up){
//do your stuff
Log.d(TAG, "onSwipe: up");
}
if (direction==Direction.down){
//do your stuff
Log.d(TAG, "onSwipe: down");
}
return true;
}
});
someLayout.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "onTouch: ");
gestureDetector.onTouchEvent(event);
return true;
}
}
A full Usage Example for fernandohur answer above:上面fernandohur 答案的完整用法示例:
If you want to apply OnSwipeListener to one of your views, so:如果要将 OnSwipeListener 应用于您的视图之一,请执行以下操作:
Wherever this view is - set a touch listener for that view, like this:无论此视图在哪里 - 为该视图设置一个触摸侦听器,如下所示:
myview.setOnTouchListener(this);
Now in your Activity's OnCreate or in your Custom view constructor do this:现在在您的 Activity 的 OnCreate 或您的自定义视图构造函数中执行以下操作:
// Global
private GestureDetectorCompat detector;
// In OnCreate or custome view constructor (which extends one of Android views)
detector = new GestureDetectorCompat(context, onSwipeListener);
Override in the same class the onTouch event, like this:在同一个类中覆盖 onTouch 事件,如下所示:
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return detector.onTouchEvent(motionEvent);
}
And also have in the same class this listener object:并且在同一个类中也有这个监听器对象:
OnSwipeListener onSwipeListener = new OnSwipeListener() {
@Override
public boolean onSwipe(Direction direction) {
// Possible implementation
if(direction == Direction.left|| direction == Direction.right) {
// Do something COOL like animation or whatever you want
// Refer to your view if needed using a global reference
return true;
}
else if(direction == Direction.up|| direction == Direction.down) {
// Do something COOL like animation or whatever you want
// Refer to your view if needed using a global reference
return true;
}
return super.onSwipe(direction);
}
};
that is how I did it, easiest way我就是这样做的,最简单的方法
float initialX, initialY;
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
initialX = event.getX();
initialY = event.getY();
// Log.d(TAG, "Action was DOWN");
break;
case MotionEvent.ACTION_MOVE:
//Log.d(TAG, "Action was MOVE");
break;
case MotionEvent.ACTION_UP:
float finalX = event.getX();
float finalY = event.getY();
//Log.d(TAG, "Action was UP");
if (initialX < finalX) {
// Log.d(TAG, "Left to Right swipe performed");
}
if (initialX > finalX) {
// Log.d(TAG, "Right to Left swipe performed");
}
if (initialY < finalY) {
// Log.d(TAG, "Up to Down swipe performed");
}
if (initialY > finalY) {
// Log.d(TAG, "Down to Up swipe performed");
}
break;
case MotionEvent.ACTION_CANCEL:
//Log.d(TAG,"Action was CANCEL");
break;
case MotionEvent.ACTION_OUTSIDE:
// Log.d(TAG, "Movement occurred outside bounds of current screen element");
break;
}
return super.onTouchEvent(event);
}
You could override SimpleGestureListener and calculate the diff between start end current coordinates:您可以覆盖 SimpleGestureListener 并计算开始结束当前坐标之间的差异:
private class GestureListener extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e2.getY() > e1.getY()) {
// direction up
}else {
// direction down
}
if (e2.getX() > e1.getX()) {
// direction right
}else {
// direction left
}
return true;
}
}
I solved this way:我是这样解决的:
viewPager.setOnTouchListener(new View.OnTouchListener() {
float prevX = -1;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (prevX != -1) {
if (event.getX() > prevX) {
if (viewPager.getCurrentItem() == 0) {
// Left to Right swipe
}
//Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Left Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem());
} else if (prevX > event.getX()) {
// Right to left swipe
//Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Right Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem());
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
prevX = event.getX();
} else {
prevX = -1;
}
return false;
}
});
The best answer that i test it several times and its worked for me我多次测试它并且对我有用的最佳答案
float firstX_point, firstY_point;
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
firstX_point = event.getRawX();
firstY_point = event.getRawY();
break;
case MotionEvent.ACTION_UP:
float finalX = event.getRawX();
float finalY = event.getRawY();
int distanceX = (int) (finalX - firstX_point);
int distanceY = (int) (finalY - firstY_point);
if (Math.abs(distanceX) > Math.abs(distanceY)) {
if ((firstX_point < finalX)) {
Log.d("Test", "Left to Right swipe performed");
} else {
Log.d("Test", "Right to Left swipe performed");
}
}else{
if ((firstY_point < finalY)) {
Log.d("Test", "Up to Down swipe performed");
} else {
Log.d("Test", "Down to Up swipe performed");
}
}
break;
}
return true;
}
I have an open source gesture library on bitbucket that does this.我在 bitbucket 上有一个开源手势库可以做到这一点。 Within this library is a 'HGFling' class.
在这个库中是一个“HGFling”类。 This demonstrates how to detect the direction of a fling.
这演示了如何检测甩动的方向。 You can download the library from: https://bitbucket.org/warwick/hacergestov3 .
您可以从以下位置下载该库: https : //bitbucket.org/warwick/hacergestov3 。 It's open source.
它是开源的。
The answers available are far too complex for such a simple problem.对于这样一个简单的问题,可用的答案太复杂了。 I suggest another approach to it (code is as3, but you can get the idea):
我建议另一种方法(代码是 as3,但您可以理解):
var touchDistance:Number = Point.distance(_moveTouchPoint, _startTouchPoint);
if (touchDistance >= SWIPE_MIN_DISTANCE)
{
var xDiff:Number = _moveTouchPoint.x - _startTouchPoint.x;
var yDiff:Number = _moveTouchPoint.y - _startTouchPoint.y;
var yGreater:Boolean = Math.abs(yDiff) >= Math.abs(xDiff);
if (yGreater)
{
// direction is up or down
changePlayerDirectionTo(yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN);
}
else
{
// direction is left or right
changePlayerDirectionTo(xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT)
}
}
In each case either x or y will be greater in absolute value, which can resolve to certain direction set.在每种情况下,x 或 y 的绝对值都会更大,这可以解析为特定的方向集。 From then on you can rely on the coordinate sign to detect which direction exactly.
从那时起,您可以依靠坐标符号来准确检测哪个方向。
Adding Kotlin
implementation for @Fernandour answer.为@Fernandour答案添加
Kotlin
实现。 For Java look at @f arhan patel answer I am adding this because I was having difficulty, hope it will save someone#s time.对于Java,请查看@f arhan patel 的答案,因为我遇到了困难,所以我添加了这个,希望它能节省某人的时间。
class ClientFragment : Fragment(), View.OnTouchListener {
private lateinit var gestureDetector: GestureDetector
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
Log.d(TAG, "onTouch: ");
gestureDetector.onTouchEvent(event);
return true
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
...
gestureDetector = GestureDetector(activity, object : OnSwipeListener() {
override fun onSwipe(direction: Direction): Boolean {
when(direction){
Direction.up ->
{
Log.d(TAG, "onSwipe: up")
sendCommand("UP")
return true
}
Direction.down ->{
Log.d(TAG, "onSwipe: down")
sendCommand("DOWN")
return true
}
Direction.left ->
{
Log.d(TAG, "onSwipe: left")
sendCommand("LEFT")
return true
}
Direction.right ->{
Log.d(TAG, "onSwipe: right")
sendCommand("RIGHT")
return true
}
else -> {
}
}
return true
}
})
dpadLayout.setOnTouchListener(this)
the extended version of @Fernandour 's answer easy to implement direct with on touch listener no to do extra code… with on touch o'clock ,long press ,double click implementation…. @Fernandour 的答案的扩展版本很容易直接通过触摸监听器实现,不需要做额外的代码......触摸时,长按,双击实现......。
public class OnSwipeListener implements View.OnTouchListener{
public enum Direction{up,down,left,right;}
private GestureDetector gestureDetector;
private Context context;
public OnSwipeListener(Context c) {
this.context = c;
gestureDetector = new GestureDetector(c, new GestureListener(c));
}
public boolean onTouch(final View view, final MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeUp() {
}
public void onSwipeDown() {
}
public void onClick() {
}
public void onDoubleClick() {
}
public void onLongClick() {
}
public double getAngle(float x1, float y1, float x2, float y2) {
double rad = Math.atan2(y1-y2,x2-x1) + Math.PI;
return (rad*180/Math.PI + 180)%360;
}
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
Context context;
public GestureListener(Context c) {
this.context = c;
}
@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);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float x1 = e1.getX();
float y1 = e1.getY();
float x2 = e2.getX();
float y2 = e2.getY();
Direction direction = getDirection(x1,y1,x2,y2);
return onSwipe(direction);
}
public Direction getDirection(float x1, float y1, float x2, float y2){
double angle = getAngle(x1, y1, x2, y2);
return fromAngle(angle);
}
public Direction fromAngle(double angle){
if(inRange(angle, 45, 135)){
onSwipeUp();
return Direction.up;
}
else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){
onSwipeRight();
return Direction.right;
}
// else if(inRange(angle, 225, 315)){
// //onSwipeDown();
//
// }
else if(inRange(angle,135, 225)){
onSwipeLeft();
return Direction.left;
}
else {
return Direction.down;
}
}
private boolean inRange(double angle, float init, float end){
return (angle >= init) && (angle < end);
}
public boolean onSwipe(Direction direction){
return false;
}
};
}
Best Way and Simple Way to detect Swipe left,Right,Top,Bottom
1) First Make One Java Class and implments: - View.OnTouchListener
Add below code in this Class: -
public class OnSwipeTouchListener implements View.OnTouchListener {
private final GestureDetector gestureDetector;
Context context;
OnSwipeTouchListener(Context ctx, View mainView) {
gestureDetector = new GestureDetector(ctx, new GestureListener());
mainView.setOnTouchListener(this);
context = ctx;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
public 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 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;
}
}
void onSwipeRight() {
Toast.makeText(context, "You Swipe Right", Toast.LENGTH_SHORT).show();
this.onSwipe.swipeRight();
}
void onSwipeLeft() {
Toast.makeText(context, "You Swipe Left", Toast.LENGTH_SHORT).show();
this.onSwipe.swipeLeft();
}
void onSwipeTop() {
Toast.makeText(context, "You Swipe Up", Toast.LENGTH_SHORT).show();
this.onSwipe.swipeTop();
}
void onSwipeBottom() {
Toast.makeText(context, "You Swipe Down", Toast.LENGTH_SHORT).show();
this.onSwipe.swipeBottom();
}
interface onSwipeListener {
void swipeRight();
void swipeTop();
void swipeBottom();
void swipeLeft();
}
onSwipeListener onSwipe;
}
And In Your MainActivity Class Use This Code: -
public class MainActivity extends AppCompatActivity {
OnSwipeTouchListener onSwipeTouchListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
onSwipeTouchListener = new OnSwipeTouchListener(this, findViewById(R.id.relativeLayout));
}
}
In activity_main.xml: -
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
</RelativeLayout>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.