简体   繁体   中英

How to superimpose text on an image in a Xamarin Forms Button?

I was not able to find an easy way to create a Xamarin Forms button as an image with superimposed text on top. Curiously the Image property of Button only allows text next to the image.

So I'll share my code implementation here. It includes a facility to hide the keyboard when the button is tapped and supports text properties, image sizing and enablement. Grateful to @F. Badili, @Sharada Gururaj and others for assistance.

My implementation:

public class ImageButton : Grid
// A clickable visual element overlaying a label on an image, serving as a button
{
    public event EventHandler Clicked;

    public Image TheImage { get; set; }
    public Label TheLabel { get; set; }
    public bool HideKeyboard { get; set; }
    public string Text
    { 
        get
        {
            if ( TheLabel == null )
                return null;
            return TheLabel.Text;
        }

        set
        {
            if ( TheLabel == null )
                return;
            TheLabel.Text = value;
        }
    }

    public Color TextColor 
    {
        set
        {
            if ( TheLabel != null )
                TheLabel.TextColor = value;
        }
    }
    public string FontFamily 
    {
        set 
        {
            if ( TheLabel != null )
                TheLabel.FontFamily = value;
        }
    }

    public FontAttributes FontAttributes 
    {
        set 
        {
            if ( TheLabel != null )
                TheLabel.FontAttributes = value;
        }
    }

    public double FontSize 
    {
        set 
        {
            if ( TheLabel != null )
                TheLabel.FontSize = value;
        }
    }

    public double ImageHeightRequest
    {
        set
        {
            if ( TheImage != null )
                TheImage.HeightRequest = value;
        }
    }

    public double ImageWidthRequest
    {
        set
        {
            if ( TheImage != null )
                TheImage.WidthRequest = value;    
        }
    }

    public ImageButton ( Image image, Label label, bool hideKeyboard = true )
    {
        TheImage = image;
        TheLabel = label;
        HideKeyboard = hideKeyboard;
        Text = label.Text;

        // Construct the control
        ConstructControl ();
    }

    // Invoke the Clicked event; called when ImageButton is tapped
    protected virtual void OnClicked ( EventArgs e )
    {
        if ( Clicked != null )
            Clicked ( this, e );
    }

    private void ConstructControl ()
    // Construct the control using a grid with a single cell
    {
        // Add action performed upon tap
        this.AddTap ( () => 
        {
            // Hide keyboard
            if ( HideKeyboard )
                DependencyService.Get<IKeyboard> ().HideKeyboard ();

            // Fire the event 
            OnClicked ( EventArgs.Empty );
        });

        // Bind opacity of image and label to IsEnabled via a converter
        TheLabel.SetBinding ( OpacityProperty, new Binding ( "IsEnabled", converter: new BooleanToOpacityConverter (), source: this ) );
        TheImage.SetBinding ( OpacityProperty, new Binding ( "IsEnabled", converter: new BooleanToOpacityConverter (), source: this ) );

        // Stack image and label on top of each other in a grid with a single cell
        this.RowDefinitions.Add ( new RowDefinition { Height = new GridLength ( 1, GridUnitType.Auto ) } );
        this.ColumnDefinitions.Add ( new ColumnDefinition { Width = new GridLength ( 1, GridUnitType.Auto ) } );
        this.Children.Add ( TheImage, 0, 0 );   // Add image first, so overlaid label will be visible
        this.Children.Add ( TheLabel, 0, 0 );
    }

    public class BooleanToOpacityConverter : IValueConverter
    {
        public object Convert ( object value, Type targetType, object parameter, CultureInfo culture )
        {
            var isEnabled = ( value != null ) && (bool)value;
            return isEnabled ? 1 : 0.5;
        }

        public object ConvertBack ( object value, Type targetType, object parameter, CultureInfo culture )
        {
            throw new NotImplementedException ();
        }
    }
}

public static class GridExtensions
{
    // Grid extension methods

    private static void AddTap ( this Grid grid, Action action )
    // Allows Grid to be tappable
    // action = method to call when Grid is tapped
    // Example:
    //          Grid grid = new Grid ();
    //          grid.AddTap ( () => MyMethod () );
    {
        grid.GestureRecognizers.Add ( new TapGestureRecognizer 
        {
            Command = new Command (action)
        });
    }
}

public interface IKeyboard
{
    void HideKeyboard ();       // Hide the keyboard
}

iOS:

[assembly: Xamarin.Forms.Dependency (typeof(Keyboard))]
namespace MyApp.iOS
{
    public class Keyboard : MyApp.IKeyboard
    {
        public Keyboard () {}

        public void HideKeyboard ()
        // Hides the keyboard
        {
            UIApplication.SharedApplication.KeyWindow.EndEditing ( true );
        }
    }
}

Android (not tested yet):

[assembly: Xamarin.Forms.Dependency (typeof (Keyboard))]
namespace MyApp.Droid
{
    public class Keyboard : MyApp.IKeyboard
    {
        public Keyboard () {}

        public void HideKeyboard ()
        // Hides the keyboard
        {
            var context = Forms.Context;
            var inputMethodManager = context.GetSystemService ( Context.InputMethodService ) as InputMethodManager;
            if ( inputMethodManager != null && context is Activity ) 
            {
                var activity = context as Activity;
                var token = activity.CurrentFocus?.WindowToken;
                inputMethodManager.HideSoftInputFromWindow ( token, HideSoftInputFlags.None );

                activity.Window.DecorView.ClearFocus ();
            }
        }
    }
}    

Usage example:

public static ImageButton StandardImageButton ( string buttonText, EventHandler onClickedEventHandler = null, string imageResourceID = "MyApp.standardbutton.png", string imageSource = null, 
                                              Color textColour = default(Color), string fontFamily = null, double fontSize = 15D, FontAttributes fontAttributes = FontAttributes.Bold, 
                                              double heightRequest = 44D, double widthRequest = 200D, bool hideKeyboard = true )
    // Returns tappable image serving as a button
    // buttonText = text overlaid onbutton
    // Image is sourced from embedded PCL file (Build Action = Embedded Resource) OR, if imageSourceID is non-null, from local platform file in platform-specific location
    {
        // Background image
        Image image = new Image 
        { 
            Aspect = Aspect.Fill,           // Stretch to fill
            HeightRequest = heightRequest,
            WidthRequest = widthRequest,
            HorizontalOptions = LayoutOptions.CenterAndExpand,
            VerticalOptions = LayoutOptions.CenterAndExpand
        };

        if ( imageResourceID == null )
            image.Source = ImageSource.FromFile ( imageSource );
        else
            image.Source = ImageSource.FromResource ( imageResourceID );

        // Foreground label
        Color colour;
        if ( textColour == default(Color) )
            colour = Color.Black;
        else
            colour = textColour;

        Label label = new Label 
        {
            Text = buttonText,
            TextColor = colour,
            FontFamily = fontFamily,
            FontSize = fontSize,
            FontAttributes = fontAttributes,
            HorizontalTextAlignment = TextAlignment.Center,
            VerticalTextAlignment = TextAlignment.Center
        };

        ImageButton ret = new ImageButton ( image, label, hideKeyboard );
        ret.Clicked += onClickedEventHandler;
        return ret;
    }

If you assign btn1.Image in code or xaml you could do this

btn1.ContentLayout = new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Bottom, 10);

That will give you a button with image at the bottom and text on top

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM