I created an ImageButton with a selector for pressed and non-pressed states, and this works fine.
But the button has an irregular shape and I only want it clickable
I think you need to modify your collision detection for whether touch is inside your button or not. Use folowing approach.
public boolean OnStartCallButtonTouch(View v, MotionEvent event)
{
Bitmap TheBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.button_start_call_normal);
int eventPadTouch = event.getAction();
int iX = (int) event.getX();
int iY = (int) event.getY();
int[] location = new int[2];
v.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
switch (eventPadTouch) {
case MotionEvent.ACTION_DOWN:
if (iX>=viewX & iY>=viewY & iX<=(viewX+TheBitmap.getWidth()) & iY<=(viewY+TheBitmap.getHeight())) {
if (TheBitmap.getPixel(iX,iY)!=0) {
onStartCallButtonClicked(v);
showPressedState();
return false;
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
showNormalState();
break;
}
return true;
}
private void showNormalState() {
// set your button background drawable to show normal state
}
private void showPressedState() {
// set your button background drawable to show pressed state
}
I think you're actually looking for this:
View.dispatchTouchEvent(...)
You need to override this method in your custom button to return false if point is not in the desired area. I suggest you go about it like this:
public static class MyButton extends ImageButton {
...
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int iX = (int) event.getX();
int iY = (int) event.getY();
// TODO Or use a more sophisticated, pixel value-based condition
if (!(iX>=0 & iY>=0 & iX<TheBitmap.getWidth() & iY<TheBitmap.getHeight())) {
return false;
}
return super.dispatchTouchEvent(event)
}
}
Why not use OnTouchListener: Because you need to call through to View.onTouchEvent() (which is done in super.dispatchTouchEvent(event)) to let View handle drawable states (it's done in onTouchEvent() using View.refreshDrawableState() method, and there's also some more rather complex logic there)
Why not override onTouchEvent(): Because in View.dispatchTouchEvent() there is a condition that if there is an OnTouchListener, then let the listener handle everything and return true. So if later on you set an OnTouchListener to the button for some reason, your coordinate-based condition in onTouchEvent() would not get checked, because onTouchEvent() would never get called. By overriding dispatchTouchEvent() you filter touches at the very top and leave all the other logic in place - you can add any listeners to the button and they will work as they would for normal button, but only when your condition is true.