I'm trying to create a custom renderer that should produce a vertical Slider. The drag of the thumb of the slider works and the thumb starts to slide perfectly if I run it on a tablet emulator. Sizes are detected perfectly. Than I ran it on a my phone and whoops what's that. The thumb suddenly doesn't function as expected anymore. The problem is in the Width and Height of the parent the Thumb is placed upon. On the table emulator I get the expected Width and Height but on my Phone I get unexpected values.
Are sizes not in pixels on Android? Is there some factor I need to take into consideration.
Below is the Custom Renderer. The problem is in this part:
case MotionEventActions.Move:
if (touchedDown)
{
if (dragView.DragDirection == DragDirectionTypes.All || dragView.DragDirection == DragDirectionTypes.Horizontal)
{
var newX = x - dX;
if (parent != null)
{
// The parent.Width isn't what I expect below
if (newX + Width > parent.Width) newX = (float)(parent.Width - Width);
if (newX < 0) newX = 0;
}
SetX(newX);
}
if (dragView.DragDirection == DragDirectionTypes.All || dragView.DragDirection == DragDirectionTypes.Vertical)
{
var newY = y - dY;
if (parent != null)
{
// The parent.Height isn't what I expect below
if (newY + Height > parent.Height) newY = (float)(parent.Height - Height);
if (newY < 0) newY = 0;
}
SetY(newY);
}
}
break;
Here's the full source of the DraggableViewRenderer:
using Android.Content;
using Android.Views;
using TGB.Xamarin.Forms.TestApp.Droid.Renderers.Views;
using TGB.Xamarin.Forms.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using static TGB.Xamarin.Forms.Views.DraggableView;
using xam = global::Xamarin.Forms;
[assembly: ExportRenderer(typeof(DraggableView), typeof(DraggableViewRenderer))]
namespace TGB.Xamarin.Forms.TestApp.Droid.Renderers.Views
{
public class DraggableViewRenderer : VisualElementRenderer<xam.View>
{
float originalX;
float originalY;
float dX;
float dY;
bool firstTime = true;
bool touchedDown = false;
public DraggableViewRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<xam.View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
LongClick -= HandleLongClick;
}
if (e.NewElement != null)
{
LongClick += HandleLongClick;
var dragView = Element as DraggableView;
dragView.RestorePositionCommand = new Command(() =>
{
if (!firstTime)
{
SetX(originalX);
SetY(originalY);
}
});
}
}
private void HandleLongClick(object sender, LongClickEventArgs e)
{
var dragView = Element as DraggableView;
if (firstTime)
{
originalX = GetX();
originalY = GetY();
firstTime = false;
}
dragView.DragStarted();
touchedDown = true;
}
public override bool OnTouchEvent(MotionEvent e)
{
float x = e.RawX;
float y = e.RawY;
var dragView = Element as DraggableView;
var parent = dragView.Parent as xam.View;
switch (e.Action)
{
case MotionEventActions.Down:
if (dragView.DragMode == DragModes.Touch)
{
if (!touchedDown)
{
if (firstTime)
{
originalX = GetX();
originalY = GetY();
firstTime = false;
}
dragView.DragStarted();
}
touchedDown = true;
}
dX = x - this.GetX();
dY = y - this.GetY();
break;
case MotionEventActions.Move:
if (touchedDown)
{
if (dragView.DragDirection == DragDirectionTypes.All || dragView.DragDirection == DragDirectionTypes.Horizontal)
{
var newX = x - dX;
if (parent != null)
{
if (newX + Width > parent.Width) newX = (float)(parent.Width - Width);
if (newX < 0) newX = 0;
}
SetX(newX);
}
if (dragView.DragDirection == DragDirectionTypes.All || dragView.DragDirection == DragDirectionTypes.Vertical)
{
var newY = y - dY;
if (parent != null)
{
if (newY + Height > parent.Height) newY = (float)(parent.Height - Height);
if (newY < 0) newY = 0;
}
SetY(newY);
}
}
break;
case MotionEventActions.Up:
touchedDown = false;
DraggableViewDragEndedEventArgs args = new DraggableViewDragEndedEventArgs
{
X = GetX(),
Y = GetY()
};
dragView.DragEnded(args);
break;
case MotionEventActions.Cancel:
touchedDown = false;
break;
}
return base.OnTouchEvent(e);
}
public override bool OnInterceptTouchEvent(MotionEvent e)
{
BringToFront();
return true;
}
}
}
Ok the problem was Density as I suspected. Xamarin.Essentials has the solution. Do this in you code:
var density = global::Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density; // 2.625 in my case
Multiply the parent.Width and parent.Height with density and you get the width in pixels
var width = parent.Width * density;
var height = parent.Height * density;