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 where the underlying rectangular image is non-transparent.
So I implemented an OnTouchListener that checks the touch event's co-ordinates against the Bitmap's pixel values (as described in the first answer here: link). This works, in terms of the logic deciding if the button was pressed, but now the image of the button doesn't change to the pressed image anymore.
Here is what I have:
Selector xml file:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" android:drawable="@drawable/button_start_call_pressed" />
<item android:drawable="@drawable/button_start_call_normal" />
</selector>
Partly transparent ImageButton in the layout:
<ImageButton
android:id="@+id/dashboardStartCallButton"
android:background="@null"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/start_call_button_selector"
/>
In the Activity:
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
...
ImageButton startCallButton = (ImageButton) this.findViewById(R.id.dashboardStartCallButton);
startCallButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return OnStartCallButtonTouch(v,event);
}
});
}
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();
switch (eventPadTouch) {
case MotionEvent.ACTION_DOWN:
if (iX>=0 & iY>=0 & iX<TheBitmap.getWidth() & iY<TheBitmap.getHeight()) {
if (TheBitmap.getPixel(iX,iY)!=0) {
onStartCallButtonClicked(v);
return false;
}
}
}
return true;
}
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:
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.
I think you need to modify your collision detection for whether touch is inside your button or not. Use folowing approach.