Xamarin - ConvertFormsToNative 函数不是 return Spotlight 中的真正原生元素

Xamarin - ConvertFormsToNative function doesn't return the real native element in Spotlight

我想在我的应用程序中实现 Spotlight 功能,如 Spotlight library is Android specific and I didn't knew how to access platform specific libraries from the core Forms project, @Jason recommended to use DependencyService and @Colex - MSFT helped in its 。有一些小东西没有按预期工作。

我基本上有这 3 个按钮(第一个在左上角,第二个在右上角,第三个在底部中心)。

  1. 线条动画必须从组件(按钮)开始,就像 Spotlight 库中的 Preview 一样,但在我的应用程序中它们是从左上角开始的(所有这些).

  2. 我希望当第一个按钮的聚光灯淡出时,第二个按钮的聚光灯应该淡入,当它淡出时,第三个按钮的聚光灯应该淡入。 但是现在发生的情况是,当第一个按钮的聚光灯淡出时,第二个按钮的聚光灯已经淡入(已经启用),因此没有动画(行从组件(按钮)开始并且文本出现)。

Droid\Control\SpotLightService.cs

[assembly: Xamarin.Forms.Dependency(typeof(SpotLightService))]
namespace CustomRenderer.Droid.Control
{
    public class SpotLightService : ISpotLight
    {
        private bool isRevealEnabled_FirstButton_SpotLight = true;
        private bool isRevealEnabled_SecondButton_SpotLight = true;
        private bool isRevealEnabled_ThirdButton_SpotLight = true;
        private SpotlightView FirstButton_SpotLight;
        private SpotlightView SecondButton_SpotLight;
        private SpotlightView ThirdButton_SpotLight;

        public void ShowSpotLight_FirstButton(Xamarin.Forms.View view, string usageId)
        {
            FirstButton_SpotLight = new SpotlightView.Builder(MainActivity.Instance)
                .IntroAnimationDuration(400)
                .EnableRevealAnimation(isRevealEnabled_FirstButton_SpotLight)
                .PerformClick(true)
                .FadeinTextDuration(400)
                .HeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .HeadingTvSize(32)
                .HeadingTvText("First Button")
                .SubHeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .SubHeadingTvSize(16)
                .SubHeadingTvText("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
                .MaskColor(Android.Graphics.Color.ParseColor("#dc000000"))
                .Target(ConvertFormsToNative(view))
                .LineAnimDuration(400)
                .LineAndArcColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .DismissOnTouch(true)
                .DismissOnBackPress(true)
                .EnableDismissAfterShown(true)
                .UsageId(usageId)
                .ShowTargetArc(true)
                .Show();
        }

        public void ShowSpotLight_SecondButton(Xamarin.Forms.View view, string usageId)
        {
            SecondButton_SpotLight = new SpotlightView.Builder(MainActivity.Instance)
                .IntroAnimationDuration(400)
                .EnableRevealAnimation(isRevealEnabled_SecondButton_SpotLight)
                .PerformClick(true)
                .FadeinTextDuration(400)
                .HeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .HeadingTvSize(32)
                .HeadingTvText("Second Button")
                .SubHeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .SubHeadingTvSize(16)
                .SubHeadingTvText("Sed do eiusmod tempor incididunt ut labore eta")
                .MaskColor(Android.Graphics.Color.ParseColor("#dc000000"))
                .Target(ConvertFormsToNative(view))
                .LineAnimDuration(400)
                .LineAndArcColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .DismissOnTouch(true)
                .DismissOnBackPress(true)
                .EnableDismissAfterShown(true)
                .UsageId(usageId)
                .ShowTargetArc(true)
                .Show();
        }

        public void ShowSpotLight_ThirdButton(Xamarin.Forms.View view, string usageId)
        {
            ThirdButton_SpotLight = new SpotlightView.Builder(MainActivity.Instance)
                .IntroAnimationDuration(400)
                .EnableRevealAnimation(isRevealEnabled_ThirdButton_SpotLight)
                .PerformClick(true)
                .FadeinTextDuration(400)
                .HeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .HeadingTvSize(32)
                .HeadingTvText("Third Button")
                .SubHeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .SubHeadingTvSize(16)
                .SubHeadingTvText("Ut enim ad minim veniam, quis nostrud exercitation")
                .MaskColor(Android.Graphics.Color.ParseColor("#dc000000"))
                .Target(ConvertFormsToNative(view))
                .LineAnimDuration(400)
                .LineAndArcColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .DismissOnTouch(true)
                .DismissOnBackPress(true)
                .EnableDismissAfterShown(true)
                .UsageId(usageId)
                .ShowTargetArc(true)
                .Show();
        }

        public View ConvertFormsToNative(Xamarin.Forms.View view)
        {
            var vRenderer = Platform.CreateRendererWithContext(view, MainActivity.Instance);
            var Androidview = vRenderer.View;
            vRenderer.Tracker.UpdateLayout();

            var size = view.Bounds;
            var layoutParams = new ViewGroup.LayoutParams((int)size.Width, (int)size.Height);
            Androidview.LayoutParameters = layoutParams;
            view.Layout(size);
            Androidview.Layout((int)size.X, (int)size.Y, (int)view.WidthRequest, (int)view.HeightRequest);
            Androidview.SetBackgroundColor(Android.Graphics.Color.Red);
            return Androidview;
        }
    }
}

MainPage.xml.cs

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        LayoutChanged += FirstButton_SpotLight;
        LayoutChanged += SecondButton_SpotLight;
        LayoutChanged += ThirdButton_SpotLight;
    }


    bool isShown_FirstButton_SpotLight = false;
    private void FirstButton_SpotLight(object sender, EventArgs e)
    {
        if (!isShown_FirstButton_SpotLight)
        {
            DependencyService.Get<ISpotLight>().ShowSpotLight_FirstButton(FirstButton, "FirstButton");
            isShown_FirstButton_SpotLight = true;
        }
    }


    bool isShown_SecondButton_SpotLight = false;
    private void SecondButton_SpotLight(object sender, EventArgs e)
    {
        if (!isShown_SecondButton_SpotLight)
        {
            DependencyService.Get<ISpotLight>().ShowSpotLight_SecondButton(SecondButton, "SecondButton");
            isShown_SecondButton_SpotLight = true;
        }
    }

    bool isShown_ThirdButton_SpotLight = false;
    private void ThirdButton_SpotLight(object sender, EventArgs e)
    {
        if (!isShown_ThirdButton_SpotLight)
        {
            DependencyService.Get<ISpotLight>().ShowSpotLight_ThirdButton(ThirdButton, "ThirdButton");
            isShown_ThirdButton_SpotLight = true;
        }
    }
}

正如 json 提到的,我意识到这是真的,函数 ConvertFormsToNative 不是 return 真正的原生元素。

满足您要求的唯一解决方法是

为整个 Page 创建自定义渲染器,并在 native(android) 项目中添加控件。

详情可参考Customizing a ContentPage.

  1. 为页面创建自定义渲染器。
[assembly: ExportRenderer(typeof(Page1), typeof(MainPageRenderer))]
namespace CustomRenderer.Droid
{
    class MainPageRenderer : PageRenderer
    {
        private Android.Views.View view;
        private Android.Widget.Button button1;
        private Android.Widget.Button button2;
        private Android.Widget.Button button3;
        public MainPageRenderer(Context context) : base(context)
        {
        }

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

            if (e.OldElement != null || Element == null)
            {
                return;
            }


            view = MainActivity.Instance.LayoutInflater.Inflate(Resource.Layout.MyPage, this, false);
            AddView(view);


            view.Click += View_Click;

            button1 = view.FindViewById<Android.Widget.Button>(Resource.Id.firstButton);
            button2 = view.FindViewById<Android.Widget.Button>(Resource.Id.secondButton);
            button3 = view.FindViewById<Android.Widget.Button>(Resource.Id.thirdButton);

            
        }

        bool isShown_FirstButton_SpotLight = false;
        bool isShown_SecondButton_SpotLight = false;
        bool isShown_ThirdButton_SpotLight = false;
        private void View_Click(object sender, EventArgs e)
        {
            if (!isShown_FirstButton_SpotLight)
            {
                show(button1 ,"First Button" ,"Lorem ipsum dolor sit amet, consectetur adipiscing elit", "FirstButton");
                isShown_FirstButton_SpotLight = true;
                return;
            }
            if (!isShown_SecondButton_SpotLight)
            {
                show(button2,"Second Button", "Sed do eiusmod tempor incididunt ut labore eta", "SecondButton");
                isShown_SecondButton_SpotLight = true;
                return;
            }
            if (!isShown_ThirdButton_SpotLight)
            {
                show(button3,"Third Button", "Ut enim ad minim veniam, quis nostrud exercitation", "ThirdButton");
                isShown_ThirdButton_SpotLight = true;
                return;
            }
        }

        void show(Android.Views.View view, string title, string subTitle, string usageId)
        {
            new SpotlightView.Builder(MainActivity.Instance)
                .IntroAnimationDuration(400)
                .EnableRevealAnimation(true)
                .PerformClick(true)
                .FadeinTextDuration(400)
                .HeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .HeadingTvSize(32)
                .HeadingTvText(title)
                .SubHeadingTvColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .SubHeadingTvSize(16)
                .SubHeadingTvText(subTitle)
                .MaskColor(Android.Graphics.Color.ParseColor("#dc000000"))
                .Target(view)
                .LineAnimDuration(400)
                .LineAndArcColor(Android.Graphics.Color.ParseColor("#eb273f"))
                .DismissOnTouch(true)
                .DismissOnBackPress(true)
                .EnableDismissAfterShown(true)
                .UsageId(usageId)
                .ShowTargetArc(true)
                .Show();
        }

        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            base.OnLayout(changed, l, t, r, b);

            var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
            var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);

            view.Measure(msw, msh);
            view.Layout(0, 0, r - l, b - t);
        }
    }
}
  1. 创建一个 xml 并将其放在 android 项目的文件夹 Resources/layout 中。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <Button
        android:id="@+id/firstButton"
        android:layout_width="50dp"    
        android:layout_height="50dp"
        android:layout_marginLeft="50dp"    
        android:layout_marginTop="50dp"       
        android:text="First"
        android:background="#00ff00"/>

    <Button
        android:id="@+id/secondButton"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginLeft="100dp"
        android:layout_marginTop="100dp"     
        android:text="Second"
        android:background="#ff0000"/>

    <Button
        android:id="@+id/thirdButton"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginLeft="150dp"
        android:layout_marginTop="150dp"
        android:text="Third"
        android:background="#0000ff"/>
</LinearLayout>

小东西

  • 点击白色space区域依次显示Spotlight

  • 插件只支持圆形聚光灯,你可以把控件做成圆形,勾选here