Can not override OnInterceptTouchEvent method in xamarin.android - xamarin

I want to override OnInterceptTouchEvent method. I am using listview and trying to implement gesture detecor on it but as I appiled click listeners on child view so on fling method return null. I figured out that this method can solve my problem but I am unable to override in my activity.
#Override
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
return true;
}
If there exist any other approach to fix this please let me know.

Activity is not a view class, so there is no OnInterceptTouchEvent method,
OnInterceptTouchEvent is a function of the ViewGroup class that intercepts touch events.
So you could customize the ListView , then override the OnInterceptTouchEvent method,like this :
class MyListView : ListView
{
public MyListView(Context context) : base(context)
{
}
public MyListView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public MyListView(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
}
public override bool OnInterceptTouchEvent(MotionEvent e)
{
return base.OnInterceptTouchEvent(e);
}
}

You are trying to override it the Java way when you should be using the C# equivalent.
Also, Intercept touch events are a part of the ViewGroup hierarchy and hence unavailable in your activity.
A ViewGroup is basically a class that holds a bunch of widgets together like a View holder.
Try pasting the below in your custom ListView and then use that ListView as your to implement a gesture detector
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return base.OnInterceptTouchEvent(ev);
}

Related

Get instance of rendered class Button (inside Xamarin.Forms) inside custom button renderer (Xamarin.Android)

I have the simple custom button in my code:
public class CustomButton : Button
{
public bool State { get; set; } = false;
}
and its renderer:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
public CustomButtonRenderer(Context context) : base(context) { }
ObjectAnimator objectAnimator;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var button = (Control as Android.Widget.Button);
(Control as Android.Widget.Button).Touch += Control_Touch;
// this don't works:
if (Control.State) Control.SetBackgroundColor(global::Android.Graphics.Color.LightGray);
}
}
}
And I want to get access to State property of CustomButton instance inside my CustomButtonRenderer class. But I can't, because the Control has type Android.Support.V7.Widget.AppCompatButton, absolutly unrelated to my CustomButton class.
Are there any ways to gain access to fields of rendered CustomButton object inside its renderer?
Control is the native control rendering your CustomButton. What you are looking for is the property Element, which represents your Xamarin.Forms CustomButton.
Within OnElementChanged it is available as e.NewElement.
if (e.NewElement is CustomButton customButton
&& customButton.State)
{
Control.SetBackgroundColor(global::Android.Graphics.Color.LightGray);
}

Removing back swipe gesture from page using xamarin forms

Is there a way i can disable the back swipe to previous page option for iOS on one single page of my project ?
You can achieve this by implementing a custom renderer and setting the right property for this. You can see a sample implementation underneath. The right property, in this case, is InteractivePopGestureRecognizer which you need to set to false.
Do this in the ViewWillAppear so the NavigationController is initialized.
using DisableSwipe.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ContentPage), typeof(NoBackSwipeRenderer))]
namespace DisableSwipe.iOS
{
public class NoBackSwipeRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (ViewController?.NavigationController != null)
ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = false;
}
}
}
#Symorp
You could do it like so:
public class YourCustomPageRenderer : PageRenderer
{
private YourCustomPage _yourCustomPage;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
_yourCustomPage = e.NewElement as YourCustomPage;
if (_yourCustomPage != null)
{
_yourCustomPage.PropertyChanged += YourCustomPagePropertyChangedEventHandler;
}
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
SetInteractivePopGestureRecognizerEnabled(isEnabled: false);
}
private void YourCustomPagePropertyChangedEventHandler(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == nameof(YourCustomPage.IsInteractivePopGestureRecognizerEnabled))
{
SetInteractivePopGestureRecognizerEnabled(_yourCustomPage.IsInteractivePopGestureRecognizerEnabled);
}
}
private void SetInteractivePopGestureRecognizerEnabled(bool isEnabled)
{
var interactivePopGestureRecognizer = ViewController?.NavigationController?.InteractivePopGestureRecognizer;
if (interactivePopGestureRecognizer != null)
{
//Prevents the back-swipe-gesture when the user wants to swipe a page away (from left edge of the screen)
interactivePopGestureRecognizer.Enabled = isEnabled;
}
}
}
public class YourCustomPage : ContentPage
{
/// <summary>
/// If you need it as bindable property, feel free to create a <see cref="BindableProperty"/>.
/// </summary>
public bool IsInteractivePopGestureRecognizerEnabled { get; set; }
}
Feel free to adjust to your needs! :-)
I omitted the export renderer attribute etc., just for simplicity.

Xamarin.Forms ListView with custom renderer selects more than one row after scroll is triggered

I needed to change the color of the selected item in ListView in my Xamarin.Forms application, so I implemented a custom rendered for ViewCell. This worked as required, untill I filled my list with so many elements, that it had to be scrolled. After scroll occurres on the list, it causes a weird bug, where more than one row gets selected (just as if scrolling the list, would change the scope where list looks for selected items and did not reset the selected item). My best guess is that I need to extend my renderer to count for the scrolling of ListView, but I have no idea how to do this. Has anyone encountered similar issues?
Custom Renderer:
[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace MyApp.Droid.PlatformSpecific.Renderers
{
public class ExtendedViewCellRenderer : ViewCellRenderer
{
private Android.Views.View _cellCore;
private Drawable _unselectedBackground;
private bool _selected;
protected override Android.Views.View GetCellCore(Cell item,
Android.Views.View convertView,
ViewGroup parent,
Context context)
{
_cellCore = base.GetCellCore(item, convertView, parent, context);
_selected = false;
_unselectedBackground = _cellCore.Background;
return _cellCore;
}
protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.OnCellPropertyChanged(sender, args);
if (args.PropertyName == "IsSelected")
{
_selected = !_selected;
if (_selected)
{
var extendedViewCell = sender as ExtendedViewCell;
_cellCore.SetBackgroundColor(extendedViewCell.SelectedBackgroundColor.ToAndroid());
}
else
{
_cellCore.SetBackground(_unselectedBackground);
}
}
}
}
}
Custom ViewCell:
public class ExtendedViewCell : ViewCell
{
public static readonly BindableProperty SelectedBackgroundColorProperty =
BindableProperty.Create("SelectedBackgroundColor",
typeof(Color),
typeof(ExtendedViewCell),
Color.Default);
public Color SelectedBackgroundColor
{
get { return (Color)GetValue(SelectedBackgroundColorProperty); }
set { SetValue(SelectedBackgroundColorProperty, value); }
}
}
Update:
I have managed to trace down the issue to beeing caused by different implementation of caching on newer Xamarin.Forms versions. Even the author of the above solution haven't managed to solve this out at this moment: Link

Navigate from Fragment to Activity MVVMCross

I am struggling to perform a navigation from a Fragment to an Activity at ViewModel level. I have an Activity with a DrawerLayout and this Activity has a FrameLayout to display different Fragments selected from the DrawerLayout. That navigation is perform by the ViewModel of this Activity and it is calling the ViewModel of each Fragment to display. In one of the Fragments I added a button binding a IMvxCommand method to perform the navigation from the Fragment to a new Activity and here is where I have the problem because when I click on the button nothing happens.
Find below my code.
ViewModel of Fragment
public class MainFrameViewModel : ContentViewModel
{
readonly IMvxNavigationService navigationService;
public MainFrameViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
this.navigationService = navigationService;
}
public IMvxCommand GoMoreInfo
{
get
{
IMvxCommand navigateCommand = new MvxCommand(() => navigationService.Navigate<MoreInfoViewModel>());
return navigateCommand;
}
}
}
Fragment code
[MvxFragmentPresentation(typeof(ContentViewModel), Resource.Id.frameLayout)]
[Register("mvvmdemo.droid.views.fragments.MainFrameFragment")]
public class MainFrameFragment : MvxFragment<MainFrameViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.MainFrame, container, false);
}
}
Activity to navigate
[MvxActivityPresentation]
[Activity(Label = "MoreInfoActivity")]
public class MoreInfoActivity : MvxAppCompatActivity<MoreInfoViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.MoreInfoLayout);
}
}
ContentViewModel is the ViewModel of the Activity containing the FrameLayout and the DrawerLayout.
Your bindings aren't working because you are using the default inflater, which knows nothing about MvvmCross bindings. You could solve this problem by using the MvvmCross inflater inside OnCreateView. Change return inflater.Inflate(Resource.Layout.MainFrame, container, false); call to return this.BindingInflate(Resource.Layout.MainFrame, null);
Also, you are ignoring the async part of the IMvxNavigationService. It would be an improvement to change from IMvxCommand to IMvxAsyncCommand and await or return the Task returned by IMvxNavigationService.Navigate()

How can I intercept all controller calls in an MVC application?

Is there a quick method for intercepting all controller calls in MVC-3?
For logging and testing purposes, I'd like to build a tool that can intercept all controller calls, and log which controller was called, with which message, at what time.
I can't remember where I got this from, but I was looking around for something similar a while back and found an article or something somewhere that contained this logging filter:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = string.Format("{0} controller: {1} action: {2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
To use it, just add it to the global filters in global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new LogActionFilter());
}
I'll have a look now to see if I can find the source.
Edit: Found it. It was from this question.
Depending on how big the site is already, you could create a class in the hierarchy between the framework's Controller class and your main controllers.
Something like
public class MyBaseController : Controller {
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
// your logging stuff here
base.OnActionExecuting(filtercontext);
}
}
Then the rest of your controllers can inherit from this, e.g.
public class HomeController : MyBaseController {
// action methods...
}
You can use your own controller factory and register it as well:
From: (many example on the net - insert logging where you want)
adapted from: http://www.keyvan.ms/custom-controller-factory-in-asp-net-mvc
using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
namespace IControllerFactorySample.ControllerFactories
{
public class YourControllerFactory : IControllerFactory
{
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
IController controller = Activator.CreateInstance(Type.GetType(controllerName)) as IController;
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
}
dont forget to register it in global.asax.cs
ControllerBuilder.Current.SetControllerFactory(
typeof(YourControllerFactory));
there is a routing debugger developed by Phil Haack
ASP.Net Routing Debugger

Resources