Android使用.NET MAUI使用处理程序自定义控件
在开发移动应用程序的过程中,创建或自定义控件是非常常见的。在这篇文章中,我们将看到如何使用.NETMaui中的处理程序来实现这一点。
创建和自定义.NETMaui控件的方法
使用.NETMaui创建和自定义控件的方法不止一种。下面的列表显示了五种不同的实现方法。
使用自定义渲染器(Xamarin.Forms Architecture)。
使用自定义处理程序。
使用ContentView。
使用TemplatedView(模板化控件)。
使用GraphicsView(绘制控件)。
处理程序
NET MAUI处理程序的一个关键概念是映射器。每个处理程序通常提供一个属性映射器,有时还提供一个命令映射器,将跨平台控件的API映射到本机视图的API。例如,在iOS上,处理程序将.NET MAUI按钮映射到iOS UIButton。在Android上,该按钮映射到AppCompatButton:
可以对处理程序进行自定义,以在跨平台控件的API中进行自定义之外,增强该控件的外观和行为。
处理程序可以通过从IView接口派生的控件特定接口访问,这意味着我们可以在实现接口IView的任何控件中使用处理程序。
创建ImageEntry控件
NETMaui条目是一个实现IEntry接口的单行文本输入控件。在iOS上,EntryHandler将条目映射到iosuitextfield。在Android上,条目映射到AppCompatEditText,在Windows上,条目映射到TextBox。
根据我们正在创建的控件,我们可以使用接口IView更通用或更具体的东西,如条目或按钮类。同样的想法也适用于处理程序,我们可以创建一个从ViewHandler继承的自定义处理程序,或者只使用像EntryHandler这样的特定处理程序。
我们将创建的ImageEntry控件是一个条目,它具有一些公共条目没有的属性,例如Image。这样做的目的是允许开发人员定义要显示在控件内部的图像和其他一些属性。
namespace Article_002.Controls
{
public class ImageEntry : Entry
{
public ImageEntry()
{
this.HeightRequest = 50;
}
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Image), typeof(string), typeof(ImageEntry), string.Empty);
public static readonly BindableProperty ImageHeightProperty =
BindableProperty.Create(nameof(ImageHeight), typeof(int), typeof(ImageEntry), 40);
public static readonly BindableProperty ImageWidthProperty =
BindableProperty.Create(nameof(ImageWidth), typeof(int), typeof(ImageEntry), 40);
public static readonly BindableProperty ImageAlignmentProperty =
BindableProperty.Create(nameof(ImageAlignment), typeof(ImageAlignment), typeof(ImageEntry), ImageAlignment.Left);
public int ImageWidth
{
get { return (int)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public int ImageHeight
{
get { return (int)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public string Image
{
get { return (string)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public ImageAlignment ImageAlignment
{
get { return (ImageAlignment)GetValue(ImageAlignmentProperty); }
set { SetValue(ImageAlignmentProperty, value); }
}
}
public enum ImageAlignment
{
Left,
Right
}
}
创建自定义处理程序
下一步是创建处理程序,对于本篇文章,将只创建Android和iOS的处理程序。表单是使用ExportRenderer属性注册渲染器所必需的,在.NET MAUI中,处理程序注册略有不同,我们需要使用AppHostBuilder和AddHandler方法来注册处理程序。
以下代码是Android和iOS处理程序的实现。
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Widget;
using AndroidX.AppCompat.Widget;
using AndroidX.Core.Content;
using AndroidX.Core.Graphics;
using Article_002.Controls;
using Microsoft.Maui.Controls.Compatibility.Platform.Android;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
namespace Article_002.Platforms.Android.Renderes
{
public class ImageEntryRenderer : EntryHandler
{
private ImageEntry element;
protected override AppCompatEditText CreatePlatformView()
{
var editText = new AppCompatEditText(Context);
element = VirtualView as ImageEntry;
if (!string.IsNullOrEmpty(element.Image))
{
switch (element.ImageAlignment)
{
case ImageAlignment.Left:
editText.SetCompoundDrawablesWithIntrinsicBounds(GetDrawable(element.Image), null, null, null);
break;
case ImageAlignment.Right:
editText.SetCompoundDrawablesWithIntrinsicBounds(null, null, GetDrawable(element.Image), null);
break;
}
}
editText.CompoundDrawablePadding = 25;
editText.Background.SetColorFilter(Colors.White.ToAndroid(), PorterDuff.Mode.SrcAtop);
return editText;
}
private BitmapDrawable GetDrawable(string imageEntryImage)
{
int resID = Context.Resources.GetIdentifier(imageEntryImage, "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(Context, resID);
var bitmap = drawableToBitmap(drawable);
return new BitmapDrawable(Bitmap.CreateScaledBitmap(bitmap, element.ImageWidth * 2, element.ImageHeight * 2, true));
}
public Bitmap drawableToBitmap(Drawable drawable)
{
if (drawable is BitmapDrawable) {
return ((BitmapDrawable)drawable).Bitmap;
}
int width = drawable.IntrinsicWidth;
width = width > 0 ? width : 1;
int height = drawable.IntrinsicHeight;
height = height > 0 ? height : 1;
Bitmap bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
drawable.Draw(canvas);
return bitmap;
}
}
}
using Article_002.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using System.Drawing;
using UIKit;
namespace Article_002.Platforms.iOS.Renderes
{
public class ImageEntryRenderer : EntryHandler
{
private ImageEntry element;
protected override MauiTextField CreatePlatformView()
{
var textField = new MauiTextField();
element = VirtualView as ImageEntry;
if (!string.IsNullOrEmpty(element.Image))
{
switch (element.ImageAlignment)
{
case ImageAlignment.Left:
textField.LeftViewMode = UITextFieldViewMode.Always;
textField.LeftView = GetImageView(element.Image, element.ImageHeight, element.ImageWidth);
break;
case ImageAlignment.Right:
textField.RightViewMode = UITextFieldViewMode.Always;
textField.RightView = GetImageView(element.Image, element.ImageHeight, element.ImageWidth);
break;
}
}
textField.BorderStyle = UITextBorderStyle.Line;
textField.Layer.MasksToBounds = true;
return textField;
}
private UIView GetImageView(string imagePath, int height, int width)
{
var uiImageView = new UIImageView(UIImage.FromFile(imagePath))
{
Frame = new RectangleF(0, 0, width, height)
};
UIView objLeftView = new UIView(new System.Drawing.Rectangle(0, 0, width + 10, height));
objLeftView.AddSubview(uiImageView);
return objLeftView;
}
}
}
在.NETMaui中,要访问控件的本机实现,我们需要重写该方法CreatePlatformView,这样我们就可以实例化本机控件并更改我们需要/想要的属性。
总之,我们定义了图像对齐方式,并使用创建的属性按名称加载图像。
在.NETMaui中,我们处理图像的方式也有所不同。没有必要在平台项目中有图像,所有内容都位于资源文件夹中。
注册处理程序
现在是注册处理程序的时候了。我们可以使用MauiAppBuilder类中的ConfigureMauiHandlers方法来添加处理程序。
using Article_002.Controls;
namespace Article_002;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers((handlers) => {
#if ANDROID
handlers.AddHandler(typeof(ImageEntry), typeof(Platforms.Android.Renderes.ImageEntryRenderer));
#elif IOS
handlers.AddHandler(typeof(ImageEntry), typeof(Platforms.iOS.Renderes.ImageEntryRenderer));
#endif
});
return builder.Build();
}
}
使用编译指令将有助于将正确的处理程序应用到特定的平台。
使用ImageEntry
一切就绪后,我们就可以在应用程序中使用该控件,为此,我们只需在页面的XAML中添加名称空间,以便可以识别该控件。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Article_002.Controls"
BackgroundColor="{StaticResource Primary}"
x:Class="Article_002.MainPage">
<ScrollView>
<VerticalStackLayout Spacing="10">
<local:ImageEntry TextColor="{StaticResource Black}"
PlaceholderColor="{StaticResource White}"
Image="user"
Placeholder="Email"
HorizontalOptions="FillAndExpand"/>
<local:ImageEntry TextColor="{StaticResource Black}"
PlaceholderColor="{StaticResource White}"
Image="password"
Placeholder="Password"
HorizontalOptions="FillAndExpand"/>
<Button HeightRequest="50"
TextColor="{StaticResource White}"
Text="Login"
BackgroundColor="{StaticResource primary}"
HorizontalOptions="FillAndExpand"/>
<Label Text="Forgot password"
HorizontalOptions="Center"
TextColor="{StaticResource White}"/>
<Label Margin="0,0,0,20" Text="I don't have an account" VerticalOptions="EndAndExpand"
HorizontalOptions="Center"
TextColor="{StaticResource White}">
</Label>
</VerticalStackLayout>
</ScrollView>
</ContentPage>