I have looked at the following:
imagebutton with @null background (transparent)
How to prevent onClick method on transparent portion of a PNG-loaded ImageView
And countless others, so I must be missing it.
I have an ImageButton
. The image represented by the button is a PNG with transparency. Everything displays great. However, when I click the button on the transparency, the click event fires. That is not what I want at all.
I am looking for a solution, and it's probably obvious and I am missing it, where the transparency does not count in the hit test of the button.
I want to do this all in code, not in xml.
So far I am initializing my ImageButton
like this. This is all in Xamarin.Android, but that shouldn't matter. The syntax will be C# instead of Java.
// make the states:
var states = new StateListDrawable();
states.AddState(new int[] { -Android.Resource.Attribute.StateEnabled }, new BitmapDrawable(Context.Resources, disabledImage));
states.AddState(StateSet.WildCard.ToArray(), drawable);
// setup the button
var button = new Android.Widget.ImageButton(Context);
button.SetBackgroundColor(Android.Graphics.Color.Transparent);
button.SetPadding(0, 0, 0, 0);
button.SetImageDrawable(states);
// I am using this in a custom renderer, so these are my
// click handlers. I don't think that matters, but maybe it does?
button.SetOnClickListener(ButtonClickListener.Instance.Value);
button.SetOnTouchListener(ButtonTouchListener.Instance.Value);
button.Tag = this;
Again, everything displays perfectly, the only thing is the click is triggered on the transparent part of the image, which is what I don't want.
EDIT
My Click/Touch Listener Code. This is pulled from the Platform Button Render in Xamarin.Forms
class ButtonClickListener : Object, IOnClickListener
{
public static readonly Lazy<ButtonClickListener> Instance = new Lazy<ButtonClickListener>(() => new ButtonClickListener());
public void OnClick(AView v)
{
var renderer = v.Tag as MyButtonRenderer;
if (renderer != null)
((IButtonController)renderer.Element).SendClicked();
}
}
class ButtonTouchListener : Object, IOnTouchListener
{
public static readonly Lazy<ButtonTouchListener> Instance = new Lazy<ButtonTouchListener>(() => new ButtonTouchListener());
public bool OnTouch(AView v, AMotionEvent e)
{
var renderer = v.Tag as MyButtonRenderer;
if (renderer != null)
{
var buttonController = renderer.Element as IButtonController;
if (e.Action == AMotionEventActions.Down)
{
buttonController?.SendPressed();
}
else if (e.Action == AMotionEventActions.Up)
{
buttonController?.SendReleased();
}
}
return false;
}
}
Things I have tried: in the OnTouchListener
Attempt to get
renderer.Control.DrawingCache
so I could get a Bitmap and test for a transparent pixel. The DrawingCache always returnsnull
. When setting up the button, I alsobutton.DrawingCacheEnabled = true;
If the
DrawingCache
was null in theOnTouchListener
attempt to build it and grab it
For Example:
if (cache == null)
{
renderer.Control.BuildDrawingCache();
cache = renderer.Control.DrawingCache;
}
Still always null.
- Attempt to draw the control to a bitmap to test for the transparent pixel. This was a bad idea generally speaking, but I just wanted to see if it worked.
For Example:
Bitmap bitmap = Bitmap.CreateBitmap(renderer.Control.Width, renderer.Control.Height, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
renderer.Control.Draw(canvas);
In the above cases, once I had a, or thought I had, a Bitmap
representation I would check the pixel like this:
int color = bitmap.GetPixel((int)e.GetX(), (int)e.GetY());
if (color == Android.Graphics.Color.Transparent)
return false;
The above attempts were in service of trying this methodology: https://stackoverflow.com/a/19566795/1060314
Nothing above seemed to work. I may have been on the correct path but just missed something critical along the way.