Android Studio - How to use implement swipe gesture in android game - java

I was working on a game on Android studio where user touch drives tiles to combine. To do this, I have used a GridLayout separated into rows and columns. The value is displayed with imageview and all gridlayout cells are linked to a separate cell class which includes imageview ID's. I implemented a listener on each image view to detect when a swiping motion occured. The code for initalizing the board and listeners is below:
for(int i = 0; i<noOfBlocks; i++) {
for (int j = 0; j < noOfBlocks; j++) {
ImageView imageView = new ImageView(this);
imageView.setId(iDcnt);
imageView.setLayoutParams(new ViewGroup.LayoutParams(widthOfBlock, widthOfBlock));
imageView.setMaxHeight(widthOfBlock);
imageView.setMaxWidth(widthOfBlock);
int randomNum = (int) Math.floor(random.nextDouble() * gamepiece.length);
imageView.setImageResource(gamepiece[randomNum]);
Cell c = new Cell(i + 1, j+1, randomNum, imageView.getId());
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
SquarePlay s = null;
try {
s = new SquarePlay();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
s.onSwipeEvent(event);
return false;
}
});
gameBoard.addView(imageView);
iDcnt++;
}
The OnTouch events are handled in a separate class in the method which is shown below:
float x1, x2, y1, y2, dx, dy;
public void onSwipeEvent(MotionEvent event){
switch(event.getAction()) {
case(MotionEvent.ACTION_DOWN):
x1 = event.getX();
y1 = event.getY();
break;
case(MotionEvent.ACTION_UP): {
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(Math.abs(dx) > Math.abs(dy)) {
if(dx>0)
onSwipeRight();
else
onSwipeLeft();
} else {
if(dy>0)
onSwipeDown();
else
onSwipeUp();
}
}
}
}
private void onSwipeLeft() {
System.exit(0);
}
private void onSwipeRight() {
System.exit(0);
}
private void onSwipeUp() {
System.exit(0);
}
private void onSwipeDown() {
System.exit(0);
}
NOTE:System.exit is just there to test if this works.
The App loads but does not produce a response from the swipe events, Does anyone have any suggestions on how to resolve this issue?
Thank you :)

To use swipe You can create your class inside the activity class as follow##
declare the class name inside the activity class
SwipeListener swipeListener;
inside the activity class write this code to handle the swipe##
private class SwipeListener implements View.OnTouchListener {
//create gesture detector variable
GestureDetector gestureDetector;
//create constructor
SwipeListener(View view){
int threshold = 100;
int velocity_threshold = 100;
GestureDetector.SimpleOnGestureListener listener =
new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onDown(MotionEvent e) {
return super.onDown(e);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//get x and y difference
float xDiff = e2.getX() - e1.getX();
float yDiff = e2.getY() - e1.getY();
try {
if(Math.abs(xDiff)>Math.abs(yDiff)){
if(Math.abs(xDiff)>threshold &&
Math.abs(velocityX)> velocity_threshold){
if(xDiff > 0){
//swipe right
Intent intent = new Intent(TopUpBalance.this, HomePage.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}else{
//swipe left
}
return true;
}//you can handle swipe down and above here as well, the same way above
}
}catch (Exception e){
e.printStackTrace();
}
return false;
}
};
//init gesture
gestureDetector = new GestureDetector(listener);
view.setOnTouchListener(this);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
}
Declare your SwipeListener onCreate method as follow##
linearLayout = findViewById(R.id.layoutID);
//swipe
swipeListener = new SwipeListener(linearLayout);

Related

How to Track all gestures on ENTIRE android screen?

How do I track all gestures on an entire application screen. I was struggling with adding a gesture detector to the entire screen of my application. I am using gesturedetector compat to get gestures from the layout however when I tap or double tap the button and edit text gesturedetector does not detect the gesture on the view. Is it possible to have all tap,swipes,gestures on the ENTIRE screen as opposed to only the layout. If there is a better solution to tracking all touches,swipes,taps please let me know I have struggling with this for weeks.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private GestureDetectorCompat mGestureDetector;
private TextView touchCheckText;
private TextView singleTapText;
private TextView doubleTapText;
private static final String TAG = "Gesture ";
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction()!=KeyEvent.ACTION_DOWN)
Log.i("key pressed", String.valueOf(event.getKeyCode()));
return super.dispatchKeyEvent(event);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGestureDetector = new GestureDetectorCompat(this,new GestureListener());
touchCheckText = (TextView) findViewById(R.id.touchCheckTextView);
scrollText = (TextView) findViewById(R.id.scrollTextView);
singleTapText = (TextView) findViewById(R.id.singleTapTextView);
doubleTapText = (TextView) findViewById(R.id.doubleTapTextView);
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
#Override
public boolean onDoubleTap(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
Long tsLong = System.currentTimeMillis()/1000;
String ts = tsLong.toString();
Log.e(TAG, "in on onDoubleTap Event");
doubleTapText.setBackgroundColor(Color.GREEN);
return super.onDoubleTap(e);
}
#Override
public boolean onDown(MotionEvent e) {
touchCheckText.setText("TOUCHING!");
return super.onDown(e);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
Long tsLong = System.currentTimeMillis()/1000;
String ts = tsLong.toString();
singleTapText.setBackgroundColor(Color.GREEN);
Log.e(TAG, "Single Tap "+ts+" x:"+x+" y:"+y);
singleTapText.postDelayed(new Runnable() {
#Override
public void run() {
// change color in here
singleTapText.setBackgroundColor(Color.TRANSPARENT);
}
}, 100);
return super.onSingleTapConfirmed(e);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
switch ( event.getAction() ) {
case MotionEvent.ACTION_UP:
Log.e("onTouch","Released");
doubleTapText.setBackgroundColor(Color.TRANSPARENT);
touchCheckText.setText("No Longer Touching");
break;
}
return super.onTouchEvent(event);
}
}

How to implement pinch zooming in Android?

After some research on the SO, I cannot find the way to implement the pinch zooming using android.hardware.camera. I am using SeekBar but this is not what I need. Is anyone who knows where I can find an example that will works on Android SKD >= 15?
In my cameraFragment, I am using OnSeekBarChangeListener
and in the method onProgressChanged I set the zoom for the camera.
seekbar.setOnSeekBarChangeListener(this);
.....
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
try {
Camera.Parameters params = camera.getParameters();
params.setZoom(progress);
currentZoomLevel = progress;
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
camera.setParameters(params);
//set zoom level value
try {
float ratio = ((float) params.getZoomRatios().get(progress)) / 100;
tvZoomLevel.setText(String.format("%.1fX", ratio));
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
}
1.Override onTouchEvent(MotionEvent event)
2.Get the maximum zoom level of camera param
3.Adjust your pinch zoom into a camera param
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
public void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
// ...
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
This certain step help you a basic implemention of your requirement.

Disable swipe on some fragments in ViewPager

I have a ViewPager that can disable or enable swipe touches:
public class ConfigurablePager extends ViewPager {
private final AtomicBoolean touchesAllowed = new AtomicBoolean();
...
private boolean touchesAllowed() {
return touchesAllowed.get();
}
public void enableTouches() {
touchesAllowed.set(true);
}
public void disableTouches() {
touchesAllowed.set(false);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return touchesAllowed() && super.onTouchEvent(ev);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return touchesAllowed() && super.onInterceptTouchEvent(ev);
}
}
Some fragments can be swiped but other can't. Pager adapter aware of swipe behaviour for each fragment. This behaviour can be changed in ViewPager.OnPageChangeListener:
#Override
public void onPageSelected(int position) {
if (adapter.isTouchesAllowed(position)) {
views.pager.enableTouches();
} else {
views.pager.disableTouches();
}
}
The problem
Sometimes, when I swipe fragments very fast and click on tab for other fragment simultaneously viewpager can throw IllegalArgumentException:
FATAL EXCEPTION:
main java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:1979)
at android.support.v4.view.MotionEventCompatEclair.getX(MotionEventCompatEclair.java:32)
at android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.getX(MotionEventCompat.java:110)
at android.support.v4.view.MotionEventCompat.getX(MotionEventCompat.java:462)
at android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:2080)
at com.test.debugpager.ConfigurablePager.onTouchEvent(ConfigurablePager.java:39)
at android.view.View.dispatchTouchEvent(View.java:7384)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2203)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1938)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2231)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1952)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2209)
It's happend because ViewPager save last pointerId and get inconsistent state (some touch events dropped by onInterceptTouchEvent) e.g. ACTION_MOVE with incorrect mActivePointerId from last touch event (see sources of ViewPager.java)
The question
Is it posible to disable swipe on some fragments in other way, maybe without overriding onInterceptTouchEvent?
ViewPager sources (onTouchEvent):
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
// Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
Solved
I've read intently android guide about gesture recognizing in a ViewGroup and analyzed ViewPager onTouchEvent sources. Here I recognize that ViewPager do swipe only for ACTION_MOVE event so we shouldn't call touch callbacks only for this action and we should obey base ViewGroup onInterceptTouchEvent result before calling base class onTouchEvent.
According to this rules I changed my ViewPager code:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (touchesAllowed()) {
return super.onInterceptTouchEvent(ev);
} else {
if (MotionEventCompat.getActionMasked(ev) == MotionEvent.ACTION_MOVE) {
// ignore move action
} else {
if (super.onInterceptTouchEvent(ev)) {
super.onTouchEvent(ev);
}
}
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (touchesAllowed()) {
return super.onTouchEvent(ev);
} else {
return MotionEventCompat.getActionMasked(ev) != MotionEvent.ACTION_MOVE && super.onTouchEvent(ev);
}
}

ViewPager Disable Swiping

I have a ViewPager which has 3 fragments. Fragment A on the left, B in the middle and C on the right. Fragment C has a ListView which fills the whole width of the screen. I implemented a swipe listener on my ListView items using the following code:
SWIPE DETECTOR :
public class SwipeDetector implements View.OnTouchListener {
public static enum Action {
LR, // Left to Right
RL, // Right to Left
TB, // Top to bottom
BT, // Bottom to Top
None // when no action was detected
}
private static final String logTag = "SwipeDetector";
private static final int MIN_DISTANCE = 100;
private static final int VERTICAL_MIN_DISTANCE = 80;
private static final int HORIZONTAL_MIN_DISTANCE = 80;
private float downX, downY, upX, upY;
private Action mSwipeDetected = Action.None;
public boolean swipeDetected() {
return mSwipeDetected != Action.None;
}
public Action getAction() {
return mSwipeDetected;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
downX = event.getX();
downY = event.getY();
mSwipeDetected = Action.None;
return false; // allow other events like Click to be processed
}
case MotionEvent.ACTION_MOVE: {
upX = event.getX();
upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
// horizontal swipe detection
if (Math.abs(deltaX) > HORIZONTAL_MIN_DISTANCE) {
// left or right
if (deltaX < 0) {
// Log.i(logTag, "Swipe Left to Right");
mSwipeDetected = Action.LR;
return true;
}
if (deltaX > 0) {
// Log.i(logTag, "Swipe Right to Left");
mSwipeDetected = Action.RL;
return true;
}
} else
// vertical swipe detection
if (Math.abs(deltaY) > VERTICAL_MIN_DISTANCE) {
// top or down
if (deltaY < 0) {
Log.i(logTag, "Swipe Top to Bottom");
mSwipeDetected = Action.TB;
return false;
}
if (deltaY > 0) {
Log.i(logTag, "Swipe Bottom to Top");
mSwipeDetected = Action.BT;
return false;
}
}
return true;
}
}
return false;
}}
I use it in the following way :
listView.setOnTouchListener(swipeDetector);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Log.d("CLICKED", "CLICKED");
if (swipeDetector.swipeDetected()) {
Log.d("SWIPING", "SWIPING");
Log.d("ACTION", swipeDetector.getAction().toString());
final Button del = (Button) view.findViewById(R.id.delete_button);
if (swipeDetector.getAction() == SwipeDetector.Action.LR) {
Log.d("LEFT TO RIGHT", "Left to right");
This works perfectly fine with Activities. However, the problem now is that when I swipe, it assumes I am swiping in the ViewPager and takes me back to the middle fragment. Is there a way to disable the ViewPager swiping on this ListView or change the focus so that this works?

Is it possible to make Imageview with checked and unchecked like radiobutton?

I am having nearly 500+ images in Imageview inside Horizontalscrollview. If i am selecting an image then I am marking it as selected. If I am selecting any other images in the view, it should be un-select and newly clicked image should have to be selected. How could I can achieve it?
for (int i = 0; i < Home.arr_category_item_list.size(); i++) {
ImageView circleImageView = new ImageView(getActivity());
imageLoader.get(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_IMAGE), ImageLoader.getImageListener(circleImageView, R.drawable.defaultimage, R.drawable.defaultimage));
circleImageView.setTag(Integer.parseInt(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_ID)));
circleImageView.setLayoutParams(params);
lnr_category_item.addView(circleImageView);
}
Please check the image attached. At the bottom of the screen there is an Image view. So user will have option to select only one image at a time.
Now this is some really old code, hope it still works.
Note: there may be a thing or two missing, but you can get the idea from this implementation
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import yourpackage.R;
//Creataed by Bojan Kseneman on 14.8.2013
public class CustomCheckBox extends ImageView {
private boolean isChecked;
private boolean isImageShown;
private boolean useCustomClickListener;
//private String android_xmlns = "http://schemas.android.com/apk/res/android";
private String app_xmlns;
private int checkboxOnResID;
private int checkboxOffResID;
private int checkboxDisabledOnResID;
private int checkboxDisabledOffResID;
//private int imageHeight;
//private int imageWidth;
public CustomCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
app_xmlns = new StringBuilder("http://schemas.android.com/apk/res/" + context.getPackageName()).toString();
init(attrs);
}
private void init(AttributeSet attrs) {
checkboxOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceChecked", R.drawable.round_checkbox_on);
checkboxOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceNotChecked", R.drawable.round_checkbox_off);
checkboxDisabledOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOn", R.drawable.round_checkbox_off);
checkboxDisabledOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOff", R.drawable.round_checkbox_off);
useCustomClickListener = attrs.getAttributeBooleanValue(app_xmlns, "customClickEvent", false);
if (useCustomClickListener)
this.setOnTouchListener(new CboxTouchListener());
else {
this.setOnTouchListener(new NormalClickListener());
}
if (!hasOnClickListener()) {
/**
* assign a new onClick listener so we get desired onClick sound
* effect (because we call it) this is opposite to how android
* behaves, where you don't hear the sound if there is not
* onClickListener assigned
*/
this.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
private boolean hasOnClickListener() {
try {
if (android.os.Build.VERSION.SDK_INT >= 14) {
//the information is inside ListenerInfo
java.lang.reflect.Field listenerInfoField = null;
listenerInfoField = Class.forName("android.view.View").getDeclaredField("mListenerInfo");
if (listenerInfoField != null)
listenerInfoField.setAccessible(true);
Object mOnClickListener = null;
mOnClickListener = listenerInfoField.get(this); //get from view object, in this case this is this
// get the field mOnClickListener, that holds the listener and cast it to a listener
java.lang.reflect.Field listenerField = null;
listenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener");
//View.OnClickListener myListener = (View.OnClickListener) listenerField.get(myLiObject);
return (listenerField.get(mOnClickListener) != null);
}
else {
//directly in View
java.lang.reflect.Field f = Class.forName("android.view.View").getDeclaredField("mOnClickListener");
return (f.get(this) != null);
}
}
catch (Exception e) {
return false;
}
}
// private void setScaledDownImage(int resID) {
// this.setImageBitmap(CommonMethods.decodeSampledBitmapFromResource(getContext(), resID, imageWidth, imageHeight));
// }
public boolean isChecked() {
return (isChecked && this.isEnabled());
}
public void setChecked(boolean isChecked) {
if (this.isEnabled())
setCheckedIgnoreEnabled(isChecked);
else
this.setEnabled(false);
}
public void setCheckedIgnoreEnabled(boolean isChecked) {
if ((this.isChecked != isChecked) || !isImageShown) {
this.isChecked = isChecked;
if (isChecked)
setImageResource(checkboxOnResID);
else
setImageResource(checkboxOffResID);
}
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
setChecked(isChecked);
else {
int resID = isChecked ? checkboxDisabledOnResID : checkboxDisabledOffResID;
setImageResource(resID);
}
}
public void setCheckedAndEnabled(boolean isChecked, boolean isEnabled) {
setCheckedIgnoreEnabled(isChecked);
setEnabled(isEnabled);
}
public void toggle() {
setChecked(!isChecked);
}
public void toggleWithClick() {
toggle();
this.performClick();
}
public void toggleWithSilentClick() {
//v.playSoundEffect(android.view.SoundEffectConstants.CLICK);
boolean currentState = this.isSoundEffectsEnabled();
this.setSoundEffectsEnabled(false);
toggleWithClick();
this.setSoundEffectsEnabled(currentState);
}
private class CboxTouchListener extends ImageBoundClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
private class NormalClickListener extends MyOnClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//this.imageWidth = MeasureSpec.getSize(widthMeasureSpec);
//this.imageHeight = MeasureSpec.getSize(heightMeasureSpec);
if (!isImageShown) {
setChecked(isChecked);
isImageShown = !isImageShown;
}
}
}
I used this click listener, since I was using some round images and I didn't want to trigger the click events when the user clicked on the transparent part of the image.
public abstract class ImageBoundClickListener implements android.view.View.OnTouchListener {
private String TAG = "ImageBoundsTouchListener";
private boolean shouldTriggerAction = false;
Rect allowedArea;
#Override
public boolean onTouch(View v, MotionEvent event) {
//let's us detect only clicks on the part where actual image is and not where the ImageView is
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float[] eventXY = new float[] { event.getX(), event.getY() };
allowedArea = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
android.graphics.Matrix invertMatrix = new android.graphics.Matrix();
ImageView iv = (ImageView) v;
iv.getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = (int) eventXY[0];
int y = (int) eventXY[1];
Drawable imgDrawable = iv.getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
//Limit x,y within the bitmap
if (x < 0)
x = 0;
else if (x > (bitmap.getWidth() - 1))
x = bitmap.getWidth() - 1;
if (y < 0)
y = 0;
else if (y > bitmap.getHeight() - 1)
y = bitmap.getHeight() - 1;
int touchedRGB = bitmap.getPixel(x, y);
//is transparent?
shouldTriggerAction = (touchedRGB == 0) ? false : true;
return true;
case MotionEvent.ACTION_MOVE:
if (!allowedArea.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY()))
//the user went out of the user area
shouldTriggerAction = false;
return true;
case MotionEvent.ACTION_UP:
//finger is no longer on screen
if (shouldTriggerAction)
doSomething();
return true;
default:
return false;
}
}
public void doSomething() {
}
}

Categories