using System;
using Android.Views;
using Android.Content;
using Android.Widget;
using Android.Graphics.Drawables;
namespace Shared
{
//已经全面检查了代码
///
/// 位置布局
///
public class HorizontalPages : ViewGroup
{
///
/// 视图高度
///
/// The height.
public override int Height
{
get
{
return base.Height;
}
set
{
base.Height = value;
if (!IsCanRefresh)
{
return;
}
var layoutParameters = realViewGroup.LayoutParameters;
layoutParameters.Height = Height;
realViewGroup.LayoutParameters = layoutParameters;
}
}
///
/// 视图宽度
///
/// The width.
public override int Width
{
get
{
return base.Width;
}
set
{
base.Width = value;
if (!IsCanRefresh)
{
return;
}
var layoutParameters = realViewGroup.LayoutParameters;
layoutParameters.Width = Width;
realViewGroup.LayoutParameters = layoutParameters;
}
}
///
/// 页面变化事件
///
public Action PageChange;
///
/// 构造函数
///
public HorizontalPages()
{
viewGroup = new AndroidFrameLayout(Application.Activity, null);
realViewGroup = new InnerHorizontalPages(Application.Activity, this) { };
viewGroup.AddView(realViewGroup);
}
int pageIndex;
///
/// 设置或者获取当前的界面索引
///
/// The index of the page.
public int PageIndex
{
get
{
return (realViewGroup as InnerHorizontalPages).CurrentScreen;
}
set
{
if (value < 0 || ChildrenCount < value)
{
return;
}
pageIndex = value;
if (!IsCanRefresh)
{
return;
}
(realViewGroup as InnerHorizontalPages).SetSelection(value);
}
}
///
/// 是否允许滑动
///
/// true if scroll enabled; otherwise, false.
public bool ScrollEnabled
{
get
{
return (realViewGroup as InnerHorizontalPages).ScrollEnabled;
}
set
{
(realViewGroup as InnerHorizontalPages).ScrollEnabled = value;
}
}
///
/// 增加子控件
///
/// View.
public override void AddChidren(View view)
{
view.Parent = this;
viewList.Add(view);
realViewGroup.AddView(view.AndroidView);
if (!IsCanRefresh)
{
return;
}
view.Refresh();
if (view is ViewGroup)
{
var tempViewGroup = (ViewGroup)view;
for (int i = 0; i < tempViewGroup.ChildrenCount; i++)
{
tempViewGroup.GetChildren(i).Refresh();
}
}
}
///
/// 移除当前控件
///
/// View.
internal override void Remove(View view)
{
if (view == null)
{
return;
}
var index = viewList.FindIndex(obj => obj == view);
if (index < 0)
{
return;
}
viewList.Remove(view);
(realViewGroup as InnerHorizontalPages).RemoveViewAt(index);
view.Parent = null;
}
///
/// 移除所有的控件
///
public override void RemoveAll()
{
while (0 < viewList.Count)
{
GetChildren(0)?.RemoveFromParent();
}
(realViewGroup as InnerHorizontalPages).SetSelection(0);
}
///
/// 根据索引移除控件
///
/// Index.
public override void RemoveAt(int index)
{
if (viewList.Count - 1 < index || index < 0)
{
return;
}
GetChildren(index)?.RemoveFromParent();
}
///
/// page的外边距
///
public int RowPadding
{
get
{
return (realViewGroup as InnerHorizontalPages).Padding;
}
set
{
(realViewGroup as InnerHorizontalPages).Padding = value;
}
}
///
/// 两个Page之间的距离
///
public int PagePadding
{
get
{
return (realViewGroup as InnerHorizontalPages).Padding - (realViewGroup as InnerHorizontalPages).PagePadding;
}
set
{
(realViewGroup as InnerHorizontalPages).PagePadding = (realViewGroup as InnerHorizontalPages).Padding - value;
}
}
}
public class InnerHorizontalPages : Android.Widget.FrameLayout
{
public bool ScrollEnabled = true;
static int snapVelocity = 1000;
static int invalidScreen = -1;
static int touchStateRest = 0;
static int touchStateScrolling = 1;
///
/// 界面之间的边距
///
public int Padding = 100;
public int PagePadding = 50;
Scroller scroller;
VelocityTracker mVelocityTracker = VelocityTracker.Obtain();
int touchState = touchStateRest;
float lastMotionX;
float lastMotionY;
int mTouchSlop;
int mMaximumVelocity;
internal int CurrentScreen;
public override void RemoveViewAt(int index)
{
if (index == ChildCount - 1) {
CurrentScreen--;
if (CurrentScreen < 0) {
CurrentScreen = 0;
}
}
base.RemoveViewAt(index);
//刷新
snapToDestination();
}
Shared.HorizontalPages horizontalPages;
public InnerHorizontalPages(Context context, Shared.HorizontalPages view) : base(context)
{
horizontalPages = view;
init();
}
///
/// 重写这个是为了方便部局,在每个界面之间都多加了一个控件包住
///
///
public override void AddView(Android.Views.View child)
{
var innerView = new Android.Widget.FrameLayout(Shared.Application.Activity);
innerView.SetPadding(Padding, 0, Padding, 0);
if (1 <= ChildCount)
{
innerView.SetX(LayoutParameters.Width * ChildCount - Padding - PagePadding);
}
base.AddView(innerView, new Android.Widget.FrameLayout.LayoutParams(LayoutParameters.Width, LayoutParameters.Height) { });
innerView.AddView(child);
}
void init()
{
scroller = new Scroller(Context);
var configuration = ViewConfiguration.Get(Context);
//最小的滑动距离
mTouchSlop = configuration.ScaledTouchSlop;
mMaximumVelocity = configuration.ScaledMaximumFlingVelocity;
}
public override void RequestDisallowInterceptTouchEvent(bool disallowIntercept)
{
base.RequestDisallowInterceptTouchEvent(disallowIntercept);
disallowInterceptTouchEvent = disallowIntercept;
}
bool disallowInterceptTouchEvent;
bool isFirst;
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
if (disallowInterceptTouchEvent)
{
return false;
}
System.Console.WriteLine($"PageLayout->OnInterceptTouchEvent:{Height} {ev.Action}");
//如果没有子控件或者不允许滑动就返回,不处理当前事件
if (ChildCount == 0 || !ScrollEnabled)
return false;
mVelocityTracker.AddMovement(ev);
var x = ev.GetX();
var y = ev.GetY();
switch (ev.Action)
{
//每次点击都执行这里
case MotionEventActions.Down:
if (!scroller.IsFinished)
{
//如果还在滑动,但是用户重新点击了,就让控件马上停止
scroller.AbortAnimation();
snapToDestination(false);
}
//记录点击的最新X坐标
lastMotionX = x;
lastMotionY = y;
touchState = scroller.IsFinished ? touchStateRest : touchStateScrolling;
break;
//有子控件时执行这里
case MotionEventActions.Move:
var deltaX = (int)(lastMotionX - x);
var deltaY = (int)(lastMotionY - y);
//当前滑动是否已经超出了设定值
var xMoved = Math.Abs(deltaX) > mTouchSlop;
System.Console.WriteLine($"PageLayout->xMoved22:{xMoved } {deltaX}");
if (xMoved && Math.Abs(deltaY) < Math.Abs(deltaX))
{
if (!isFirst)
{
touchState = touchStateScrolling;
}
isFirst = false;
System.Console.WriteLine($"PageLayout->xMoved33:{xMoved } {deltaX}");
}
if (touchState == touchStateScrolling)
{
//记录最新的坐标,因为接下来控件已经滑动了
lastMotionX = x;
System.Console.WriteLine($"PageLayout->lastMotionX:{lastMotionX } {touchState}");
//当前事件自己处理了,子控制不需要处理当前这事件
return true;
}
break;
case MotionEventActions.Cancel:
touchState = touchStateRest;
mVelocityTracker.Clear();
break;
}
return false;
}
public override bool OnTouchEvent(MotionEvent e)
{
if (disallowInterceptTouchEvent)
{
return false;
}
System.Console.WriteLine($"PageLayout->OnTouchEvent:{Height} {e.Action}");
if (ChildCount == 0 || !ScrollEnabled)
return false;
mVelocityTracker.AddMovement(e);
var x = e.GetX();
var y = e.GetY();
switch (e.Action)
{
case MotionEventActions.Move:
var deltaX = (int)(lastMotionX - x);
var deltaY = (int)(lastMotionY - y);
//当前滑动是否已经超出了设定值
var xMoved = Math.Abs(deltaX) > mTouchSlop;
System.Console.WriteLine($"PageLayout->xMoved:{xMoved } {deltaX}");
if (xMoved && Math.Abs(deltaY) < Math.Abs(deltaX))
{
if (!isFirst)
{
touchState = touchStateScrolling;
}
isFirst = false;
}
if (touchState == touchStateScrolling)
{
lastMotionX = x;
return true;
}
break;
case MotionEventActions.Up:
if (touchState == touchStateScrolling)
{
mVelocityTracker.ComputeCurrentVelocity(1000, mMaximumVelocity);
var velocityX = mVelocityTracker.XVelocity;
if (velocityX > snapVelocity && CurrentScreen > 0)
{
// 显示左边界面
snapToScreen(CurrentScreen - 1);
}
else if (velocityX < -snapVelocity && CurrentScreen < ChildCount - 1)
{
// 显示右边界面
snapToScreen(CurrentScreen + 1);
}
}
touchState = touchStateRest;
mVelocityTracker.Clear();
break;
case MotionEventActions.Cancel:
touchState = touchStateRest;
mVelocityTracker.Clear();
break;
}
return true;
}
///
/// 滑动到指定的界面
///
void snapToDestination(bool isDelay = true)
{
var whichScreen = (ScrollX + (MeasuredWidth / 2)) / MeasuredWidth;
snapToScreen(whichScreen, isDelay);
}
///
/// 滑动到指定界面
///
/// Which screen.
void snapToScreen(int whichScreen, bool isDelay = true)
{
if (!scroller.IsFinished)
{
scroller.AbortAnimation();
}
var beforePosition = CurrentScreen;
whichScreen = Math.Max(0, Math.Min(whichScreen, ChildCount - 1));
CurrentScreen = whichScreen;
//还原这个控件之前的位置
var newX = CurrentScreen * MeasuredWidth;
var delta = newX - ScrollX;
scroller.StartScroll(ScrollX, 0, delta, 0, isDelay ? 800 : 0);
Invalidate();
refreshPage();
if (beforePosition != CurrentScreen)
{
horizontalPages.PageChange?.Invoke(horizontalPages, CurrentScreen);
}
}
void refreshPage()
{
GetChildAt(CurrentScreen).SetX(LayoutParameters.Width * CurrentScreen);
if (0 <= CurrentScreen - 1)
{
//让上一个界面向右出来一点
GetChildAt(CurrentScreen - 1).SetX(LayoutParameters.Width * (CurrentScreen - 1) + Padding + PagePadding);
}
if (CurrentScreen + 1 <= ChildCount - 1)
{
//让下一个界面向左出来一点
GetChildAt(CurrentScreen + 1).SetX(LayoutParameters.Width * (CurrentScreen + 1) - Padding - PagePadding);
}
}
public override void ComputeScroll()
{
if (scroller.ComputeScrollOffset())
{
ScrollTo(scroller.CurrX, scroller.CurrY);
PostInvalidate();
}
else if (CurrentScreen != invalidScreen)
{
//mCurrentScreen = Math.Max(0, Math.Min(mNextScreen, ChildCount - 1));
//mNextScreen = invalidScreen;
//postViewSwitched(mLastScrollDirection);
}
}
public void SetSelection(int position)
{
if (position < 0)
{
return;
}
scroller.ForceFinished(true);
position = Math.Min(position, ChildCount - 1);
snapToScreen(position, false);
}
public override bool DispatchTouchEvent(MotionEvent e)
{
//System.Console.WriteLine($"PageLayout->DispatchTouchEvent:{Height} {e.Action}");
if (e.Action == MotionEventActions.Down)
{
//还原中断
RequestDisallowInterceptTouchEvent(false);
isFirst = true;
}
return base.DispatchTouchEvent(e);
}
}
}