Xamarin にて、ImageButton をに .cs 使から画像を .Source に設定し画面を表示すると、一瞬ちらっと何かがせ見えます。iPhone より Andoird の方が顕著です。よく凝視すると、ImageButton に載せた画像が、ボタンのサイズを超えて描画されているようです。
GUIシステムの創成期だった1990年代ならまだしも、イマドキこんなあからさまな事象があるなんて。。。HTMLだってそんなことはないでしょう。
以下のようにトライしてみましたがダメでした。
- 昔よくやったGUI描画中隠し:isVisbile=false して表示寸前で isVisbile=true の効果もなし。
- 通常のImageでは発生しませんが、Xamarin ではタップイベントが未サポ。
- NuGetのFFImageLoadingライブラリの CachedImage でも同じ。
- .cs 内で、WidthRequest で強制しても効かず。
面倒ですが、ImageButton のサイズに合わせて画像を縮小して、設定するしかないようです。しかし画像の縮小は Xamarin.Form では書けないようです。 Android 、iOS側のそれぞれのコードで書く必要があるようです。以下URLにサンプルコードがありました。
上記コードを、Project.Android/MainActivity.cs と Project.iOS/Main.cs に、サービスとして分けて実装します。 当方は、.NETのStremクラス に置き替えて実装しました。 実用には各所でnullチェックが必要です。
AndroidのMainActivity.csの場合:
[assembly: Dependency( typeof( ResizeImageService ) ) ]
ublic class ResizeImageService : IResizeImageService {
public Stream ResizeImage(
Stream imgStream, // Imageストリーム
double width, // 0時は縦サイズに合わせる
double height // 0時は横サイズに合わせる
) {
Bitmap orgImg = BitmapFactory.DecodeStream ( imgStream );
if ( height == 0 ) {
// 縦サイズ未指定のとき、幅からImgaeオリジナル縦横比率で算出する。
height = width / orgImg.Width * orgImg.Height;
}
else if ( width == 0 ) {
// 幅サイズ未指定のとき、高さからImgaeオリジナル縦横比率で算出する。
width = height / orgImg.Height * orgImg.Width ;
}
Bitmap resizImg = Bitmap.CreateScaledBitmap( orgImg, ( int )width, ( int )height, false );
using (MemoryStream ms = new MemoryStream())
{
resizImg.Compress( Bitmap.CompressFormat.Jpeg, // 画像形式 Jpeg or prg
100, // image品質 0~100
ms // 出力Stream
);
return new MemoryStream( ms.ToArray() );
}
}
iOSのmain.csの場合:
[assembly: Dependency( typeof( ResizeImageService ) ) ]
public class ResizeImageService : IResizeImageService {
public Stream ResizeImage(
Stream imgStream, // Imageストリーム
double width, // 0時は縦サイズに合わせる
double height // 0時は横サイズに合わせる
) {
// Stream(生Data)からImage生成
UIImage orgImg = ImageFromStream( imgStream );
UIImageOrientation orientation = orgImg.Orientation;
if ( height == 0 ) {
// 縦サイズ未指定のとき、幅からImgaeオリジナル縦横比率で算出する。
height = width / orgImg.Size.Width * orgImg.Size.Height;
}
else if ( width == 0 ) {
// 幅サイズ未指定のとき、高さからImgaeオリジナル縦横比率で算出する。
width = height / orgImg.Size.Height * orgImg.Size.Width ;
}
// create a 24bit RGB image (Apple API)
using (CGBitmapContext context = new CGBitmapContext(
IntPtr.Zero, // i : dataポインタ
(int)width, // i : Bitmap幅
(int)height, // i : Bitmap縦
8, // i : 1つの色のBit数。× 3 が所謂Bit深さ
4 * (int)width, // i : Bitmap容量
CGColorSpace.CreateDeviceRGB(), // i : カラースペース
CGImageAlphaInfo.PremultipliedFirst // i : bitmapInfo
) ) {
// RectangleF は Microsoft のもの。
RectangleF imageRect = new RectangleF(0, 0, ( float )width, ( float )height );
context.DrawImage( imageRect, orgImg.CGImage ); // draw the image
UIKit.UIImage resizedImage = UIKit.UIImage.FromImage(
context.ToImage(),
0,
orientation );
// save the image as a jpeg (ToStreamは未サポ)
byte[] imgBytes = resizedImage.AsJPEG().ToArray();
return new MemoryStream( imgBytes ); // byte[] -> Stream 変換
}
private static UIKit.UIImage ImageFromStream( Stream data) {
UIKit.UIImage image;
try {
image = new UIKit.UIImage( Foundation.NSData.FromStream( data ) );
}
catch (Exception e) {
return null;
}
return image;
}
}
Xamarin.Form側:
ImageButton newImg = new ImageButton {
中略
};
System.IO.Stream stream = 画像をStream変換;
Stream stream2 = DependencyService.
Get<IResizeImageService>().ResizeImage(
stream,
newImg.Width, /* 幅: Grid幅 */
0 /* 高さ:幅に合せ */ );
newImg.Source = ImageSource.FromStream(() => stream2 );
ImageButton を継承して画像縮小付きの新クラスをつくるとベストですね。