【问题标题】:OnTouchEvent never called in Xamarin Android Custom RendererOnTouchEvent 从未在 Xamarin Android 自定义渲染器中调用
【发布时间】:2020-09-08 15:48:59
【问题描述】:

我为 Android 创建了一个自定义渲染器,并为自定义 Slider 控件实现了它。我有2个解决方案。在这两种解决方案中,Android 应用程序都是在 Android 手机上构建和启动的。测试程序显示滑块很好,我可以上下拖动拇指而没有任何问题。第二种解决方案是一个更大的解决方案,应该在其中实现滑块。如果我运行该解决方案,则会显示滑块,但我无法上下移动拇指。 OnTouchEvent 永远不会被触发。以下是 Slider 所需的所有代码。我知道这很多,但我希望有人能看到我的问题。

我的猜测是命名空间上的 ExportRenderer 属性以某种方式找不到或未触发。调用了“OnElementChanged”覆盖,但没有进一步调用。

这是 Android 项目中 DraggableView(滑块的拇指)的自定义渲染器

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)
                    {
                        var density = global::Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density;

                        if (dragView.DragDirection == DragDirectionTypes.All || dragView.DragDirection == DragDirectionTypes.Horizontal)
                        {
                            var newX = x - dX;

                            if (parent != null)
                            {
                                if (newX + Width > parent.Width * density) newX = (float)(parent.Width * density - 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 * density) newY = (float)(parent.Height * density - 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;
        }
    }
}

这是 .Net 标准 2.0 项目中的 DraggableView。

    using System;
using System.Windows.Input;
using Xamarin.Forms;

namespace TGB.Xamarin.Forms.Views
{
    public partial class DraggableView : ContentView
    {
        public event EventHandler DragStart = delegate { };

        public delegate void DragEndEventHandler(object sender, DraggableViewDragEndedEventArgs args);

        public event DragEndEventHandler DragEnd;

        public enum DragDirectionTypes
        {
            All,
            Vertical,
            Horizontal
        }

        public enum DragModes
        {
            Touch,
            LongPress
        }

        public static readonly BindableProperty DragDirectionProperty = BindableProperty.Create(
            propertyName: "DragDirection",
            returnType: typeof(DragDirectionTypes),
            declaringType: typeof(DraggableView),
            defaultValue: DragDirectionTypes.All,
            defaultBindingMode: BindingMode.TwoWay);

        public DragDirectionTypes DragDirection
        {
            get { return (DragDirectionTypes)GetValue(DragDirectionProperty); }
            set { SetValue(DragDirectionProperty, value); }
        }


        public static readonly BindableProperty DragModeProperty = BindableProperty.Create(
           propertyName: "DragMode",
           returnType: typeof(DragModes),
           declaringType: typeof(DraggableView),
           defaultValue: DragModes.Touch,
           defaultBindingMode: BindingMode.TwoWay);

        public DragModes DragMode
        {
            get { return (DragModes)GetValue(DragModeProperty); }
            set { SetValue(DragModeProperty, value); }
        }

        public static readonly BindableProperty IsDraggingProperty = BindableProperty.Create(
          propertyName: "IsDragging",
          returnType: typeof(bool),
          declaringType: typeof(DraggableView),
          defaultValue: false,
          defaultBindingMode: BindingMode.TwoWay);

        public bool IsDragging
        {
            get { return (bool)GetValue(IsDraggingProperty); }
            set { SetValue(IsDraggingProperty, value); }
        }

        public static readonly BindableProperty RestorePositionCommandProperty = BindableProperty.Create(nameof(RestorePositionCommand), typeof(ICommand), typeof(DraggableView), default(ICommand), BindingMode.TwoWay, null, OnRestorePositionCommandPropertyChanged);

        static void OnRestorePositionCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var source = bindable as DraggableView;
            if (source == null)
            {
                return;
            }
            source.OnRestorePositionCommandChanged();
        }

        private void OnRestorePositionCommandChanged()
        {
            OnPropertyChanged("RestorePositionCommand");
        }

        public ICommand RestorePositionCommand
        {
            get
            {
                return (ICommand)GetValue(RestorePositionCommandProperty);
            }
            set
            {
                SetValue(RestorePositionCommandProperty, value);
            }
        }

        public void DragStarted()
        {
            DragStart(this, default);
            IsDragging = true;
        }

        public void DragEnded(DraggableViewDragEndedEventArgs args)
        {

            IsDragging = false;
            DragEnd(this, args);
        }
    }
}

EndDrag 中使用的 EventArgs 类

using System;
using System.Collections.Generic;
using System.Text;

namespace TGB.Xamarin.Forms.Views
{
    public class DraggableViewDragEndedEventArgs : EventArgs
    {
        public double X;
        public double Y;
    }
}

这是 Slider 类:

using System;
using TGB.Xamarin.Forms.Views;
using Xamarin.Forms;

namespace TGB.Xamarin.Forms.Controls
{
    public class Slider : AbsoluteLayout
    {
        private DraggableView m_Thumb;

        public static readonly BindableProperty MinProperty = BindableProperty.Create(
            "Min", typeof(int), typeof(Slider), 0, propertyChanged: (bindable, oldvalue, newvalue) => { ((Slider)bindable).InvalidateLayout(); });

        /// <summary>
        /// The minimum for the slider
        /// </summary>
        public int Min
        {
            set { SetValue(MinProperty, value); }
            get { return (int)GetValue(MinProperty); }
        }

        public static readonly BindableProperty MaxProperty = BindableProperty.Create(
            "Max", typeof(int), typeof(Slider), 100, propertyChanged: (bindable, oldvalue, newvalue) => { ((Slider)bindable).InvalidateLayout(); });

        /// <summary>
        /// The maximum for the slider
        /// </summary>
        public int Max
        {
            set { SetValue(MaxProperty, value); }
            get { return (int)GetValue(MaxProperty); }
        }

        public static readonly BindableProperty ValueProperty = BindableProperty.Create(
            "Value", typeof(int), typeof(Slider), 100, propertyChanged: (bindable, oldvalue, newvalue) => { ((Slider)bindable).InvalidateLayout(); });

        /// <summary>
        /// The value for the slider
        /// </summary>
        public int Value
        {
            set { SetValue(ValueProperty, value); }
            get { return (int)GetValue(ValueProperty); }
        }

        public Slider()
        {
            BackgroundColor = System.Drawing.Color.Green;

            Init();
        }

        private void Init()
        {
            m_Thumb = new DraggableView();

            m_Thumb.DragEnd += Draggable_DragEnd;

            m_Thumb.HorizontalOptions = new LayoutOptions { Alignment = LayoutAlignment.Fill, Expands = true };

            AbsoluteLayout.SetLayoutBounds(m_Thumb, new Rectangle(0, 0, 1, 0.2));
            AbsoluteLayout.SetLayoutFlags(m_Thumb, AbsoluteLayoutFlags.All);

            m_Thumb.BackgroundColor = System.Drawing.Color.Orange;

            this.Children.Add(m_Thumb);
        }

        private void Draggable_DragEnd(object sender, DraggableViewDragEndedEventArgs e)
        {
            var scope = Max - Min;

            var regionSize = this.Height - m_Thumb.Height;

            var perPixel = scope / regionSize;

            Value = Min + Convert.ToInt32(e.Y * perPixel);
        }
    }
}

这是 MainPage.xaml 中的实现:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:tgbc="clr-namespace:TGB.Xamarin.Forms.Controls;assembly=TGB.Xamarin.Forms"
             mc:Ignorable="d"
             x:Name="Page"
             x:Class="ElectroSpit.MainPage">

    <ContentPage.Resources>
        <StyleSheet Source="Styles\Styles.css" />
    </ContentPage.Resources>

    <StackLayout x:Name="Main" 
                    Orientation="Vertical"
                    BackgroundColor="Transparent"
                    VerticalOptions="FillAndExpand"
                    HorizontalOptions="FillAndExpand">
        <ContentView x:Name="Sidebar"
                    VerticalOptions="Start"
                    HorizontalOptions="Start"
                    StyleClass="Sidebar">
            <ContentView.Content>
                <Grid VerticalOptions="FillAndExpand" StyleClass="SidebarSlider">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>

                    <StackLayout Grid.Row="0"
                                 Grid.Column="0"
                                 Orientation="Vertical"
                                 VerticalOptions="FillAndExpand" 
                                 StyleClass="SidebarSlider" >


                         <!-- HERE ARE THE SLIDERS -->


                        <tgbc:Slider x:Name="Pitch"  VerticalOptions="FillAndExpand" StyleClass="SidebarSlider" />
                        <tgbc:Slider x:Name="Tremolo" VerticalOptions="FillAndExpand"  StyleClass="SidebarSlider" />
                    </StackLayout>

                    <StackLayout Grid.Row="0"
                                 Grid.Column="1"
                                 Orientation="Vertical">

                        <Label Text="Trans"  StyleClass="SidebarLabel"/>

                        <StackLayout Orientation="Horizontal">
                            <Button x:Name="TransposeUp" Text="+" StyleClass="SidebarButton"/>
                            <Button x:Name="TransposeDown" Text="{Binding Transposition}" StyleClass="SidebarButton" />
                        </StackLayout>

                        <Label Text="Brightness"  StyleClass="SidebarLabel"/>
                        <Stepper x:Name="Brichtness" Minimum="0" Maximum="100" StyleClass="SidebarStepper"/>
                        <Button x:Name="Settings" Text="Opt" StyleClass="SidebarButton"/>
                        <Label Text="Glide"  StyleClass="SidebarLabel"/>
                        <Stepper x:Name="Glide" Minimum="0" Maximum="100" StyleClass="SidebarStepper"/>
                        <Label Text="Octave" StyleClass="SidebarLabel"/>

                        <StackLayout Orientation="Horizontal">
                            <Button x:Name="OctaveUp" Text="+" StyleClass="SidebarButton"/>

                            <Button x:Name="OctaveDown" Text="{Binding Octave}" StyleClass="SidebarButton"/>
                        </StackLayout>
                    </StackLayout>

                    <StackLayout>
                        <ContentView x:Name="Schema"
                                     BackgroundColor="Transparent"
                                     VerticalOptions="FillAndExpand"
                                     HorizontalOptions="FillAndExpand">
                        </ContentView>
                    </StackLayout>
                </Grid>
            </ContentView.Content>
        </ContentView>
        <ContentView x:Name="Notes"
                        BackgroundColor="Transparent"
                        VerticalOptions="FillAndExpand"
                        HorizontalOptions="FillAndExpand">
        </ContentView>
    </StackLayout>
</ContentPage>

== 编辑 ==

工作应用程序和非工作应用程序上的消息有所不同。这是我在工作应用程序上得到的:

05-22 10:29:42.178 D/Mono    ( 6676): DllImport searching in: '__Internal' ('(null)').
05-22 10:29:42.178 D/Mono    ( 6676): Searching for 'java_interop_jnienv_call_float_method_a'.
05-22 10:29:42.178 D/Mono    ( 6676): Probing 'java_interop_jnienv_call_float_method_a'.
05-22 10:29:42.178 D/Mono    ( 6676): Found as 'java_interop_jnienv_call_float_method_a'.
05-22 10:29:42.201 D/Mono    ( 6676): DllImport searching in: '__Internal' ('(null)').
05-22 10:29:42.201 D/Mono    ( 6676): Searching for 'java_interop_jnienv_call_long_method_a'.
05-22 10:29:42.201 D/Mono    ( 6676): Probing 'java_interop_jnienv_call_long_method_a'.
05-22 10:29:42.201 D/Mono    ( 6676): Found as 'java_interop_jnienv_call_long_method_a'.
05-22 10:29:42.260 D/Mono    ( 6676): Requesting loading reference 6 (of 7) of TGB.Xamarin.Forms.Android.dll
05-22 10:29:42.260 D/Mono    ( 6676): Loading reference 6 of TGB.Xamarin.Forms.Android.dll asmctx DEFAULT, looking for Xamarin.Essentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
05-22 10:29:42.260 D/Mono    ( 6676): Assembly Ref addref TGB.Xamarin.Forms.Android[0xd57b8680] -> Xamarin.Essentials[0xebf7fd00]: 3

这在非工作应用程序上:

05-22 09:46:52.254 D/Mono (14307): DllImport searching in: '__Internal' ('(null)'). 
05-22 09:46:52.254 D/Mono (14307): Searching for 'java_interop_jnienv_call_float_method_a'. 
05-22 09:46:52.254 D/Mono (14307): Probing 'java_interop_jnienv_call_float_method_a'. 
05-22 09:46:52.254 D/Mono (14307): Found as 'java_interop_jnienv_call_float_method_a'.

【问题讨论】:

  • 我测试你的代码,如果我上下拖动拇指,可以调用OnTouchEvent,没有问题。请看屏幕:github.com/CherryBu/gif/blob/master/8.gif
  • 是的,我知道。有点令人沮丧的是,它确实在一个解决方案中起作用,但在必须实施的解决方案中却不起作用。今天我将尝试一步一步地进行。排除代码,直到它再次开始工作。
  • 得出的结论是它不在 Slider 类中。 DraggableView(Renderer) 类不工作。当我在应用程序中单击 DraggableView 时出现此错误:5-22 09:28:39.136 W/ame.electrospi(13883): Checksum mismatch for dex base.apk 05-22 09:28:39.136 W/ame。 Electrospi(13883):无法将方法添加到现有分析器。正在清除配置文件数据。
  • 好吧,那是误导。 2恰好巧合。当我单击 DraggableView 时,下一条评论包含消息:
  • 05-22 09:46:52.254 D/Mono (14307):DllImport 搜索:'__Internal'('(null)')。 05-22 09:46:52.254 D/Mono (14307): 搜索“java_interop_jnienv_call_float_method_a”。 05-22 09:46:52.254 D/Mono (14307): 探测“java_interop_jnienv_call_float_method_a”。 05-22 09:46:52.254 D/Mono (14307): 发现为 'java_interop_jnienv_call_float_method_a'。

标签: c# android xamarin.forms custom-renderer


【解决方案1】:

好的,找到了。控制已经在工作,我的错误在其他地方。如果您仔细观察 XAML,您会发现最后一个 StackLayout 没有 Grid.Row 和 Grid.Column 属性,导致它位于两个 Sliders 的顶部。因此,触摸不会转到滑块,而是转到 StackLayout 内部的 ContentView,这给我的印象是它不起作用。

【讨论】:

  • 很高兴听到您现在已经解决了您的问题,请记得将您的回复标记为答案来关闭您的帖子,这有助于其他人找到此答案,谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-12-26
  • 2021-06-18
  • 2018-07-23
  • 1970-01-01
  • 1970-01-01
  • 2020-01-24
  • 2020-03-16
  • 1970-01-01
相关资源
最近更新 更多