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

在Xamarin.Forms中构建内嵌图片的多行文本内容

Xamarin.Forms 内嵌图片的图文混排实现方案

要实现文本内嵌图片且换行后自动环绕的效果,StackLayout和Grid这类布局控件无法满足需求,得借助原生平台的富文本能力,通过自定义渲染器来实现。以下是完整实现步骤:

1. 创建跨平台自定义控件

先定义一个继承自Label的自定义控件,用于绑定图片和标记图片在文本中的位置:

using Xamarin.Forms;

namespace YourAppName.Controls
{
    public class FlowLabel : Label
    {
        // 绑定图片源
        public static readonly BindableProperty ImageSourceProperty =
            BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(FlowLabel));

        // 图片尺寸
        public static readonly BindableProperty ImageSizeProperty =
            BindableProperty.Create(nameof(ImageSize), typeof(Size), typeof(FlowLabel), new Size(30, 30));

        // 文本中标记图片位置的占位符,比如"[inline-image]"
        public static readonly BindableProperty ImagePlaceholderProperty =
            BindableProperty.Create(nameof(ImagePlaceholder), typeof(string), typeof(FlowLabel), "[inline-image]");

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

        public Size ImageSize
        {
            get => (Size)GetValue(ImageSizeProperty);
            set => SetValue(ImageSizeProperty, value);
        }

        public string ImagePlaceholder
        {
            get => (string)GetValue(ImagePlaceholderProperty);
            set => SetValue(ImagePlaceholderProperty, value);
        }
    }
}

2. iOS平台自定义渲染器

利用iOS的NSAttributedString和NSTextAttachment实现图文混排:

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

[assembly: ExportRenderer(typeof(FlowLabel), typeof(FlowLabelRenderer))]
namespace YourAppName.iOS.Renderers
{
    public class FlowLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element == null) return;

            var flowLabel = Element as FlowLabel;
            if (flowLabel?.ImageSource == null) return;

            // 加载图片
            var imageLoader = new ImageLoaderSourceHandler();
            var uiImageTask = imageLoader.LoadImageAsync(flowLabel.ImageSource, this);
            uiImageTask.ContinueWith(task =>
            {
                if (task.IsCompletedSuccessfully && task.Result != null)
                {
                    InvokeOnMainThread(() => UpdateAttributedText(flowLabel, task.Result));
                }
            });
        }

        private void UpdateAttributedText(FlowLabel flowLabel, UIImage uiImage)
        {
            var originalText = flowLabel.Text ?? string.Empty;
            if (!originalText.Contains(flowLabel.ImagePlaceholder)) return;

            // 缩放图片到指定尺寸
            var scaledImage = uiImage.Scale(new CoreGraphics.CGSize((nfloat)flowLabel.ImageSize.Width, (nfloat)flowLabel.ImageSize.Height));

            // 创建文本附件
            var textAttachment = new NSTextAttachment();
            textAttachment.Image = scaledImage;
            textAttachment.Bounds = new CoreGraphics.CGRect(0, -2, scaledImage.Size.Width, scaledImage.Size.Height); // 微调垂直对齐

            // 将占位符替换为图片附件
            var finalAttributedString = new NSMutableAttributedString();
            var parts = originalText.Split(new[] { flowLabel.ImagePlaceholder }, StringSplitOptions.None);
            for (int i = 0; i < parts.Length; i++)
            {
                finalAttributedString.Append(new NSAttributedString(parts[i]));
                if (i < parts.Length - 1)
                {
                    finalAttributedString.Append(NSAttributedString.FromAttachment(textAttachment));
                }
            }

            // 应用富文本到Label
            Control.AttributedText = finalAttributedString;
        }
    }
}

3. Android平台自定义渲染器

利用Android的SpannableString和ImageSpan实现文本环绕图片:

using System;
using Android.Graphics;
using Android.Text;
using Android.Text.Style;
using Android.Widget;
using YourAppName.Controls;
using YourAppName.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(FlowLabel), typeof(FlowLabelRenderer))]
namespace YourAppName.Droid.Renderers
{
    public class FlowLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element == null) return;

            var flowLabel = Element as FlowLabel;
            if (flowLabel?.ImageSource == null) return;

            // 加载图片
            var imageLoader = new ImageLoaderSourceHandler();
            var bitmapTask = imageLoader.LoadImageAsync(flowLabel.ImageSource, this);
            bitmapTask.ContinueWith(task =>
            {
                if (task.IsCompletedSuccessfully && task.Result != null)
                {
                    InvokeOnMainThread(() => UpdateSpannableText(flowLabel, task.Result));
                }
            });
        }

        private void UpdateSpannableText(FlowLabel flowLabel, Bitmap bitmap)
        {
            var originalText = flowLabel.Text ?? string.Empty;
            if (!originalText.Contains(flowLabel.ImagePlaceholder)) return;

            // 缩放图片到指定尺寸
            var scaledBitmap = Bitmap.CreateScaledBitmap(bitmap, (int)flowLabel.ImageSize.Width, (int)flowLabel.ImageSize.Height, true);

            // 创建图片Span,设置对齐方式为基线对齐
            var imageSpan = new ImageSpan(scaledBitmap, SpanAlignBaseline);

            // 替换占位符
            var placeholderIndex = originalText.IndexOf(flowLabel.ImagePlaceholder, StringComparison.Ordinal);
            var spannableString = new SpannableString(originalText.Replace(flowLabel.ImagePlaceholder, " "));
            spannableString.SetSpan(imageSpan, placeholderIndex, placeholderIndex + 1, SpanTypes.ExclusiveExclusive);

            // 应用富文本到TextView
            Control.SetText(spannableString, TextView.BufferType.Spannable);
        }
    }
}

4. 在XAML中使用自定义控件

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:YourAppName.Controls"
             x:Class="YourAppName.MainPage">

    <StackLayout Padding="20">
        <controls:FlowLabel 
            Text="这是一段需要内嵌图片的文本[inline-image],文本过长换行后会自动环绕在图片周围,实现类似Word中的图文混排效果,再也不用纠结StackLayout和Grid的局限了。"
            ImageSource="your_image.png"
            ImageSize="40,40"
            FontSize="16"
            LineBreakMode="WordWrap"/>
    </StackLayout>
</ContentPage>

注意事项

  • 确保图片文件已添加到各平台资源目录(iOS的Resources、Android的Drawable),并设置正确的生成操作。
  • 可根据需求调整图片的垂直偏移(iOS的textAttachment.Bounds、Android的Span对齐方式),优化排版效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 10:43:20