Piszę niestandardową nakładkę dla czytnika QR ZXinga. Oto działający przykład:Dlaczego ten sam interfejs jest renderowany inaczej na emulatorze i urządzeniu?
public partial class CustomScanPage : ContentPage
{
ZXingScannerView zxing;
private List<BoxView> _boxes;
public CustomScanPage() : base()
{
zxing = new ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
zxing.OnScanResult += (result) =>
Device.BeginInvokeOnMainThread(async() =>
{
zxing.IsAnalyzing = false;
SetBoxesColor(Color.FromHex("#76ff03"));
await Task.Delay(2000);
await DisplayAlert("Scanned Barcode", result.Text, "OK");
SetBoxesColor(Color.White);
});
zxing.Options = new MobileBarcodeScanningOptions {
PossibleFormats = new List<ZXing.BarcodeFormat> { ZXing.BarcodeFormat.QR_CODE }
};
var overlay = BuildGrid();
var grid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
grid.Children.Add(zxing);
grid.Children.Add(overlay);
Content = grid;
}
protected override void OnAppearing()
{
base.OnAppearing();
zxing.IsScanning = true;
}
protected override void OnDisappearing()
{
zxing.IsScanning = false;
base.OnDisappearing();
}
private AbsoluteLayout BuildGrid()
{
var al = new AbsoluteLayout();
var mask = new BoxView
{
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
BackgroundColor = Color.Transparent
};
var maskSide = 196;
var yBegin = Math.Round((App.ScreenHeight - maskSide)/2);
var xBegin = Math.Round((App.ScreenWidth - maskSide)/2);
var barLong = 40;
var barShort = 4;
var barColor = Color.White;
var grid = new Grid { ColumnSpacing = 0, RowSpacing = 0 };
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(yBegin, GridUnitType.Absolute) });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(maskSide, GridUnitType.Absolute) });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(App.ScreenHeight - yBegin, GridUnitType.Absolute) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(xBegin, GridUnitType.Absolute) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(maskSide, GridUnitType.Absolute) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(App.ScreenWidth - xBegin, GridUnitType.Absolute) });
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232"), }, 0, 0);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 0, 1);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 0, 2);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 1, 0);
grid.Children.Add(mask, 1, 1);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 1, 2);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 2, 0);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 2, 1);
grid.Children.Add(new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = Color.FromHex("#80323232") }, 2, 2);
grid.HorizontalOptions = LayoutOptions.FillAndExpand;
grid.VerticalOptions = LayoutOptions.FillAndExpand;
AbsoluteLayout.SetLayoutBounds(grid, new Rectangle(0, 0, 1, 1));
AbsoluteLayout.SetLayoutFlags(grid, AbsoluteLayoutFlags.All);
al.Children.Add(grid);
var b1 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b1, new Rectangle(xBegin - barShort, yBegin, barShort, barLong - barShort));
AbsoluteLayout.SetLayoutFlags(b1, AbsoluteLayoutFlags.None);
var b2 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b2, new Rectangle(xBegin - barShort, yBegin - barShort, barLong, barShort));
AbsoluteLayout.SetLayoutFlags(b2, AbsoluteLayoutFlags.None);
var b3 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b3, new Rectangle(xBegin + maskSide, yBegin, barShort, barLong - barShort));
AbsoluteLayout.SetLayoutFlags(b3, AbsoluteLayoutFlags.None);
var b4 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b4, new Rectangle(xBegin - barLong + maskSide + barShort, yBegin - barShort, barLong, barShort));
AbsoluteLayout.SetLayoutFlags(b4, AbsoluteLayoutFlags.None);
var b5 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b5, new Rectangle(xBegin - barShort, yBegin + maskSide + barShort - barLong, barShort, barLong - barShort));
AbsoluteLayout.SetLayoutFlags(b5, AbsoluteLayoutFlags.None);
var b6 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b6, new Rectangle(xBegin - barShort, yBegin + maskSide, barLong, barShort));
AbsoluteLayout.SetLayoutFlags(b6, AbsoluteLayoutFlags.None);
var b7 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b7, new Rectangle(xBegin + maskSide - barLong + barShort, yBegin + maskSide, barLong, barShort));
AbsoluteLayout.SetLayoutFlags(b7, AbsoluteLayoutFlags.None);
var b8 = new BoxView { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, BackgroundColor = barColor };
AbsoluteLayout.SetLayoutBounds(b8, new Rectangle(xBegin + maskSide, yBegin + maskSide - barLong + barShort, barShort, barLong - barShort));
AbsoluteLayout.SetLayoutFlags(b8, AbsoluteLayoutFlags.None);
al.Children.Add(b1);
al.Children.Add(b2);
al.Children.Add(b3);
al.Children.Add(b4);
al.Children.Add(b5);
al.Children.Add(b6);
al.Children.Add(b7);
al.Children.Add(b8);
_boxes = new List<BoxView> { b1, b2, b3, b4, b5, b6, b7, b8 };
return al;
}
private void SetBoxesColor(Color c)
{
foreach (var box in _boxes)
{
box.Color = c;
}
}
}
Jak widać, nic nie jest skomplikowane. Ale problem polega na tym, jak ta strona jest renderowana. Na urządzeniach Xamarin Android Player (Nexus 4 KitKat HD i Nexus 5 Lollipop Full HD) wszystko wygląda zgodnie z oczekiwaniami:
Jednak gdy uruchomię samej aplikacji (bez różowym tle na siatce) na moim Nexus 5x urządzenie (Marshmallow, Full HD), jak widać, piksele nie są precyzyjne, jest trochę niedopasowania w górnym prawym zielonym kształcie, trochę przesunięcia w lewych kształtach i jest jedno-pikselowa przezroczysta przerwa na siatce wiersz:
fakt, że kod jest poprawnie renderowane na dwóch urządzeniach pozwala mi sądzić, że moja arytmetyczna punktacja jest poprawna. Co może być problemem? I co ważniejsze, co mogę zrobić, aby go rozwiązać?
EDIT:
Może jakoś to dlatego, że pt/px
stosunek mojego urządzenia jest inny niż stosunek devices emulator?
EDIT:
@ odpowiedź Cheesebaron było źle. Oto, co próbowałem zrobić, co nie pomogło: Stworzyłem CustomBoxView
, CustomBoxViewNative
, CustomBoxViewRenderer
i użyłem CustomBoxView
zamiast BoxView
. To nie działa.
CustomBoxView:
public class CustomBoxView : View
{
public static readonly BindableProperty ColorProperty = BindableProperty.Create("Color", typeof (string),
typeof (CustomBoxView), "#FF0000");
public string Color
{
get { return (string) GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
}
CustomBoxViewNative:
public class CustomBoxViewNative : View
{
private Canvas _canvas;
private Rect _rect;
public CustomBoxViewNative(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public CustomBoxViewNative(Context context) : base(context)
{
}
public CustomBoxViewNative(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public CustomBoxViewNative(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
}
public CustomBoxViewNative(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
}
protected override void OnDraw(Canvas canvas)
{
var paintCircle = new Paint { Color = Color.White };
paintCircle.StrokeWidth = Width*Resources.DisplayMetrics.Density;
_rect = new Rect(0, 0, Width, Height);
_canvas = canvas;
_canvas.DrawRect(_rect, paintCircle);
}
}
CustomBoxViewRenderer:
public class CustomBoxViewRenderer : ViewRenderer<CustomBoxView, CustomBoxViewNative>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomBoxView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
var nativeControl = new CustomBoxViewNative(Forms.Context);
SetNativeControl(nativeControl);
}
}
nie ufa emulatory, spróbuj uruchomić go na wielu urządzeniach, aby sądzić. –
Jest to problem z renderowaniem podpikselowych. Musisz uwzględnić gęstość ekranu dla szerokości obrysu. – Cheesebaron
@Cheesebaron mógłbyś być bardziej konkretny? może dostarczyć pewnych zasobów – nicks