OnTouchEvent 从未调用过 Xamarin Android Custom Renderer



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

我的猜测是命名空间上的 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 项目中的可拖动视图。

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

这是滑块类:

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="StylesStyles.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'.

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

最新更新