Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/CustomLayouts/Controls/CarouselLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public enum IndicatorStyleEnum

int _selectedIndex;

public CarouselLayout ()
public CarouselLayout (ScrollOrientation scrollOrientation = ScrollOrientation.Horizontal)
{
Orientation = ScrollOrientation.Horizontal;
Orientation = scrollOrientation;

_stack = new StackLayout {
Orientation = StackOrientation.Horizontal,
Orientation = Orientation == ScrollOrientation.Horizontal ? StackOrientation.Horizontal : StackOrientation.Vertical,
Spacing = 0
};

Expand Down Expand Up @@ -55,7 +55,13 @@ protected override void LayoutChildren (double x, double y, double width, double
if (_layingOutChildren) return;

_layingOutChildren = true;
foreach (var child in Children) child.WidthRequest = width;
foreach (var child in Children)
{
child.WidthRequest = width;

if (Orientation == ScrollOrientation.Vertical)
child.HeightRequest = height;
}
_layingOutChildren = false;
}

Expand Down
11 changes: 8 additions & 3 deletions src/CustomLayouts/HomePage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ public class HomePage : ContentPage

SwitcherPageViewModel viewModel;

public HomePage(CarouselLayout.IndicatorStyleEnum indicatorStyle)
ScrollOrientation _scrollOrienation;

public HomePage(CarouselLayout.IndicatorStyleEnum indicatorStyle, ScrollOrientation scrollOrientation)
{
_indicatorStyle = indicatorStyle;

_scrollOrienation = scrollOrientation;

viewModel = new SwitcherPageViewModel();
BindingContext = viewModel;

Expand All @@ -44,7 +48,7 @@ public HomePage(CarouselLayout.IndicatorStyleEnum indicatorStyle)
Constraint.RelativeToParent ((parent) => { return parent.X; }),
Constraint.RelativeToParent ((parent) => { return parent.Y; }),
Constraint.RelativeToParent ((parent) => { return parent.Width; }),
Constraint.RelativeToParent ((parent) => { return parent.Height/2; })
Constraint.RelativeToParent((parent) => { return _scrollOrienation == ScrollOrientation.Vertical ? parent.Height : parent.Height / 2; })
);

relativeLayout.Children.Add (dots,
Expand Down Expand Up @@ -86,7 +90,8 @@ public HomePage(CarouselLayout.IndicatorStyleEnum indicatorStyle)

CarouselLayout CreatePagesCarousel ()
{
var carousel = new CarouselLayout {
var carousel = new CarouselLayout(_scrollOrienation)
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
IndicatorStyle = _indicatorStyle,
Expand Down
19 changes: 15 additions & 4 deletions src/CustomLayouts/SwitcherPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,34 @@ namespace CustomLayouts
{
public class SwitcherPage : ContentPage
{
public SwitchCell scrollOrientationSwitch;

public SwitcherPage()
{
Title = "Pager Layout w/ Indicators";

var none = new Button {
HorizontalOptions = LayoutOptions.Center,
Text = "No pager indicator",
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.None)))
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.None, scrollOrientationSwitch.On ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical)))
};
var dots = new Button {
HorizontalOptions = LayoutOptions.Center,
Text = "Dots",
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.Dots)))
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.Dots, scrollOrientationSwitch.On ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical)))
};
var tabs = new Button {
HorizontalOptions = LayoutOptions.Center,
Text = "Tabs",
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.Tabs)))
Command = new Command((obj) => Navigation.PushAsync(new HomePage(CarouselLayout.IndicatorStyleEnum.Tabs, scrollOrientationSwitch.On ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical)))
};
scrollOrientationSwitch = new SwitchCell() { Text = "Vertical/Horizontal", On = true };
var table = new TableView();
table.Intent = TableIntent.Settings;
table.Root = new TableRoot() {
new TableSection("Orientation") {
scrollOrientationSwitch
}
};
Content = new StackLayout {
Orientation = StackOrientation.Vertical,
Expand All @@ -32,7 +42,8 @@ public SwitcherPage()
Children = {
none,
dots,
tabs
tabs,
table
}
};
}
Expand Down
98 changes: 72 additions & 26 deletions src/Droid/Renderers/CarouselLayoutRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,74 @@
namespace CustomLayouts.Droid.Renderers
{
public class CarouselLayoutRenderer : ScrollViewRenderer {
int _prevScrollX;
int _deltaX;
int _prevScroll;
int _delta;
bool _motionDown;
Timer _deltaXResetTimer;
Timer _deltaResetTimer;
Timer _scrollStopTimer;
Android.Widget.ScrollView _verticalScrollView;
HorizontalScrollView _scrollView;
bool _isVertical;

protected override void OnElementChanged (VisualElementChangedEventArgs e)
{
base.OnElementChanged (e);
if(e.NewElement == null) return;

_deltaXResetTimer = new Timer(100) { AutoReset = false };
_deltaXResetTimer.Elapsed += (object sender, ElapsedEventArgs args) => _deltaX = 0;
_deltaResetTimer = new Timer(100) { AutoReset = false };
_deltaResetTimer.Elapsed += (object sender, ElapsedEventArgs args) => _delta = 0;

_scrollStopTimer = new Timer (200) { AutoReset = false };
_scrollStopTimer.Elapsed += (object sender, ElapsedEventArgs args2) => UpdateSelectedIndex ();

_isVertical = (((CarouselLayout)Element).Orientation == ScrollOrientation.Vertical);

e.NewElement.PropertyChanged += ElementPropertyChanged;
}

void ElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "Renderer") {
_scrollView = (HorizontalScrollView)typeof(ScrollViewRenderer)
.GetField ("hScrollView", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue (this);

_scrollView.HorizontalScrollBarEnabled = false;
_scrollView.Touch += HScrollViewTouch;
if (_isVertical)
{
_verticalScrollView = this;
_verticalScrollView.Touch += ScrollViewTouch;
}
else
{
_scrollView = (HorizontalScrollView)typeof(ScrollViewRenderer)
.GetField("hScrollView", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(this);

_scrollView.HorizontalScrollBarEnabled = false;
_scrollView.Touch += ScrollViewTouch;
}
}
if (e.PropertyName == CarouselLayout.SelectedIndexProperty.PropertyName && !_motionDown) {
ScrollToIndex (((CarouselLayout)this.Element).SelectedIndex);
}
}

void HScrollViewTouch (object sender, TouchEventArgs e)
void ScrollViewTouch (object sender, TouchEventArgs e)
{
e.Handled = false;

switch (e.Event.Action) {
case MotionEventActions.Move:
_deltaXResetTimer.Stop ();
_deltaX = _scrollView.ScrollX - _prevScrollX;
_prevScrollX = _scrollView.ScrollX;
_deltaResetTimer.Stop();
if (_isVertical)
{
_delta = _verticalScrollView.ScrollY - _prevScroll;
_prevScroll = _verticalScrollView.ScrollY;
}
else
{
_delta = _scrollView.ScrollX - _prevScroll;
_prevScroll = _scrollView.ScrollX;
}

UpdateSelectedIndex ();

_deltaXResetTimer.Start ();
_deltaResetTimer.Start ();
break;
case MotionEventActions.Down:
_motionDown = true;
Expand All @@ -77,29 +97,52 @@ void HScrollViewTouch (object sender, TouchEventArgs e)
}

void UpdateSelectedIndex () {
var center = _scrollView.ScrollX + (_scrollView.Width / 2);
var carouselLayout = (CarouselLayout)this.Element;
carouselLayout.SelectedIndex = (center / _scrollView.Width);
if (_isVertical)
{
var center = _verticalScrollView.ScrollY + (_verticalScrollView.Height / 2);
carouselLayout.SelectedIndex = (center / _verticalScrollView.Height);
}
else
{
var center = _scrollView.ScrollX + (_scrollView.Width / 2);
carouselLayout.SelectedIndex = (center / _scrollView.Width);
}
}

void SnapScroll ()
{
var roughIndex = (float)_scrollView.ScrollX / _scrollView.Width;
var roughIndex = 0.0;

if (_isVertical)
roughIndex = (float)_verticalScrollView.ScrollY / _verticalScrollView.Height;
else
roughIndex = (float)_scrollView.ScrollX / _scrollView.Width;

var targetIndex =
_deltaX < 0 ? Math.Floor (roughIndex)
: _deltaX > 0 ? Math.Ceil (roughIndex)
_delta < 0 ? Math.Floor (roughIndex)
: _delta > 0 ? Math.Ceil (roughIndex)
: Math.Round (roughIndex);

ScrollToIndex ((int)targetIndex);
}

void ScrollToIndex (int targetIndex)
{
var targetX = targetIndex * _scrollView.Width;
_scrollView.Post (new Runnable (() => {
_scrollView.SmoothScrollTo(targetX, 0);
}));
if (_isVertical)
{
var target = targetIndex * _verticalScrollView.Height;
_verticalScrollView.Post(new Runnable(() => {
_verticalScrollView.SmoothScrollTo(0, target);
}));
}
else
{
var target = targetIndex * _scrollView.Width;
_scrollView.Post(new Runnable(() => {
_scrollView.SmoothScrollTo(target, 0);
}));
}
}

bool _initialized = false;
Expand All @@ -109,7 +152,10 @@ public override void Draw (Canvas canvas)
if (_initialized) return;
_initialized = true;
var carouselLayout = (CarouselLayout)this.Element;
_scrollView.ScrollTo (carouselLayout.SelectedIndex * Width, 0);
if (_isVertical)
_verticalScrollView.ScrollTo(0, carouselLayout.SelectedIndex * Height);
else
_scrollView.ScrollTo (carouselLayout.SelectedIndex * Width, 0);
}

protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
Expand Down