How to Detect Common Gestures- Detect Drag
When we want to detect a subset (not all of them) of common gestures, we can use
GestureDetector.SimpleOnGestureListener which is an implementation of
the GestureDetector.OnGestureListener interface.
Note that if we use GestureDetector.OnGestureListener, all of the common gestures must be overrided.
GestureDetector.SimpleOnGestureListener provides an implementation for all of the on methods by returning false for all of them. Thus you can
override only the methods you care about.
Whether or not you use GestureDetector.OnGestureListener, it's best practice to implement an onDown() method that returns true. This is because all gestures begin with an onDown() message. If you return false from onDown(), as GestureDetector.SimpleOnGestureListener does by default, the system assumes that you want to ignore the rest of the gesture, and the other methods of GestureDetector.OnGestureListener never get called. This has the potential to cause unexpected problems in your app. The only time you should return false from onDown() is if you truly want to ignore an entire gesture.
List of all common gestures are:
Example: Here we try to do a practice of overriding onScroll() to drag a drawable (image) inside a frame.
Create a new project with an empty template. I extend a new view called ImageBoard. To define new attributes (see also custom view in Android Apps) go to res->values->attrs (if there is not such file, you can create it: right click on the values folder and select New->values resource file then enter attrs as File name). Edit attrs.xml as below. Here we define just src attribute as a reference or color.
The ImageBoard class that is the core of this tiny project is like this.
and the activity_main.xml file is shown below.
Now just run the project and see the result which should be something like this.
Note that if we use GestureDetector.OnGestureListener, all of the common gestures must be overrided.
GestureDetector.SimpleOnGestureListener provides an implementation for all of the on
Whether or not you use GestureDetector.OnGestureListener, it's best practice to implement an onDown() method that returns true. This is because all gestures begin with an onDown() message. If you return false from onDown(), as GestureDetector.SimpleOnGestureListener does by default, the system assumes that you want to ignore the rest of the gesture, and the other methods of GestureDetector.OnGestureListener never get called. This has the potential to cause unexpected problems in your app. The only time you should return false from onDown() is if you truly want to ignore an entire gesture.
List of all common gestures are:
- onDown(MotionEvent e): Notified when a tap occurs with the down MotionEvent that triggered it.
- onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY): Notified of a fling event when it occurs with the initial on down MotionEvent and the matching up MotionEvent.
- onLongPress(MotionEvent e): Notified when a long press occurs with the initial on down MotionEvent that trigged it.
- onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY): Notified when a scroll occurs with the initial on down MotionEvent and the current move MotionEvent.
- onShowPress(MotionEvent e): The user has performed a down MotionEvent and not performed a move or up yet.
- onSingleTapUp(MotionEvent e): Notified when a tap occurs with the up MotionEvent that triggered it.
Example: Here we try to do a practice of overriding onScroll() to drag a drawable (image) inside a frame.
Create a new project with an empty template. I extend a new view called ImageBoard. To define new attributes (see also custom view in Android Apps) go to res->values->attrs (if there is not such file, you can create it: right click on the values folder and select New->values resource file then enter attrs as File name). Edit attrs.xml as below. Here we define just src attribute as a reference or color.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="ImageBoard"> | |
<attr name="src" format="reference|color"/> | |
</declare-styleable> | |
</resources> |
The ImageBoard class that is the core of this tiny project is like this.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ImageBoard extends View { | |
RectF mCurrentViewPort; | |
Rect mContentRect; | |
ImageView image; | |
int width; | |
int height; | |
int dragedWidth = 0; | |
int dragedHeight = 0; | |
int dx = 0; | |
int dy = 0; | |
Paint boarderPaint; | |
int drawableWidth; | |
int drawableHeight; | |
Drawable src; | |
//GestureDetector gd; | |
GestureDetectorCompat gdc; | |
public ImageBoard(Context context){ | |
//this(context, null, 0); | |
super(context); | |
} | |
public ImageBoard(Context context, AttributeSet attrs){ | |
//this(context, attrs, 0); | |
super(context, attrs); | |
init(context, attrs); | |
} | |
public ImageBoard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(context, attrs); | |
} | |
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) | |
public ImageBoard(Context context, @Nullable AttributeSet attrs, | |
int defStyleAttr, int defStyleRes){ | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(context, attrs); | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
width = w; | |
height = h; | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ | |
int widthSize = MeasureSpec.getSize(widthMeasureSpec); | |
int heightSize = MeasureSpec.getSize(heightMeasureSpec); | |
int widthMode = MeasureSpec.getMode(widthMeasureSpec); | |
int heightMode = MeasureSpec.getMode(heightMeasureSpec); | |
int desiredWidth = 300; | |
int desiredHeight = 300; | |
int width; | |
int height; | |
//width | |
if(widthMode == MeasureSpec.EXACTLY){ | |
//Must be this size | |
width = widthSize; | |
}else if(widthMode == MeasureSpec.AT_MOST){ | |
//Can't be bigger than... | |
width = Math.min(widthSize, desiredWidth); | |
}else{ | |
//Be whatever you want | |
width = desiredWidth; | |
} | |
//height | |
if(heightMode == MeasureSpec.EXACTLY){ | |
//Must be this size | |
height = heightSize; | |
}else if(heightMode == MeasureSpec.AT_MOST){ | |
//Can't be bigger than... | |
height = Math.min(heightSize, desiredHeight); | |
}else{ | |
//Be whatever you want | |
height = desiredHeight; | |
} | |
//MUST CALL THIS | |
setMeasuredDimension(width, height); | |
} | |
public void init(Context context, @Nullable AttributeSet attrs){ | |
//gd = new GestureDetector(context, mGestureListener); | |
gdc = new GestureDetectorCompat(context, new MyGestureListener()); | |
mCurrentViewPort = new RectF(); | |
mContentRect = new Rect(); | |
boarderPaint = new Paint(); | |
boarderPaint.setColor(Color.BLUE); | |
boarderPaint.setStyle(Paint.Style.STROKE); | |
boarderPaint.setStrokeWidth(20); | |
TypedArray ta = context. | |
obtainStyledAttributes(attrs, R.styleable.ImageBoard, 0, 0); | |
try{ | |
src = (Drawable) ta.getDrawable(R.styleable.ImageBoard_src); | |
drawableWidth = src.getIntrinsicWidth(); | |
drawableHeight = src.getIntrinsicHeight(); | |
}catch(Exception ex){ | |
Toast.makeText(context, "Error: " + ex.toString(), | |
Toast.LENGTH_LONG + 3000).show(); | |
} | |
finally { | |
ta.recycle(); | |
} | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
mContentRect.left = (int) dx; | |
mContentRect.top = (int) dy; | |
mContentRect.right = (int) dx + drawableWidth; | |
mContentRect.bottom = (int) dy + drawableHeight; | |
mCurrentViewPort.left = 0; | |
mCurrentViewPort.top = 0; | |
mCurrentViewPort.right = width; | |
mCurrentViewPort.bottom = height; | |
if(src != null) | |
{ | |
src.setBounds(mContentRect); | |
src.draw(canvas); | |
} | |
canvas.drawRect(mCurrentViewPort, boarderPaint); | |
} | |
class MyGestureListener extends GestureDetector.SimpleOnGestureListener{ | |
@Override | |
public boolean onDown(MotionEvent e){ | |
return true; | |
} | |
@Override | |
public boolean onScroll(MotionEvent e1, MotionEvent e2, | |
float distanceX, float distanceY){ | |
dx -= (int) distanceX; | |
dy -= (int) distanceY; | |
ImageBoard.this.invalidate(); | |
return true; | |
} | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent e){ | |
this.gdc.onTouchEvent(e); | |
// return super.onTouchEvent(e); | |
return true; | |
} | |
} |
and the activity_main.xml file is shown below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<androidx.constraintlayout.widget.ConstraintLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<com.msh_n.myp.myapplication.ImageBoard | |
android:layout_width="100dp" | |
android:layout_height="100dp" | |
app:src="@drawable/ic_launcher_foreground" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="parent" /> | |
</androidx.constraintlayout.widget.ConstraintLayout> |
Now just run the project and see the result which should be something like this.
Comments
Post a Comment