parent 的高度和宽度不是预期的

Height and Width of parent are not what is expected

我正在尝试创建一个应该生成垂直滑块的自定义渲染器。如果我在 tablet 模拟器上 运行 滑块的拇指拖动有效并且拇指开始完美滑动。完美检测尺寸。比我 运行 它在我的 phone 和哎呀那是什么。拇指突然不能正常工作了。问题出在放置 Thumb 的 parent 的宽度和高度上。在 table 模拟器上,我得到了预期的宽度和高度,但在我的 Phone 上,我得到了意外的值。

Android 上的尺寸不是以像素为单位吗?有什么我需要考虑的因素吗?

下面是自定义渲染器。问题出在这部分:

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;

这是 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;
        }
    }
}

parent.width 是容器的宽度(以像素为单位)。它可以小于显示宽度。此外,如果容器与其尺寸重叠(例如,"android:width="1000dp"),则它可能比显示更大。您应该在此处添加布局。

要获得显示宽度,请参阅 How to get screen dimensions as pixels in Android

好的,正如我所怀疑的那样,问题出在密度上。 Xamarin.Essentials 有解决办法。在您的代码中执行此操作:

var density = global::Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density; // 2.625 in my case

将 parent.Width 和 parent.Height 与密度相乘,得到宽度(以像素为单位)

var width  = parent.Width * density;
var height = parent.Height * density;