How can I get the cursor position inside Editor control?
Ive been looking for an answer but the best I could find was the Cursor class, but that doesnt seem to exist in xamarin.
You could custom a Editor,and use custom renderer to get the SelectionPosition of the EditText.
custom a FormEditor in your fomrs project:
public class FormEditor:Editor
{
public int SelectionPosition;
public EventHandler SelectChanageEvent { get; set; }
}
create AndroidEditor in your Android project:
class AndroidEditor : EditorRenderer, EditTextSelectChange
{
private Context mContext;
public AndroidEditor(Context context) : base(context)
{
mContext = context;
}
public void Change(int lastPos, int curPos)
{
((FormEditor)Element).SelectionPosition = curPos;
((FormEditor)Element).SelectChanageEvent.Invoke(this, null);
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
MyEditText myEditText = new MyEditText(mContext);
myEditText.SetEditTextSelectChange(this);
SetNativeControl(myEditText);
}
}
custom MyEditText in your Android project:
public class MyEditText : FormsEditText
{
private int mLastPos = 0;
private int mCurPos = 0;
private EditTextSelectChange editTextSelectChange;
public void SetEditTextSelectChange(EditTextSelectChange editTextSelectChange)
{
this.editTextSelectChange = editTextSelectChange;
}
public MyEditText(Context context) : base(context)
{
}
protected override void OnSelectionChanged(int selStart, int selEnd)
{
base.OnSelectionChanged(selStart, selEnd);
if (editTextSelectChange != null)
{
mCurPos = selEnd;
editTextSelectChange.Change(mLastPos, mCurPos);
mLastPos = mCurPos;
}
}
public interface EditTextSelectChange
{
void Change(int lastPos, int curPos);
}
}
then use in your page.xaml:
<local:FormEditor x:Name="editor" Placeholder="Hello"></local:FormEditor>
in your page.xaml.cs:
public YourPage()
{
InitializeComponent();
editor.SelectChanageEvent += SelectEvent;
}
private void SelectEvent(object sender, EventArgs e)
{
// you could get the Curson Position by editor.SelectionPosition
Console.WriteLine("curPos = {0}", editor.SelectionPosition);
}
Related
i'm trying to create a Popup page (using Rg.Plugin.Popup) that has to display a stream video got from an API. Everytime i navigate into this page, i want to display a different video, obtained from the API, based on parameters provided by the page I come from (i get them by a message sent by Messenger). It seems to work at first run but, when i close the Popup page, and i open it again the videoView is all black and in the output is written: Failed to get window format.
That's my code ( base on sample provided here: https://code.videolan.org/videolan/LibVLCSharp/tree/master/Samples/Forms):
The code-behind page:
public partial class WebcamVideoPopUpPage : PopupPage
{
public WebcamVideoPopUpPage()
{
var vm = App.Locator.WebCamVideoVM;
this.BindingContext = vm;
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
Messenger.Default.Send(new OnApperingVideoMessage());
}
private void VideoView_MediaPlayerChanged(object sender,
LibVLCSharp.Shared.MediaPlayerChangedEventArgs e)
{
Messenger.Default.Send(new OnVideoViewInitializedMessage());
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
}
videoView in xaml:
<shared:VideoView x:Name="VideoView"
MediaPlayer ="{Binding MediaPlayer}"
HorizontalOptions ="FillAndExpand"
VerticalOptions ="FillAndExpand"
MediaPlayerChanged ="VideoView_MediaPlayerChanged"/>
the ViewModel:
public class WebcamVideoViewModel : BaseViewModel
{
private LibVLC LibVLC { get; set; }
private bool IsLoaded { get; set; }
private bool IsVideoViewInitialized { get; set; }
private Media media { get; set; }
private MediaPlayer _mediaPlayer;
public MediaPlayer MediaPlayer
{
get { return _mediaPlayer; }
set
{
_mediaPlayer = value;
OnPropertyChanged();
}
}
public WebcamVideoViewModel(INavigationService navigationService, IApiManagerFactory apiFactory) : base(navigationService, apiFactory)
{
Messenger.Default.Register<InfoWebcamVideoMessage>(this, OnReceivedInfoWebcam);
Messenger.Default.Register<OnApperingVideoMessage>(this, OnAppearing);
Messenger.Default.Register<OnVideoViewInitializedMessage>(this, OnVideoViewInitialized);
Task.Run(Initialize);
}
private void Initialize()
{
Core.Initialize();
LibVLC = new LibVLC();
MediaPlayer = new MediaPlayer(LibVLC);
}
private async void OnReceivedInfoWebcam(InfoWebcamVideoMessage msg)
{
var response = await ApiManager.GetVideoWebcam(msg.Mpr, msg.Uuid);
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStreamAsync();
media = new Media(LibVLC, stream);
Play();
}
}
public void OnAppearing(OnApperingVideoMessage msg)
{
IsLoaded = true;
}
public void OnVideoViewInitialized(OnVideoViewInitializedMessage msg)
{
IsVideoViewInitialized = true;
}
private void Play()
{
if (IsLoaded && IsVideoViewInitialized)
{
MediaPlayer.Play(media);
}
}
}
I resolved my problem overriding OnAppering method:
protected override void OnDisappearing()
{
base.OnDisappearing();
VideoView.MediaPlayer.Stop();
VideoView.MediaPlayer = null;
}
I´m using a custom renderer to customize the height and color of my progress bar, but my progress bar gets blurred:
My CustomRenderer looks like this:
public class ColorProgressBarRenderer : ProgressBarRenderer
{
public ColorProgressBarRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ProgressBar> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (Control != null)
{
UpdateBarColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ColorProgressBar.BarColorProperty.PropertyName)
{
UpdateBarColor();
}
}
private void UpdateBarColor()
{
var element = Element as ColorProgressBar;
Control.ProgressTintList = Android.Content.Res.ColorStateList.ValueOf(element.BarColor.ToAndroid());
Control.ScaleY = 10f;
}
}
My CustomProgressBar looks like this:
public class ColorProgressBar : ProgressBar
{
//public static BindableProperty BarColorProperty = BindableProperty.Create<ColorProgressBar, Color>(p => p.BarColor, default(Color));
public static BindableProperty BarColorProperty = BindableProperty.Create(nameof(BarColor), typeof(Color), typeof(ColorProgressBar), default(Color));
public Color BarColor
{
get { return (Color)GetValue(BarColorProperty); }
set { SetValue(BarColorProperty, value); }
}
}
This only happens in Android, with my iOS Renderer all is working fine!
Why this is happen?
You can try this advanced progress bar.
https://www.nuget.org/packages/MultiColor.ProgressBar/
Documentation: https://github.com/udayaugustin/ProgressBar/blob/master/README.md
It supports multi color.
It has properties like
Height
Width
Corner Radius and etc
I'm trying to implement this code here in my project:
The differences are that instead of a ScrollView I'm using a RecyclerView and that instead of having my RecyclerView directly in the Activity I have inside a fragment that sets the listener I need.
The problem is that while the OnScrollChanged(int l, int t, int oldl, int oldt) gets actually called everytime I scroll, the parameters it obtains are always 0 and I don't understand why, so ScrollChangedTarget doesn't work as intended.
This is the custom RecyclerView:
public class NotifyingScrollRecyclerView : RecyclerView
{
private Activity activity;
private View headerView;
private View footerView;
public delegate void ScrollChangedHandler(int l, int t, int oldl, int oldt, EventArgs e);
public event ScrollChangedHandler scrollChanged;
public EventArgs e = null;
//Enabling all required constructors
public NotifyingScrollRecyclerView(Context context) : base(context)
{
}
public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public NotifyingScrollRecyclerView(IntPtr ptr, JniHandleOwnership ownership) : base(ptr, ownership)
{
}
public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
//This method attribute allows us to register the inbuilt OnScrollChanged event that fires when scrolling a ScrollView
[Android.Runtime.Register("onScrollChanged", "(IIII)V", "GetOnScrollChanged_IIIIHandler")]
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
{
base.OnScrollChanged(l, t, oldl, oldt);
scrollChanged(l, t, oldl, oldt, e);
}
}
//Set an event listener
public class ScrollViewChangedListener
{
private Activity activity;
private NotifyingScrollRecyclerView scrollView;
private Android.Support.V7.App.ActionBar actionBar;
private Drawable actionBarDrawable;
be changed
public ScrollViewChangedListener(Activity a, NotifyingScrollRecyclerView n)
{
n.scrollChanged += ScrollChangedTarget;
this.activity = a;
this.scrollView = n;
this.actionBar = ((UserPageActivity)a).SupportActionBar;
this.actionBarDrawable = a.GetDrawable(Resource.Drawable.actionbar_background);
this.actionBarDrawable.SetAlpha(0);
}
//Handle the changing of the scroll
public void ScrollChangedTarget(int l, int t, int oldl, int oldt, EventArgs e)
{
//You set the View you want to be your header as a header height, and then get it's height
int headerHeight = activity.FindViewById<ImageView>(Resource.Id.profilebanner).Height -
this.actionBar.Height;
float ratio = (float)Math.Min(Math.Max(t, 0), headerHeight) / headerHeight;
int newAlpha = (int)(ratio * 255);
this.actionBarDrawable.SetAlpha(newAlpha);
this.actionBar.SetBackgroundDrawable(this.actionBarDrawable);
}
}
This is the Activity which calls the fragment (it doesn't do anything in particular aside calling the fragment in this case):
[Activity(Label = "UserPageActivity")]
public class UserPageActivity : BaseActivity
{
protected override int LayoutResource => Resource.Layout.activity_user_page;
UserViewModel viewModel;
TextView username;
TextView usernameToolbar;
Button followButton;
ViewPager pager;
UserTabsAdapter adapter;
bool IsLoggedUser;
protected override void OnCreate(Bundle savedInstanceState)
{
IsLoggedUser = Intent.GetStringExtra("userId").Equals(LoginController.GetInstance().CurrentUser.Email);
base.OnCreate(savedInstanceState);
viewModel = new UserViewModel();
viewModel.UserLoaded += new UserViewModel.UserLoadedHandler(OnUserLoaded);
viewModel.LoadUserCommand.Execute(Intent.GetStringExtra("userId"));
username = FindViewById<TextView>(Resource.Id.profilename);
usernameToolbar = FindViewById<TextView>(Resource.Id.usernamePage);
followButton = FindViewById<Button>(Resource.Id.followButton);
username.Text = Intent.GetStringExtra("username");
usernameToolbar.Text = Intent.GetStringExtra("username");
adapter = new UserTabsAdapter(this, SupportFragmentManager);
pager = FindViewById<ViewPager>(Resource.Id.user_viewpager);
var tabs = FindViewById<TabLayout>(Resource.Id.tabs);
pager.Adapter = adapter;
tabs.SetupWithViewPager(pager);
pager.OffscreenPageLimit = 5;
pager.PageSelected += (sender, args) =>
{
var fragment = adapter.InstantiateItem(pager, args.Position) as IFragmentVisible;
fragment?.BecameVisible();
};
}
private void OnUserLoaded(bool loaded)
{
}
protected override void OnStart()
{
base.OnStart();
if (IsLoggedUser)
{
followButton.Visibility = ViewStates.Gone;
}
else
{
bool following;
if (LoginController.GetInstance().CurrentUser.FollowsUsers.ContainsKey(Intent.GetStringExtra("userId")))
{
followButton.Text = "Unfollow";
following = true;
}
else
{
followButton.Text = "Follow";
following = false;
}
followButton.Click += (object sender, EventArgs e) =>
{
followButton.Enabled = false;
if (following)
{
UserService service = ServiceLocator.Instance.Get<UserService>();
service.SetUser(LoginController.GetInstance().CurrentUser);
service.RemoveFollowsUserCommand.Execute(viewModel.LoadedUser.Email);
service.SetUser(viewModel.LoadedUser);
service.RemoveFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser.Email);
followButton.Text = "Follow";
following = false;
}
else
{
UserService service = ServiceLocator.Instance.Get<UserService>();
service.SetUser(LoginController.GetInstance().CurrentUser);
service.AddFollowsUserCommand.Execute(viewModel.LoadedUser);
service.SetUser(viewModel.LoadedUser);
service.AddFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser);
followButton.Text = "Unfollow";
following = true;
}
followButton.Enabled = true;
};
}
}
}
class UserTabsAdapter : FragmentStatePagerAdapter
{
string[] titles;
public override int Count => titles.Length;
public UserTabsAdapter(Context context, Android.Support.V4.App.FragmentManager fm) : base(fm)
{
titles = context.Resources.GetTextArray(Resource.Array.user_sections);
}
public override Java.Lang.ICharSequence GetPageTitleFormatted(int position) =>
new Java.Lang.String(titles[position]);
public override Android.Support.V4.App.Fragment GetItem(int position)
{
switch (position)
{
case 0: return UserContestsFragment.NewInstance();
case 1: return UserPartecipationsFragment.NewInstance();
case 2: return GlobalContestFragment.NewInstance();
case 3: return MessagesFragment.NewInstance();
}
return null;
}
public override int GetItemPosition(Java.Lang.Object frag) => PositionNone;
}
This is the fragment which setups the listener for the recyclerview:
public class UserContestsFragment : AbstractRefresherFadingToolbarFragment<Contest>
{
public static UserContestsFragment NewInstance() =>
new UserContestsFragment { Arguments = new Bundle() };
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ContestViewModel viewModel = new ContestViewModel();
base.ViewModel = viewModel;
base.LoadItemsCommand = viewModel.LoadAllByCreatorUserCommand;
base.param = Activity.Intent.GetStringExtra("userId");
base.adapter = new ContestsAdapter(Activity, viewModel);
var view = base.OnCreateView(inflater, container, savedInstanceState);
ScrollViewChangedListener listener = new ScrollViewChangedListener(Activity, recyclerView);
return view;
}
And this is the abstract fragment needed by that fragment which is in charge of setting up the layout:
public abstract class AbstractRefresherFadingToolbarFragment<T> : Android.Support.V4.App.Fragment, IFragmentVisible
{
public ICollectionViewModel<T> ViewModel;
public ICommand LoadItemsCommand;
public object param; //parametro per il LoadItemsCommand
public ItemsAdapter<T> adapter;
public SwipeRefreshLayout refresher;
public ProgressBar progress;
public NotifyingScrollRecyclerView recyclerView;
//LruCache cache = new LruCache((int)(Runtime.GetRuntime().MaxMemory() / 4));
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.fragment_fading_toolbar, container, false);
recyclerView = view.FindViewById<NotifyingScrollRecyclerView>(Resource.Id.recyclerView);
//ScrollViewChangedListener listener = new ScrollViewChangedListener((UserPageActivity)Activity, recyclerView);
//adapter.cache = cache;
recyclerView.HasFixedSize = true;
recyclerView.SetAdapter(adapter);
recyclerView.SetItemViewCacheSize(4);
//recyclerView.ChildViewAttachedToWindow += new EventHandler<RecyclerView.ChildViewAttachedToWindowEventArgs>(OnChildViewAttached);
//recyclerView.ChildViewDetachedFromWindow += new EventHandler<RecyclerView.ChildViewDetachedFromWindowEventArgs>(OnChildViewDetached);
refresher = view.FindViewById<SwipeRefreshLayout>(Resource.Id.refresher);
refresher.SetColorSchemeColors(Resource.Color.accent);
progress = view.FindViewById<ProgressBar>(Resource.Id.progressbar_loading);
progress.Visibility = ViewStates.Gone;
return view;
}
}
I didn't read your entire code but I looked at the site you linked in your question. The functionality of the hidden action bar you want to use is handled by CollapsingToolbarLayout in the support library. To know how to use it, go to Cheesesquare. It is a complete example of the support library widgets and can be built and run without any changes.
Edit:
RecyclerView has a method named AddOnScrollListener. Use it instead of the OnScrollChanged. If you use an inherited class from RecyclerView, you can call it in all of the constructors of that class.
I have a xamarin forms image, and when the user taps on the image I want to get the x,y coordinates of where the user tapped. Not the x,y coordinates of the view per se, but the coordinates within the image. I have this working on Android below. What would the iOS custom renderer be like?
Create a interface for touch event:
public interface IFloorplanImageController
{
void SendTouched();
}
Create a custom control for image:
public class FloorplanImage : Image, IFloorplanImageController
{
public event EventHandler Touched;
public void SendTouched()
{
Touched?.Invoke(this, EventArgs.Empty);
}
public Tuple<float, float> TouchedCoordinate
{
get { return (Tuple<float, float>)GetValue(TouchedCoordinateProperty); }
set { SetValue(TouchedCoordinateProperty, value); }
}
public static readonly BindableProperty TouchedCoordinateProperty =
BindableProperty.Create(
propertyName: "TouchedCoordinate",
returnType: typeof(Tuple<float, float>),
declaringType: typeof(FloorplanImage),
defaultValue: new Tuple<float, float>(0, 0),
propertyChanged: OnPropertyChanged);
public static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
}
}
Implement the custom renderer:
[assembly: ExportRenderer(typeof(FloorplanImage), typeof(FloorplanImageRenderer))]
namespace EmployeeApp.Droid.Platform
{
public class FloorplanImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control != null)
{
Control.Clickable = true;
Control.SetOnTouchListener(ImageTouchListener.Instance.Value);
Control.SetTag(Control.Id, new JavaObjectWrapper<FloorplanImage> { Obj = Element as FloorplanImage });
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (Control != null)
{
Control.SetOnTouchListener(null);
}
}
base.Dispose(disposing);
}
private class ImageTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public static readonly Lazy<ImageTouchListener> Instance = new Lazy<ImageTouchListener>(
() => new ImageTouchListener());
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
var obj = v.GetTag(v.Id) as JavaObjectWrapper<FloorplanImage>;
var element = obj.Obj;
var controller = element as IFloorplanImageController;
if (e.Action == Android.Views.MotionEventActions.Down)
{
var x = e.GetX();
var y = e.GetY();
element.TouchedCoordinate = new Tuple<float, float>(x, y);
controller?.SendTouched();
}
else if (e.Action == Android.Views.MotionEventActions.Up)
{
}
return false;
}
}
}
public class JavaObjectWrapper<T> : Java.Lang.Object
{
public T Obj { get; set; }
}
}
Use this control like this:
<local:FloorplanImage HeightRequest="300" x:Name="image" WidthRequest="300"
Aspect="AspectFit" Touched="image_Touched" />
code behind:
private void image_Touched(object sender, EventArgs e)
{
var cor = image.TouchedCoordinate;
}
I found this Customer success example on github that does exactly this.
[assembly: ExportRenderer(typeof(CustomImage), typeof(CustomImageRenderer))]
namespace FormsImageTapGesture.iOS
{
public class CustomImageRenderer : ImageRenderer
{
#region properties & fields
// ---------------------------------------------------------------------------
//
// PROPERTIES & FIELDS
//
// ---------------------------------------------------------------------------
private UIImageView nativeElement;
private CustomImage formsElement;
#endregion
#region methods
// ---------------------------------------------------------------------------
//
// METHODS
//
// ---------------------------------------------------------------------------
//
// Set up the custom renderer. In this case, that means set up the gesture
// recognizer.
//
protected override void OnElementChanged(ElementChangedEventArgs<Image> e) {
base.OnElementChanged (e);
if (e.NewElement != null) {
// Grab the Xamarin.Forms control (not native)
formsElement = e.NewElement as CustomImage;
// Grab the native representation of the Xamarin.Forms control
nativeElement = Control as UIImageView;
// Set up a tap gesture recognizer on the native control
nativeElement.UserInteractionEnabled = true;
UITapGestureRecognizer tgr = new UITapGestureRecognizer (TapHandler);
nativeElement.AddGestureRecognizer (tgr);
}
}
//
// Respond to taps.
//
public void TapHandler(UITapGestureRecognizer tgr) {
CGPoint touchPoint = tgr.LocationInView (nativeElement);
formsElement.OnTapEvent ((int)touchPoint.X, (int)touchPoint.Y);
}
#endregion
}
}
In Xamarin Android how can we extend the ScrollView in order to use the protected OnScrollChanged event?
Specifically,
How do we extend the ScrollView to allow an EventHandler to be registered to the OnScrollChanged event?
What other methods of ScrollView need to be implemented in a class that extends ScrollView?
Reasons:
The Android ScrollView does not have a way to listen for a scroll event.
There have been various questions regarding how to extend ScrollView in native Android Java, however, there is not a question and answer addressing how to apply this to Xamarin.
In order to extend ScrollView in this way we should implement 3 constructors
public UsefulScrollView(Context context)
public UsefulScrollView(Context context, IAttributeSet attrs)
public UsefulScrollView(Context context, IAttributeSet attrs, int defStyle)
We also need to override the OnDraw method
protected override void OnDraw(Android.Graphics.Canvas canvas)
To achieve the functionality of an event that we can respond to when the user scrolls we need to override the OnScrollChanged method.
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
There are multiple ways to allow event listening and handling, but in order to be consistent with Xamarin we can add a public EventHandler property to our class.
public EventHandler<T> ScrollEventHandler { get; set; }
We will want to pass along the values from OnScrollChanged to the EventHandler, so let's extend EventArgs
public class UsefulScrollEventArgs : EventArgs{
public int l { get; set; }
public int t { get; set; }
public int oldl { get; set; }
public int oldt { get; set; }
}
Finally, don't forget to initialize our handler in each of our constructors
ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};
Put it all together and it might look something like this
Extended EventArgs class
public class UsefulScrollEventArgs : EventArgs{
public int l { get; set; }
public int t { get; set; }
public int oldl { get; set; }
public int oldt { get; set; }
}
Extended ScrollView class
public class UsefulScrollView : ScrollView
{
public EventHandler<UsefulScrollEventArgs> ScrollEventHandler { get; set; }
public UsefulScrollView (Context context)
: base(context)
{
ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};
}
public UsefulScrollView (Context context, IAttributeSet attrs)
: base(context, attrs) {
ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};
}
public UsefulScrollView(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle) {
ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};
}
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
{
ScrollEventHandler (this,
new UsefulScrollEventArgs ()
{l=l,t=t,oldl=oldl,oldt=oldt});
base.OnScrollChanged (l, t, oldl, oldt);
}
protected override void OnDraw(Android.Graphics.Canvas canvas)
{
}
}
This Q&A was helpful in figuring this problem out: Scrollview listener is not working in Xamarin for Android?
I extended ScrollView but used EventHandlers. One for ScrollChanged, one for ReachedBottom.
This goes in your extended class:
public delegate void ScrollChangedEventHandler(object sender, EventArgs e);
public event ScrollChangedEventHandler ScrollChanged;
public delegate void ScrollReachedBottomEventHandler(object sender, EventArgs e);
public event ScrollReachedBottomEventHandler ScrollReachedBottom;
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
{
base.OnScrollChanged(l, t, oldl, oldt);
if (ScrollChanged != null)
{
ScrollChanged.Invoke(this, new EventArgs());
}
// Grab the last child placed in the ScrollView, we need it to determinate the bottom position.
var view = GetChildAt(ChildCount - 1);
// Calculate the scrolldiff
var diff = (view.Bottom - (Height + ScrollY));
// if diff is zero, then the bottom has been reached
if (diff == 0 && ScrollReachedBottom != null)
{
ScrollReachedBottom.Invoke(this, new EventArgs());
}
}
You would then simply call
myScrollView.ScrollChanged += delegate { /* your code here */ }
and
myScrollView.ScrollReachedBottom += delegate { /* your code here */ }
enjoy.