XamEffects
XamEffects copied to clipboard
TouchEffect and scrolling (Android)
Hi, i have the same problem as described in this post: https://github.com/mrxten/XamEffects/issues/41 and i played a litte bit with your code. If i comment out the TouchCollector.Add and TouchCollector.Delete calls the ripple effect is not called while scrolling, like items in a ListView. But my problem is now, that the Command is not executed. Do you still support your library and could you fix it?
I got the Command working.
I added a TapGestureRecognizer
to my control with the touch effect. In the effect is used the click event from the viewOverlay: _viewOverlay.Click += _viewOverlay_Click;
and fire the command:
private void _viewOverlay_Click(object sender, EventArgs e) {
foreach(var recognizer in ((Xamarin.Forms.View)Element).GestureRecognizers) {
if(recognizer is TapGestureRecognizer gesture) {
if(gesture.Command != null) {
gesture.Command.Execute(gesture.CommandParameter);
}
}
}
}
My question is now: Can you remove the TouchCollector from the effect and implement the click event like i do? Then it should work in ScrollViews.
Hello. I thinking about new version with fixes all bugs. But haven't time for that...
@AlleSchonWeg Could you please share your whole solution?
@isness : Here are the effect for android:
using System;
using System.ComponentModel;
using Android.Content.Res;
using Android.Graphics.Drawables;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Color = Android.Graphics.Color;
using ListView = Android.Widget.ListView;
using ScrollView = Android.Widget.ScrollView;
using View = Android.Views.View;
using VisualTouchFeedbackRoutingEffect = xxxx.Droid.Effects.VisualTouchFeedbackRoutingEffect;
[assembly: ExportEffect(typeof(VisualTouchFeedbackRoutingEffect), nameof(xxxx.Effects.VisualTouchFeedbackRoutingEffect))]
namespace xxx.Droid.Effects {
class VisualTouchFeedbackRoutingEffect : PlatformEffect {
public bool IsDisposed => (Container as IVisualElementRenderer)?.Element == null;
public View View => Control ?? Container;
//Color _color;
//RippleDrawable _ripple;
FrameLayout _viewOverlay;
protected override void OnAttached() {
if(Control is ListView || Control is ScrollView) {
return;
}
View.Clickable = true;
View.LongClickable = true;
_viewOverlay = new FrameLayout(Container.Context) {
LayoutParameters = new ViewGroup.LayoutParams(-1, -1),
Clickable = false,
Focusable = false,
};
Container.LayoutChange += ViewOnLayoutChange;
_viewOverlay.Touch += _viewOverlay_Touch;
_viewOverlay.Click += _viewOverlay_Click;
_viewOverlay.Foreground = CreateRipple(Android.Resource.Attribute.SelectableItemBackground);
Container.AddView(_viewOverlay);
_viewOverlay.BringToFront();
}
private void _viewOverlay_Touch(object sender, View.TouchEventArgs e) {
e.Handled = false;
switch(e.Event.Action) {
case MotionEventActions.Down: {
if(IsDisposed || !(_viewOverlay.Foreground is RippleDrawable bc))
return;
bc.SetHotspot(e.Event.GetX(), e.Event.GetY());
break;
}
}
}
private void _viewOverlay_Click(object sender, EventArgs e) {
foreach(var recognizer in ((Xamarin.Forms.View)Element).GestureRecognizers) {
if(recognizer is TapGestureRecognizer gesture) {
if(gesture.Command != null) {
gesture.Command.Execute(gesture.CommandParameter);
}
}
}
}
RippleDrawable CreateRipple(int color) {
var attrs = new int[] { color };
var typedArray = Xamarin.Essentials.Platform.AppContext.ObtainStyledAttributes(attrs);
var drawableFromTheme = (RippleDrawable)typedArray.GetDrawable(0);
typedArray.Recycle();
return drawableFromTheme;
}
static ColorStateList GetPressedColorSelector(int pressedColor) {
return new ColorStateList(
new[] { new int[] { } },
new[] { pressedColor, });
}
protected override void OnDetached() {
if(IsDisposed)
return;
Container.RemoveView(_viewOverlay);
_viewOverlay.Click -= _viewOverlay_Click;
_viewOverlay.Touch -= _viewOverlay_Touch;
_viewOverlay.Pressed = false;
_viewOverlay.Foreground = null;
_viewOverlay.Dispose();
Container.LayoutChange -= ViewOnLayoutChange;
//_ripple?.Dispose();
}
private void ViewOnLayoutChange(object sender, View.LayoutChangeEventArgs layoutChangeEventArgs) {
var group = (ViewGroup)sender;
if(group == null || IsDisposed)
return;
_viewOverlay.Right = group.Width;
_viewOverlay.Bottom = group.Height;
}
}
}
And the effect in the shared project:
using System;
using Xamarin.Forms;
using XamEffects;
namespace xxx.Effects {
public class VisualTouchFeedbackRoutingEffect : RoutingEffect {
public VisualTouchFeedbackRoutingEffect() : base($"{nameof(xxx)}.{nameof(VisualTouchFeedbackRoutingEffect)}") {
}
}
public static class VisualTouchFeedbackEffect {
public static readonly BindableProperty ColorProperty =
BindableProperty.CreateAttached(
"Color",
typeof(Color),
typeof(VisualTouchFeedbackEffect),
Color.Default,
propertyChanged: PropertyChanged
);
public static void SetColor(BindableObject view, Color value) {
view.SetValue(ColorProperty, value);
}
public static Color GetColor(BindableObject view) {
return (Color)view.GetValue(ColorProperty);
}
private static void PropertyChanged(BindableObject bindable, object oldValue, object newValue) {
var color = GetColor(bindable);
//Für iOS das TouchEffect Nuget nutzen.
if(Device.RuntimePlatform == Device.iOS) {
TouchEffect.SetColor(bindable, color);
}
//Für Android abgewandelte Form des TouchEffect Nuget
else if(Device.RuntimePlatform == Device.Android) {
if(bindable is View view) {
SetColor(bindable, Color.Default);
EffectsConfig.SetChildrenInputTransparent(view, true);
view.Effects.Add(new VisualTouchFeedbackRoutingEffect());
}
}
else {
throw new NotSupportedException($"{Device.RuntimePlatform} is not supported!");
}
}
}
}
@AlleSchonWeg Hmm, I can't get it to work. Have you tried it on CollectionView? My collection view starts like this:
<CollectionView x:Name="HistoryView" ItemsSource="{Binding HistoryItems}" SelectionMode="None" Margin="0" IsGrouped="False"> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout Padding="0" Margin="0" > <Grid Padding="10,15,10,15" Margin="0" x:DataType="model:HistoryItem"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="3*"></ColumnDefinition> </Grid.ColumnDefinitions>
@isness I use the Sharpnado HorizontalListView. Not sure, if XF CollectionView works. Do you attach the effect like this: effects:VisualTouchFeedbackEffect.Color="color.red"
to your view ?
Yes, I attached it the way you described. Can't get it to work on collectionview. Maybe it doesn't work on latest XF version, I have no idea. The same bug exists on XamarinCommunityToolkit, though. It would be great to have a workaround for it: https://github.com/xamarin/XamarinCommunityToolkit/issues/1261