Xamarin Forms Android Autosize Label TextCompat pre android 8 不自动调整文本大小 [英] Xamarin Forms Android Autosize Label TextCompat pre android 8 doesn't autosize text

查看:17
本文介绍了Xamarin Forms Android Autosize Label TextCompat pre android 8 不自动调整文本大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的 xamarin 表单解决方案中利用 android textviews 的自动调整大小功能,以便随着文本长度的增加,字体大小会缩小,永远不会溢出标签的边界,并且不会被截断.为此,我创建了一个自定义 Label 控件并添加了一个 android 自定义渲染器.它不适用于 Android 7 及更低版本.它适用于 Android 8 及更高版本.

根据

按预期工作:Android 8 及更高版本

以上图片的 Xaml:

 <Label Text="固定宽度和高度,句子变长,文字应该缩小";/><控件:AutoSizeLabelAutoSizeMaxTextSize=50"AutoSizeMinTextSize=8"AutoSizeStepGranularity=1"BackgroundColor="{StaticResource Shamrock}";高度请求=40"Horizo​​ntalOptions=开始"MaxLines=1"文本=一个小句子"WidthRequest=200"/><控件:AutoSizeLabelAutoSizeMaxTextSize=50"AutoSizeMinTextSize=8"AutoSizeStepGranularity=1"BackgroundColor="{StaticResource Shamrock}";高度请求=40"Horizo​​ntalOptions=开始"MaxLines=1"文本=缩小的更大的句子"WidthRequest=200"/><控件:AutoSizeLabelAutoSizeMaxTextSize=50"AutoSizeMinTextSize=8"AutoSizeStepGranularity=1"BackgroundColor="{StaticResource Shamrock}";高度请求=40"Horizo​​ntalOptions=开始"MaxLines=1"文本=一个更大的句子,缩小得更多."WidthRequest=200"/></StackLayout>

解决方案

Leo Zhu 的回答 帮了我大忙那里.我需要采取一些额外的步骤才能使其完全正常工作,因此我将代码作为单独的答案发布在这里.

我的答案和 Leo 的答案之间的差异:

  1. 像 Leo 建议的那样在范围内创建一个新的本机控件意味着它可以工作一段时间,但被垃圾收集器处理掉,并在导航离开后返回页面时导致异常.为了解决这个问题,我需要覆盖一个名为 ManageNativeControlLifetime 的属性以返回 false,然后通过覆盖 dispose 方法并调用 Control.RemoveFromParent(); 来手动管理对象的处置.此建议来自 此主题 中的一名 xamarin 工作人员.

  2. 在创建新的原生控件时不会自动继承格式和绑定上下文,需要手动设置.我需要使用 android 特定的绑定语法根据我的需要添加那些.您可能需要根据您的需要添加其他格式和绑定代码,我在这里只做字体颜色、重力和绑定上下文.

我设置了绑定上下文appCompatTextView.SetBindingContext(autoLabel.BindingContext);

  1. 设置绑定上下文后,我需要向我的 XF AutoSizeLabel 类添加一个新的字符串属性以通过 XAML 传入,然后使用它来设置相关属性的绑定路径(在我的情况下是文本财产).如果需要多个绑定,则需要为每个必需的属性添加多个新的绑定路径属性.我设置了这样的特定绑定:

    appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));

    为了在我的 Xamarin Forms Xaml 中实现这一点,我的 Xaml 来自 <Label Text="{Binding MyViewModelPropertyName}"/>

这是渲染器的完整代码:

 protected override bool ManageNativeControlLifetime =>错误的;受保护的覆盖无效处置(布尔处置){Control.RemoveFromParent();base.Dispose(处置);}私有 AppCompatTextView appCompatTextView;protected override void OnElementChanged(ElementChangedEventArgs

I want to utilise the auto-sizing feature of android textviews in my xamarin forms solution so that as the text length grows, the font sizes shrinks to never overflow the bounds of the label, and doesn't get truncated. I've created a custom Label control to do so and added an android custom renderer. It's not working in Android 7 and below. It is working in Android 8 and above.

According to the docs autosize support was introduced in android 8, but can be supported back to Android 4 with AppCompat.v4. However, my custom rendered label just renders the default font size in Android pre 8. It works fine in 8+ devices, the label text resizes as needed to not overflow the bounds. The accepted answer to this question with a similar issue on native android says it can be to do with not setting a width and height, I've tried setting widthrequest and heightrequest explicitly and it doesn't change anything. Also setting maxlines=1 doesn't change anything. An alternative thread suggests that custom fonts are the culprit. I created a vanilla forms solution using the default device font, and get the same effect.

My code:

internal class AutosizeLabelRenderer : LabelRenderer
{
    #region constructor

    public AutosizeLabelRenderer(Context context) : base(context)
    {
    }

    #endregion

    #region overridable

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

        if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }

        TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize,
                autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
        
    }

    #endregion
}

public class AutoSizeLabel : Label
{
    public int AutoSizeMaxTextSize
    {
        get => (int)GetValue(AutoSizeMaxTextSizeProperty);
        set => SetValue(AutoSizeMaxTextSizeProperty, value);
    }

    public static readonly BindableProperty AutoSizeMaxTextSizeProperty = BindableProperty.Create(
        nameof(AutoSizeMaxTextSize),        // the name of the bindable property
        typeof(int),     // the bindable property type
        typeof(AutoSizeLabel));      // the default value for the property

    public int AutoSizeMinTextSize
    {
        get => (int)GetValue(AutoSizeMinTextSizeProperty);
        set => SetValue(AutoSizeMinTextSizeProperty, value);
    }

    public static readonly BindableProperty AutoSizeMinTextSizeProperty = BindableProperty.Create(
        nameof(AutoSizeMinTextSize),        // the name of the bindable property
        typeof(int),     // the bindable property type
        typeof(AutoSizeLabel));      // the default value for the property


    public int AutoSizeStepGranularity
    {
        get => (int)GetValue(AutoSizeStepGranularityProperty);
        set => SetValue(AutoSizeStepGranularityProperty, value);
    }

    public static readonly BindableProperty AutoSizeStepGranularityProperty = BindableProperty.Create(
        nameof(AutoSizeStepGranularity),        // the name of the bindable property
        typeof(int),     // the bindable property type
        typeof(AutoSizeLabel));      // the default value for the property

    //
}

Not working: Android 7 - text does not shrink

Working as expected: Android 8 and above

Xaml for above images:

        <StackLayout HeightRequest="200" WidthRequest="100">
            <Label Text="Fixed width and height, sentences get longer, text should shrink" />
            <controls:AutoSizeLabel
                AutoSizeMaxTextSize="50"
                AutoSizeMinTextSize="8"
                AutoSizeStepGranularity="1"
                BackgroundColor="{StaticResource Shamrock}"
                HeightRequest="40"
                HorizontalOptions="Start"
                MaxLines="1"
                Text="A small sentence"
                WidthRequest="200" />
            <controls:AutoSizeLabel
                AutoSizeMaxTextSize="50"
                AutoSizeMinTextSize="8"
                AutoSizeStepGranularity="1"
                BackgroundColor="{StaticResource Shamrock}"
                HeightRequest="40"
                HorizontalOptions="Start"
                MaxLines="1"
                Text="A larger sentence that shrinks"
                WidthRequest="200" />
            <controls:AutoSizeLabel
                AutoSizeMaxTextSize="50"
                AutoSizeMinTextSize="8"
                AutoSizeStepGranularity="1"
                BackgroundColor="{StaticResource Shamrock}"
                HeightRequest="40"
                HorizontalOptions="Start"
                MaxLines="1"
                Text="An even larger sentence that shrinks more."
                WidthRequest="200" />
        </StackLayout>

解决方案

Leo Zhu's answer got me most of the way there. There were a couple of extra steps I needed to take to get it fully working, so I'm posting the code as a separate answer here.

Differences between mine and Leo's answer:

  1. Creating a new native control in scope like Leo suggested meant that it worked for a while but got disposed by the garbage collector and caused an exception when returning to the page after navigating away. To fix this I needed to override a property called ManageNativeControlLifetime to return false, and then manually manage disposing the object by overriding the dispose method and calling Control.RemoveFromParent();. This advice comes from a xamarin staff member in this thread.

  2. Formatting and binding context are not automatically inherited when creating the new native control and need to be set manually. I needed to add those based on my needs using the android specific binding syntax. You may need to add other formatting and binding code based on your needs, I'm just doing font colour, gravity and binding context here.

I set the binding context with appCompatTextView.SetBindingContext(autoLabel.BindingContext);

  1. Once the binding context was set, I needed to add a new string property to my XF AutoSizeLabel class to pass in through XAML, then use it to set the binding path for the relevant property (In my case the text property). If more than one binding is required, you would need to add multiple new binding path properties for each required property. I set a specific binding like this:

    appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));

    To facilitate this in my Xamarin Forms Xaml, my Xaml went from <Label Text="{Binding MyViewModelPropertyName}" /> to <controls:AutoSizeLabel TextBindingPath="MyViewModelPropertyName" />

Here's the full code of the renderer:

    protected override bool ManageNativeControlLifetime => false;

    protected override void Dispose(bool disposing)
    {
        Control.RemoveFromParent();
        base.Dispose(disposing);
    }

    private AppCompatTextView appCompatTextView;

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

        if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }

        //v8 and above supported natively, no need for the extra stuff below.
        if (DeviceInfo.Version.Major >= 8)
        {
            Control?.SetAutoSizeTextTypeUniformWithConfiguration(
                autoLabel.AutoSizeMinTextSize,
                autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity,
                (int)ComplexUnitType.Sp);
            return;
        }

            appCompatTextView = new AppCompatTextView(Context);
            appCompatTextView.SetTextColor(Element.TextColor.ToAndroid());
            appCompatTextView.SetMaxLines(1);
            appCompatTextView.Gravity = GravityFlags.Center;
            appCompatTextView.SetBindingContext(autoLabel.BindingContext);
            appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));
            SetNativeControl(appCompatTextView);
        

        TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize, autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
    }

这篇关于Xamarin Forms Android Autosize Label TextCompat pre android 8 不自动调整文本大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆