You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms实现带前置图标及密码显隐图标的Entry

Absolutely! You can definitely have both a leading icon and the trailing password toggle on your custom Entry—this is totally achievable. The issue you're running into is most likely because the separate effects/renderers you're using are overriding each other's changes instead of working in tandem. Let's walk through how to fix this by creating a unified custom Entry control that supports both features.

Solution: Unified Custom Entry with Leading Icon + Password Toggle

The best approach is to build a single custom Entry control in your shared code, then implement platform-specific renderers that handle both icons at once. This avoids conflicts between separate effects.

1. Define the Custom Entry in Shared Code

First, create a reusable Entry subclass that exposes properties for both the leading icon and password toggle functionality:

using Xamarin.Forms;

namespace YourAppNamespace.Controls
{
    public class CustomPasswordEntry : Entry
    {
        // Property for leading icon
        public static readonly BindableProperty LeadingIconProperty =
            BindableProperty.Create(nameof(LeadingIcon), typeof(ImageSource), typeof(CustomPasswordEntry));

        // Property to enable password toggle
        public static readonly BindableProperty IsPasswordToggleEnabledProperty =
            BindableProperty.Create(nameof(IsPasswordToggleEnabled), typeof(bool), typeof(CustomPasswordEntry), false);

        public ImageSource LeadingIcon
        {
            get => (ImageSource)GetValue(LeadingIconProperty);
            set => SetValue(LeadingIconProperty, value);
        }

        public bool IsPasswordToggleEnabled
        {
            get => (bool)GetValue(IsPasswordToggleEnabledProperty);
            set => SetValue(IsPasswordToggleEnabledProperty, value);
        }
    }
}

2. Android Renderer (Handle Both Icons)

On Android, we'll use CompoundDrawables to add both the leading (left) and trailing (right) icons, plus add a touch listener to the trailing icon for the password toggle:

using Android.Content;
using Android.Graphics.Drawables;
using Android.Views;
using YourAppNamespace.Controls;
using YourAppNamespace.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomPasswordEntry), typeof(CustomPasswordEntryRenderer))]
namespace YourAppNamespace.Droid.Renderers
{
    public class CustomPasswordEntryRenderer : EntryRenderer
    {
        public CustomPasswordEntryRenderer(Context context) : base(context) { }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element is not CustomPasswordEntry customEntry) return;

            // Setup leading icon
            Drawable leadingDrawable = null;
            if (customEntry.LeadingIcon != null)
            {
                var handler = new ImageSourceHandler();
                leadingDrawable = handler.LoadImageAsync(customEntry.LeadingIcon, Context).Result;
                leadingDrawable.SetBounds(0, 0, leadingDrawable.IntrinsicWidth, leadingDrawable.IntrinsicHeight);
            }

            // Setup password toggle icon
            Drawable trailingDrawable = null;
            if (customEntry.IsPasswordToggleEnabled)
            {
                // Load initial toggle icon based on IsPassword state
                trailingDrawable = Resources.GetDrawable(customEntry.IsPassword ? Resource.Drawable.ic_eye_off : Resource.Drawable.ic_eye);
                trailingDrawable.SetBounds(0, 0, trailingDrawable.IntrinsicWidth, trailingDrawable.IntrinsicHeight);

                // Add touch listener to detect taps on the trailing icon
                Control.Touch += (s, args) =>
                {
                    if (args.Action != MotionEventActions.Up) return;

                    var entryRect = new Rect();
                    Control.GetGlobalVisibleRect(entryRect);
                    // Check if tap is within the trailing icon area
                    if (args.RawX >= entryRect.Right - trailingDrawable.IntrinsicWidth - Control.PaddingRight)
                    {
                        customEntry.IsPassword = !customEntry.IsPassword;
                        // Update the toggle icon
                        trailingDrawable = Resources.GetDrawable(customEntry.IsPassword ? Resource.Drawable.ic_eye_off : Resource.Drawable.ic_eye);
                        trailingDrawable.SetBounds(0, 0, trailingDrawable.IntrinsicWidth, trailingDrawable.IntrinsicHeight);
                        Control.SetCompoundDrawables(leadingDrawable, null, trailingDrawable, null);
                        args.Handled = true;
                    }
                };
            }

            // Set both drawables and adjust padding
            Control.SetCompoundDrawables(leadingDrawable, null, trailingDrawable, null);
            Control.CompoundDrawablePadding = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, 12, Resources.DisplayMetrics);
        }
    }
}

3. iOS Renderer (Handle Both Icons)

On iOS, we'll use LeftView for the leading icon and RightView for the password toggle, then enable both views and add a tap gesture to the toggle:

using System;
using UIKit;
using YourAppNamespace.Controls;
using YourAppNamespace.iOS.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CustomPasswordEntry), typeof(CustomPasswordEntryRenderer))]
namespace YourAppNamespace.iOS.Renderers
{
    public class CustomPasswordEntryRenderer : EntryRenderer
    {
        private UIImageView _passwordToggleView;

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element is not CustomPasswordEntry customEntry) return;

            // Setup leading icon
            if (customEntry.LeadingIcon != null)
            {
                var leadingImageView = new UIImageView
                {
                    ContentMode = UIViewContentMode.ScaleAspectFit
                };

                // Load the leading icon image
                ImageSource.FromUri(new Uri(customEntry.LeadingIcon.ToString())).LoadImageAsync((image, error) =>
                {
                    if (image != null)
                    {
                        leadingImageView.Image = image;
                        leadingImageView.Frame = new CoreGraphics.CGRect(0, 0, 20, 20);
                        Control.LeftView = leadingImageView;
                        Control.LeftViewMode = UITextFieldViewMode.Always;
                        Control.LeftView.Frame = new CoreGraphics.CGRect(0, 0, 30, Control.Frame.Height);
                    }
                });
            }

            // Setup password toggle icon
            if (customEntry.IsPasswordToggleEnabled)
            {
                _passwordToggleView = new UIImageView
                {
                    Image = UIImage.FromBundle(customEntry.IsPassword ? "ic_eye_off" : "ic_eye"),
                    ContentMode = UIViewContentMode.ScaleAspectFit,
                    UserInteractionEnabled = true
                };

                // Add tap gesture for toggle
                var tapGesture = new UITapGestureRecognizer(() =>
                {
                    customEntry.IsPassword = !customEntry.IsPassword;
                    _passwordToggleView.Image = UIImage.FromBundle(customEntry.IsPassword ? "ic_eye_off" : "ic_eye");
                });
                _passwordToggleView.AddGestureRecognizer(tapGesture);

                Control.RightView = _passwordToggleView;
                Control.RightViewMode = UITextFieldViewMode.Always;
                Control.RightView.Frame = new CoreGraphics.CGRect(0, 0, 30, Control.Frame.Height);
            }

            // Adjust padding to prevent text from overlapping icons
            Control.LeftViewPadding = new UIEdgeInsets(0, 8, 0, 8);
            Control.RightViewPadding = new UIEdgeInsets(0, 8, 0, 8);
        }
    }
}

4. Use the Custom Entry in XAML

Now you can use your CustomPasswordEntry in any XAML page, setting both the leading icon and enabling the password toggle:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:YourAppNamespace.Controls"
             x:Class="YourAppNamespace.Views.PasswordPage">
    <StackLayout Padding="20" VerticalOptions="Center">
        <controls:CustomPasswordEntry
            Placeholder="Enter your password"
            IsPassword="True"
            IsPasswordToggleEnabled="True"
            LeadingIcon="lock_icon.png"
            Margin="0 0 0 15"/>
    </StackLayout>
</ContentPage>

Important Notes

  • Make sure your icon assets (lock_icon.png, ic_eye.png, ic_eye_off.png) are properly added to each platform's resource folders:
    • Android: Place drawables in the Resources/drawable (and density-specific folders like drawable-hdpi)
    • iOS: Add images to the Resources folder and set their build action to BundleResource
  • By handling both icons in a single renderer, you avoid conflicts that happen when using separate effects (which often overwrite each other's changes to the Entry's UI elements)
  • Test on both platforms to ensure icons are positioned correctly and the password toggle works as expected

内容的提问来源于stack exchange,提问作者Phill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:17:29