扣脚大暖女 發表於 2025-9-25 21:23:00

Avalonia:开发Android应用

<p>我把成功开发Android应用的经过记录下来,在开发过程中,模拟器经常出问题,将Java Development Kit的位置和Android SDK的位置改动一下,就解决了模拟器报错的问题,这是在Github上看到的解决办法。<br>
先建Models文件夹,创建模型ColorItem.cs文件。</p>
<pre><code>using Avalonia.Media;

namespace AvaloniaMobileApp.Models
{
    public class ColorItem
    {
      public Color? Color { get; set; }
      public string? ColorName {get; set; }
    }
}
</code></pre>
<p>再创建ColorItemMessage记录,用于在viewmodel间传递参数。</p>
<pre><code>namespace AvaloniaMobileApp.Models
{
    publicrecord ColorItemMessage(string Sender,ColorItem Item);   
   
}
</code></pre>
<p>先前用ReactiveUI写移动应用,结果闪退了,换用Communitytoolkit.mvvm社区工具,所以将ViewModelBase.cs继承 ObservableRecipient。</p>
<pre><code>using CommunityToolkit.Mvvm.ComponentModel;

namespace AvaloniaMobileApp.ViewModels;

public abstract class ViewModelBase : ObservableRecipient
{
}
</code></pre>
<p>在ViewModels文件夹下先创建要展示的页面ViewModel,ColorsViewModel.cs,AboutViewModel.cs,PalletteViewModel.cs。<br>
ColorsViewModel.cs</p>
<pre><code>using Avalonia.Data.Converters;
using Avalonia.Media;
using AvaloniaMobileApp.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using System;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Threading.Tasks;

namespace AvaloniaMobileApp.ViewModels
{
    public partial class ColorsViewModel : ViewModelBase
    {
      public ObservableCollection&lt;ColorItem&gt; Colors { get; }
      public ColorsViewModel()
      {
            Colors = [];
      }

      
      private ColorItem _selectedColorItem = new();

      partial void OnSelectedColorItemChanged(ColorItem value)
      {
            IsActive = true;

            WeakReferenceMessenger.Default.Send(new ColorItemMessage("colors", value));
      }

      
      private Task Init()
      {
            if (Colors.Count &gt; 0)
            {
                return Task.CompletedTask;
            }
            var properties = typeof(Colors).GetProperties(BindingFlags.Public | BindingFlags.Static);

            foreach (var property in properties)
            {
                if (property.GetValue(null) is Color color)
                {
                  Colors.Add(new ColorItem
                  {
                        Color = color,
                        ColorName = property.Name
                  });
                }
            }
            return Task.CompletedTask;
      }

      public static FuncValueConverter&lt;Color, string&gt; ColorToHex =&gt; new(color =&gt;
      {
            return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
      });

      public static FuncValueConverter&lt;Color, string&gt; ColorToCMYK =&gt; new(value =&gt;
      {
            if (value is Color color)
            {
                double r = color.R / 255.0;
                double g = color.G / 255.0;
                double b = color.B / 255.0;

                double k = 1 - Math.Max(Math.Max(r, g), b);

                double c = k &lt; 1 ? (1 - r - k) / (1 - k) : 0;
                double m = k &lt; 1 ? (1 - g - k) / (1 - k) : 0;
                double y = k &lt; 1 ? (1 - b - k) / (1 - k) : 0;

                return $"CMYK = ({Math.Round(c*100,1)}% {Math.Round(m*100,1)}% {Math.Round(y*100,1)}% {Math.Round(k*100,1)}%)";
            }
            else
            {
                return "";
            }
      });
    }
}
</code></pre>
<p>AboutViewModel.cs</p>
<pre><code>using System.Reflection;

namespace AvaloniaMobileApp.ViewModels
{
    public class AboutViewModel : ViewModelBase
    {
      public string? AppName =&gt; Assembly.GetExecutingAssembly().GetName().Name;

      public string? Version =&gt; Assembly.GetExecutingAssembly().GetName().Version!.ToString();

      public string? Message =&gt; $"该应用使用 Avalonia框架,Avalonia 是跨平台的优秀框架,这是 Android App,使用 Avalonia 基础导航功能,应用 CommunityToolKit.Mvvm 工具开发";
    }
}
</code></pre>
<p>PalletteViewModel.cs</p>
<pre><code>using Avalonia.Media;
using AvaloniaMobileApp.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;

namespace AvaloniaMobileApp.ViewModels
{
    public partial class PalletteViewModel : ViewModelBase, IRecipient&lt;ColorItemMessage&gt;
    {
      
      private Color? _colorType;

      
      private ColorItem? _colorItem;
      public void Receive(ColorItemMessage message)
      {            
            if(message.Sender == "main")
            {
                ColorType = message.Item.Color;
                ColorItem = message.Item;
                Red = message.Item.Color!.Value.R;
                Green = message.Item.Color.Value.G;
                Blue = message.Item.Color.Value.B;
            }
      }

      public PalletteViewModel()
      {
            IsActive = true;
      }

      
      private byte red;

      
      private byte green;

      
      private byte blue;

      private void UpdateColorType()
      {
            ColorType = Color.FromRgb((byte)Red, (byte)Green, (byte)Blue);
      }

      partial void OnRedChanged(byte value) =&gt; UpdateColorType();
      partial void OnGreenChanged(byte value) =&gt; UpdateColorType();
      partial void OnBlueChanged(byte value) =&gt; UpdateColorType();
    }
}
</code></pre>
<p>更改MainViewModel.cs</p>
<pre><code>using AvaloniaMobileApp.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;

namespace AvaloniaMobileApp.ViewModels;

public partial class MainViewModel : ViewModelBase, IRecipient&lt;ColorItemMessage&gt;
{
       
    private ViewModelBase? _currentPage;

   
    private ColorItem _selectedColorItem = new();
   
    public MainViewModel()
    {
      CurrentPage = App.Current.Services?.GetService&lt;ColorsViewModel&gt;();
      IsActive = true;
    }

    public void Receive(ColorItemMessage message)
    {
      if(message.Sender == "colors")
      {
            SelectedColorItem = message.Item;

            GotoPalletteCommand.Execute(null);
      }      
      
    }

   
    private Task GotoAbout()
    {
      if(CurrentPage is not AboutViewModel)
      {
            CurrentPage = App.Current.Services?.GetService&lt;AboutViewModel&gt;();
      }
      return Task.CompletedTask;
    }

   
    private Task GotoHome()
    {
      if(CurrentPage is not ColorsViewModel)
      {
            CurrentPage = App.Current.Services?.GetService&lt;ColorsViewModel&gt;();
      }
      return Task.CompletedTask;
    }

   
    private Task GotoPallette()
    {
      if (CurrentPage is not PalletteViewModel)
      {
         
            CurrentPage = App.Current.Services!.GetService&lt;PalletteViewModel&gt;();

            WeakReferenceMessenger.Default.Send(new ColorItemMessage("main", SelectedColorItem));
      }

      return Task.CompletedTask;
    }
}
</code></pre>
<p>创建要展示的Views,ColorsView.axaml。</p>
<pre><code>&lt;UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                       xmlns:vm="using:AvaloniaMobileApp.ViewModels"
                       xmlns:models="using:AvaloniaMobileApp.Models"
                       xmlns:b="using:AvaloniaMobileApp.Behaviors"
                       x:DataType="vm:ColorsViewModel"
                       b:LoadedBehavior.ExecuteCommand="{Binding InitCommand}"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:Class="AvaloniaMobileApp.Views.ColorsView"&gt;       
&lt;Grid RowDefinitions="Auto,*,auto"&gt;
          &lt;TextBlock Text="{Binding Colors.Count,StringFormat='Avalonia.Media Colors = {0}'}" Grid.Row="0"/&gt;
          &lt;ListBox x:Name="ColorsListBox" Grid.Row="1" ItemsSource="{Binding Colors}" SelectedItem="{Binding SelectedColorItem}"&gt;
                  &lt;ListBox.ItemTemplate&gt;
                       &lt;DataTemplate x:DataType="models:ColorItem"&gt;
                                  &lt;StackPanel Orientation="Horizontal"&gt;
                                  &lt;Rectangle Height="80" Width="80"&gt;
                                          &lt;Rectangle.Fill&gt;
                                                  &lt;SolidColorBrush Color="{Binding Color}"/&gt;
                                          &lt;/Rectangle.Fill&gt;
                                  &lt;/Rectangle&gt;
                                  &lt;StackPanel&gt;
                                          &lt;TextBlock Text="{Binding ColorName}"/&gt;
                                          &lt;TextBlock Text="{Binding Color,Converter={x:Static vm:ColorsViewModel.ColorToHex}}"/&gt;
                                          &lt;TextBlock Text="{Binding Color,Converter={x:Static vm:ColorsViewModel.ColorToCMYK}}"/&gt;
                                  &lt;/StackPanel&gt;
                          &lt;/StackPanel&gt;
                       &lt;/DataTemplate&gt;
                  &lt;/ListBox.ItemTemplate&gt;
          &lt;/ListBox&gt;          
&lt;/Grid&gt;
&lt;/UserControl&gt;
</code></pre>
<p>由于在代码中应用了这二行代码,导致xaml设计器报错。</p>
<pre><code>&lt;TextBlock Text="{Binding Color,Converter={x:Static vm:ColorsViewModel.ColorToHex}}"/&gt;
&lt;TextBlock Text="{Binding Color,Converter={x:Static vm:ColorsViewModel.ColorToCMYK}}"/&gt;
</code></pre>
<p>如果不介意的话,可以忽略,不影响运行,介意就改成IValueConverter。<br>
AboutView.axaml</p>
<pre><code>&lt;UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                       xmlns:vm="using:AvaloniaMobileApp.ViewModels"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
                       x:DataType="vm:AboutViewModel"
             x:Class="AvaloniaMobileApp.Views.AboutView"&gt;
&lt;Grid RowDefinitions="Auto,Auto"&gt;
          &lt;StackPanel Orientation="Horizontal" Spacing="5" Grid.Row="0"&gt;
                        &lt;TextBlock x:Name="AppNameTextBlock" Text="{Binding AppName}" FontSize="18" FontWeight="Bold"/&gt;
                        &lt;TextBlock x:Name="VersionTextBlock" FontSize="16" Text="{Binding Version}"/&gt;
                &lt;/StackPanel&gt;
                &lt;TextBlock x:Name="MessageTextBlock" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Message}"/&gt;
&lt;/Grid&gt;
&lt;/UserControl&gt;
</code></pre>
<p>Pallette.axaml</p>
<pre><code>&lt;UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
                       xmlns:vm="using:AvaloniaMobileApp.ViewModels"
                       x:DataType="vm:PalletteViewModel"
                       xmlns:conv="using:AvaloniaMobileApp.Converter"
             x:Class="AvaloniaMobileApp.Views.PalletteView"&gt;
        &lt;Grid RowDefinitions="Auto,Auto,Auto"&gt;
                &lt;TextBlock Text="{Binding ColorItem.ColorName,StringFormat='传过来的颜色名: {0}'}" Grid.Row="0"/&gt;
                &lt;Border Grid.Row="1" Background="White" Margin="5"&gt;
                        &lt;Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="200"&gt;
                                &lt;Rectangle.Fill&gt;
                                        &lt;SolidColorBrush Color="{Binding ColorType}"/&gt;
                                &lt;/Rectangle.Fill&gt;
                        &lt;/Rectangle&gt;
                &lt;/Border&gt;
                &lt;UniformGrid Columns="6" Grid.Row="2"&gt;
                        &lt;TextBlock Text="Red:" Grid.Column="0" HorizontalAlignment="Right"/&gt;
                        &lt;TextBox Watermark="16进制值" Text="{Binding Red,Converter={x:Static conv:ByteToString.Instance},UpdateSourceTrigger=LostFocus}"Grid.Column="1"/&gt;
                        &lt;TextBlock Text="Green:" Grid.Column="2" HorizontalAlignment="Right"/&gt;
                        &lt;TextBox Watermark="16进制值" Text="{Binding Green,Converter={x:Static conv:ByteToString.Instance},UpdateSourceTrigger=LostFocus}" Grid.Column="3"/&gt;
                        &lt;TextBlock Text="Blue:" Grid.Column="4" HorizontalAlignment="Right"/&gt;
                        &lt;TextBox Watermark="16进制值" Text="{Binding Blue,Converter={x:Static conv:ByteToString.Instance},UpdateSourceTrigger=LostFocus}" Grid.Column="5"/&gt;
                &lt;/UniformGrid&gt;
        &lt;/Grid&gt;
&lt;/UserControl&gt;
</code></pre>
<p>MainView.axaml</p>
<pre><code>&lt;UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="clr-namespace:AvaloniaMobileApp.ViewModels"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:Class="AvaloniaMobileApp.Views.MainView"
             x:DataType="vm:MainViewModel"&gt;
&lt;Design.DataContext&gt;
    &lt;!-- This only sets the DataContext for the previewer in an IDE,
         to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) --&gt;
    &lt;vm:MainViewModel /&gt;
&lt;/Design.DataContext&gt;

        &lt;Border x:Name="MainBorder"&gt;
                &lt;Grid RowDefinitions="Auto,*,Auto"&gt;
                        &lt;Grid ColumnDefinitions="*,Auto" Grid.Row="0"&gt;
                                &lt;TextBlock Text="Colors View" x:Name="TitleTextBlock" Grid.Column="0"/&gt;
                                &lt;StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10"&gt;
                                        &lt;Button Command="{Binding GotoHomeCommand}" Content="&amp;lt;&amp;#8211;" FontSize="18"/&gt;
                                        &lt;Button Command="{Binding GotoAboutCommand}" Content="About"/&gt;
                                &lt;/StackPanel&gt;
                        &lt;/Grid&gt;                       
                        &lt;TransitioningContentControl Grid.Row="1" Content="{Binding CurrentPage}"&gt;
                                &lt;TransitioningContentControl.PageTransition&gt;
                                        &lt;CrossFade Duration="0:0:0.500"/&gt;
                                &lt;/TransitioningContentControl.PageTransition&gt;
                        &lt;/TransitioningContentControl&gt;                       
                &lt;/Grid&gt;
        &lt;/Border&gt;
&lt;/UserControl&gt;
</code></pre>
<p>MainWindow.axaml文件不用动。<br>
由于不能用ReactiveUI,就得写附加属性,将命令绑定到事件。</p>
<pre><code>using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using System.Windows.Input;

namespace AvaloniaMobileApp.Behaviors
{
    public class LoadedBehavior : AvaloniaObject
    {
      static LoadedBehavior()
      {
            ExecuteCommandProperty.Changed.AddClassHandler&lt;Interactive&gt;(OnExecuteCommandChanged);
      }

      private static void OnExecuteCommandChanged(Interactive interactive, AvaloniaPropertyChangedEventArgs args)
      {
            if (args.NewValue is ICommand command)
            {
                interactive.AddHandler(Control.LoadedEvent, Handler);
            }
            else
            {
                interactive.RemoveHandler(Control.LoadedEvent, Handler);
            }
      }

      private static void Handler(object? sender, RoutedEventArgs e)
      {
            if (sender is Interactive interactive)
            {
                var command = interactive.GetValue(ExecuteCommandProperty);
                if (command?.CanExecute(null) == true)
                {
                  command.Execute(null);
                }
            }
      }

      public static readonly AttachedProperty&lt;ICommand&gt; ExecuteCommandProperty = AvaloniaProperty
            .RegisterAttached&lt;LoadedBehavior, Interactive, ICommand&gt;("ExecuteCommand", default, false, BindingMode.OneWay);

      public static ICommand GetExecuteCommand(AvaloniaObject obj)
      {
            return obj.GetValue(ExecuteCommandProperty);
      }

      public static void SetExecuteCommand(AvaloniaObject obj, ICommand value)
      {
            obj.SetValue(ExecuteCommandProperty, value);
      }
    }
}
</code></pre>
<p>在Converter文件夹下创建ByteToString.cs,将byte转换成string。</p>
<pre><code>using Avalonia.Data;
using Avalonia.Data.Converters;
using System;
using System.Globalization;

namespace AvaloniaMobileApp.Converter
{
    public class ByteToString : IValueConverter
    {
      public static readonly ByteToString Instance = new();
      public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
      {
            if (value is byte b &amp;&amp; targetType.IsAssignableTo(typeof(string)))
            {
                return b.ToString("X2");
            }

            return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
      }

      public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
      {
            if(value is string str &amp;&amp; targetType.IsAssignableTo(typeof(byte)))
            {
                if(byte.TryParse(str,NumberStyles.HexNumber,CultureInfo.InvariantCulture,out var b))
                {
                  return b;
                }
                return byte.MinValue;
            }

            return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
      }
    }
}
</code></pre>
<p>在App.axaml中编写样式。</p>
<pre><code>&lt;Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="using:AvaloniaMobileApp"
             x:Class="AvaloniaMobileApp.App"
             RequestedThemeVariant="Default"&gt;
             &lt;!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --&gt;

    &lt;Application.DataTemplates&gt;
      &lt;local:ViewLocator/&gt;
    &lt;/Application.DataTemplates&gt;
       
        &lt;Application.Resources&gt;
                &lt;SolidColorBrush x:Key="PrimaryBackground"&gt;#4A598F&lt;/SolidColorBrush&gt;
                &lt;SolidColorBrush x:Key="PrimaryForeground"&gt;#E8EFF7&lt;/SolidColorBrush&gt;               
        &lt;/Application.Resources&gt;

    &lt;Application.Styles&gt;
      &lt;FluentTheme /&gt;
                &lt;!--style--&gt;
                &lt;Style Selector="Border#MainBorder"&gt;
                        &lt;Setter Property="Background" Value="{StaticResource PrimaryBackground}"/&gt;
                &lt;/Style&gt;
               
                &lt;Style Selector="Border"&gt;
                        &lt;Setter Property="Padding" Value="10"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="TextBlock"&gt;
                        &lt;Setter Property="Foreground" Value="{StaticResource PrimaryForeground}"/&gt;
                        &lt;Setter Property="Margin" Value="5"/&gt;
                        &lt;Setter Property="VerticalAlignment" Value="Center"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="ListBox"&gt;
                        &lt;Setter Property="Margin" Value="5"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="ListBox TextBlock"&gt;
                        &lt;Setter Property="Foreground" Value="Black"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="TextBlock#TitleTextBlock"&gt;
                        &lt;Setter Property="FontSize" Value="18"/&gt;
                        &lt;Setter Property="FontWeight" Value="Bold"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="Button"&gt;
                        &lt;Setter Property="Background" Value="Transparent"/&gt;
                        &lt;Setter Property="VerticalAlignment" Value="Center"/&gt;
                        &lt;Setter Property="Padding" Value="5"/&gt;
                &lt;/Style&gt;               

                &lt;Style Selector="Button /template/ ContentPresenter"&gt;
                        &lt;Setter Property="Foreground" Value="{StaticResource PrimaryForeground}"/&gt;
                &lt;/Style&gt;

                &lt;Style Selector="Rectangle"&gt;
                        &lt;Setter Property="Margin" Value="5"/&gt;
                &lt;/Style&gt;
    &lt;/Application.Styles&gt;
&lt;/Application&gt;
</code></pre>
<p>在App.axaml.cs文件中使用ioc。</p>
<pre><code>using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using AvaloniaMobileApp.ViewModels;
using AvaloniaMobileApp.Views;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;

namespace AvaloniaMobileApp;

public partial class App : Application
{
    public override void Initialize()
    {
      AvaloniaXamlLoader.Load(this);
    }

    public IServiceProvider? Services { get; private set; }

    public new static App Current =&gt; (App)Application.Current!;

    private IServiceProvider ConfigureServices()
    {
      var services = new ServiceCollection();
      services.AddTransient&lt;AboutViewModel&gt;();
      services.AddTransient&lt;ColorsViewModel&gt;();
      services.AddTransient&lt;PalletteViewModel&gt;();

      return services.BuildServiceProvider();
    }

    public override void OnFrameworkInitializationCompleted()
    {
      BindingPlugins.DataValidators.RemoveAt(0);

      Services = ConfigureServices();

      if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
      {
            // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
            // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
            DisableAvaloniaDataAnnotationValidation();
            desktop.MainWindow = new MainWindow
            {
                DataContext = new MainViewModel()
            };
      }
      else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
      {
            singleViewPlatform.MainView = new MainView
            {
                DataContext = new MainViewModel()
            };
      }

      base.OnFrameworkInitializationCompleted();
    }

    private void DisableAvaloniaDataAnnotationValidation()
    {
      // Get an array of plugins to remove
      var dataValidationPluginsToRemove =
            BindingPlugins.DataValidators.OfType&lt;DataAnnotationsValidationPlugin&gt;().ToArray();

      // remove each entry found
      foreach (var plugin in dataValidationPluginsToRemove)
      {
            BindingPlugins.DataValidators.Remove(plugin);
      }
    }
}
</code></pre>
<p>将项目设为Android启动,存档,分发。<br>
在我的荣耀手机Magic5 pro上成功运行。<br>
效果展示。</p>
<p><img src="https://img2024.cnblogs.com/blog/3617433/202509/3617433-20250925212201182-1263385032.jpg"></p>
<p><img src="https://img2024.cnblogs.com/blog/3617433/202509/3617433-20250925212212575-1112709768.jpg"></p>
<p><img src="https://img2024.cnblogs.com/blog/3617433/202509/3617433-20250925212222701-876492886.jpg"></p><br><br>
来源:https://www.cnblogs.com/fudc/p/19111971
頁: [1]
查看完整版本: Avalonia:开发Android应用