起因
做了个播放器控制界面,发现进度滑动条Slider
这个控件,如果全用默认不加修饰,那个滑块在iOS上会特别大。
要定制滑块,只要修改这个属性:
1 public Xamarin.Forms.ImageSource ThumbImageSource { get ; set ; }
最简单的做法当然是直接加载一张图片。
但是由于我想做一个方便复用的控件,最好是只包含代码就够,如果还带有资源,就不那么省心了。
所以想想不如自己画这个小圆块,反正xamarin里也支持动态绘图,且能将绘好的图直接做为图片使用。
于是祭起SkiaSharp
这个大杀器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var slider = this .FindByName<Slider>("PrgSlider" );var size = (int )slider.Height;SKBitmap bitmap = new SKBitmap(size, size); using (SKCanvas bitmapCanvas = new SKCanvas(bitmap)){ bitmapCanvas.Clear(); SKPaint paint = new SKPaint { Style = SKPaintStyle.Fill, Color = Color.Pink.ToSKColor(), }; bitmapCanvas.DrawCircle(size / 2 , size / 2 , size / 2 , paint); } this .FindByName<Slider>("PrgSlider" ).ThumbImageSource = (SKBitmapImageSource)bitmap;
但是没想到用了自绘的图像后,居然还是一大一小,iOS上仍然倍大。
这时我想到了之前改过的一个Xamarin.iOS
的bug
,(提过pr ):
动态加载的图片,没有根据屏幕缩放系数(retina scale)做相应处理!
很快找到它的代码 一看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKImageImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKBitmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPixmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] [assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPictureImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler)) ] public sealed class SKImageSourceHandler : IImageSourceHandler { public Task<UIImage> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken ), float scale = 1f ) { UIImage image = null ; var imageImageSource = imagesource as SKImageImageSource; if (imageImageSource != null ) { image = imageImageSource.Image?.ToUIImage(); } var bitmapImageSource = imagesource as SKBitmapImageSource; if (bitmapImageSource != null ) { image = bitmapImageSource.Bitmap?.ToUIImage(); } var pixmapImageSource = imagesource as SKPixmapImageSource; if (pixmapImageSource != null ) { image = pixmapImageSource.Pixmap?.ToUIImage(); } var pictureImageSource = imagesource as SKPictureImageSource; if (pictureImageSource != null ) { image = pictureImageSource.Picture?.ToUIImage(pictureImageSource.Dimensions); } return Task.FromResult(image); } }
果不其然!完全没有用到最后一个参数scale
,而且从标注了默认值1f
来看,他们可能都不认为这个值会变 😓
修正很简单,只要加上对scale
的处理即可:
1 if (scale > 1 ) image = new UIImage(image.CGImage, scale, UIImageOrientation.Up);
但问题是如何在不自己编译SkiaSharp
的情况下修正它呢?毕竟维护一个xamarin
库太费事了 😂
好在问题是出在ImageSourceHandler
上,xamarin
上所有图像处理器都是通过属性自注册,集中处理,即:
1 2 3 4 5 6 7 8 namespace Xamarin.Forms { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true) ] public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute { public ExportImageSourceHandlerAttribute (Type handler, Type target ) ; } }
这个ExportImageSourceHandler
为每一种类型的ImageSource
指定一个处理类,只要自己写个修正好的类,注册给对应的SkImageImageSource
即可。
但是,和它源码中已经注册的处理类会不会冲突呢?
幸好,该属性父类中有一个Priority
成员,就是用来解决绑定到同一目标的多个处理类之间优先级的 😆
所以,只要在注册自己的类时随便指定一个比默认值大的优先级,就顺利接管了SkImageImageSource
的处理了:
[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKBitmapImageSource), typeof(mb.SKImageSourceHandler), Priority = mb.SKImageSourceHandler.Priority)]
修正后: