Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TouchEffect and scrolling (Android) #52

Open
AlleSchonWeg opened this issue Jun 1, 2021 · 7 comments
Open

TouchEffect and scrolling (Android) #52

AlleSchonWeg opened this issue Jun 1, 2021 · 7 comments

Comments

@AlleSchonWeg
Copy link

Hi,
i have the same problem as described in this post: #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?

@AlleSchonWeg
Copy link
Author

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.

@mrxten
Copy link
Owner

mrxten commented Jun 2, 2021

Hello. I thinking about new version with fixes all bugs. But haven't time for that...

@isness
Copy link

isness commented Aug 9, 2021

@AlleSchonWeg Could you please share your whole solution?

@AlleSchonWeg
Copy link
Author

@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!");
			}
		}
	}
}

@isness
Copy link

isness commented Aug 10, 2021

@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>

@AlleSchonWeg
Copy link
Author

@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 ?

@isness
Copy link

isness commented Aug 17, 2021

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: xamarin/XamarinCommunityToolkit#1261

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants