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; } } //InnerHorizontalPages (realViewGroup as InnerHorizontalPages); /// /// 页面变化事件 /// public Action PageChange; /// /// 开始滑动事件 /// public Action StartScrollAction; /// /// 结束滑动事件 /// public Action EndScrollAction; ///// ///// 没有更多提示Action,已经是最后一个 ///// //public Action NoMoreAction; /// /// 构造函数 /// public HorizontalPages() { viewGroup = new AndroidFrameLayout(Application.Activity, null); realViewGroup = new InnerHorizontalPages(Application.Activity, this) { }; //realViewGroup = InnerHorizontalPages; viewGroup.AddView(realViewGroup); } ImageView LeftImageView; ImageView RightImageView; /// /// 是否设置了虚假背景View 设置了才显示 /// public bool IsSetLeftAndRightImageView = false; /// /// 设置控制最左和最右2边,填充的假背景View,不设置不显示,需要在AddChidren 前调用该方法 /// /// /// public void SetLeftAndRightImageView(ImageView mLeftImageView, ImageView mRightImageView) { if (mLeftImageView == null) return; if (mRightImageView == null) return; //设置之前先清空一次 (realViewGroup as InnerHorizontalPages).RemoveAllViews(); LeftImageView = mLeftImageView; (realViewGroup as InnerHorizontalPages).AddView(LeftImageView.AndroidView); LeftImageView.Parent = this; LeftImageView.Refresh(); RightImageView = mRightImageView; (realViewGroup as InnerHorizontalPages).AddView(RightImageView.AndroidView); RightImageView.Parent = this; RightImageView.Refresh(); IsSetLeftAndRightImageView = true; } 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 as InnerHorizontalPages).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; base.Remove(view); (realViewGroup as InnerHorizontalPages).ReLocation(); if (PageIndex > ChildrenCount - 1) { PageIndex = ChildrenCount - 1; } } /// /// 移除所有的控件 /// public override void RemoveAll() { //while (0 < viewList.Count) //{ // GetChildren(0)?.RemoveFromParent(); //} //(realViewGroup as InnerHorizontalPages).SetSelection(0); base.RemoveAll(); (realViewGroup as InnerHorizontalPages).ReLocation(); PageIndex = 0; } /// /// 根据索引移除控件 /// /// Index. public override void RemoveAt(int index) { if (viewList.Count - 1 < index || index < 0) { return; } Remove(viewList[index]); } /// /// 界面之间的边距 /// public int JMBJ { get { return (realViewGroup as InnerHorizontalPages).JMBJ; } set { (realViewGroup as InnerHorizontalPages).JMBJ = value; (realViewGroup as InnerHorizontalPages).RefreshPageWidth(); } } /// /// 突出宽度 /// public int TCBJ { get { return (realViewGroup as InnerHorizontalPages).TCBJ; } set { (realViewGroup as InnerHorizontalPages).TCBJ = value; (realViewGroup as InnerHorizontalPages).RefreshPageWidth(); } } //********************Transform3D 旋转效果方法********************** ////默认20度 //float transformAngle = 20; ////旋转角度 //public int TransformAngle //{ // set // { // transformAngle = value; // } //} // //public void RefreshPageView() //{ // //RefreshViewWithTransform3D(); // //RefreshViewWithChangeHeight(); // (realViewGroup as InnerHorizontalPages).RefreshViewWithChangeHeight(); //} //public void RefreshViewWithTransform3D() //{ // if (viewList.Count <= 0) return; // if (PageIndex > viewList.Count - 1) // { // PageIndex = viewList.Count - 1; // } // viewList[PageIndex].AndroidView.RotationY = 0; // //判断PageIndex 前后是否有view,并旋转 // if (PageIndex - 1 >= 0) // { // viewList[PageIndex - 1].AndroidView.RotationY = transformAngle; // } // if (PageIndex <= viewList.Count - 2) // { // viewList[PageIndex + 1].AndroidView.RotationY = -1 * transformAngle; // } //} public bool isScrolling; /// /// 是否正在滑动 /// public bool ISScrolling { get { return isScrolling; } } /// /// 是否正在滑动 /// /// true if decelerating; otherwise, false. public bool Decelerating { get { return isScrolling; } } } 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 JMBJ = 0; //突出宽度 public int TCBJ = 100; Scroller scroller; VelocityTracker mVelocityTracker = VelocityTracker.Obtain(); int touchState = touchStateRest; float lastMotionX; float lastMotionY; int mTouchSlop; int mMaximumVelocity; internal int CurrentScreen; int mPageWidth; public void RefreshPageWidth() { mPageWidth = (int)LayoutParameters.Width - 2 * (TCBJ + JMBJ); } //public override void RemoveViewAt(int index) //{ // int nowScreen = CurrentScreen; // if (index == horizontalPages.ChildrenCount - 1) // { // nowScreen--; // if (nowScreen < 0) // { // nowScreen = 0; // } // } // base.RemoveViewAt(index); // ReLocation(); // snapToScreen(nowScreen, true); // //刷新 // //snapToDestination(); //} Shared.HorizontalPages horizontalPages; public InnerHorizontalPages(Context context, Shared.HorizontalPages view) : base(context) { horizontalPages = view; init(); } /// /// 重写这个是为了方便部局,在每个界面之间都多加了一个控件包住 /// /// public override void AddView(Android.Views.View child) { base.AddView(child); ReLocation(); } /// /// 重新排位置及设备内容大小 /// public virtual void ReLocation() { try { if (horizontalPages.ChildrenCount == 0) { return; } horizontalPages.GetChildren(0).AndroidView.SetX(TCBJ + JMBJ); for (int i = 1; i < horizontalPages.ChildrenCount; i++) { horizontalPages.GetChildren(i).AndroidView.SetX((int)horizontalPages.GetChildren(i - 1).AndroidView.GetX() + horizontalPages.GetChildren(i - 1).AndroidView.LayoutParameters.Width + JMBJ); } if (horizontalPages.IsSetLeftAndRightImageView) { var mHeight = LayoutParameters.Height - JMBJ * 2; //System.Console.WriteLine("ChildCountl:"+ChildCount.ToString() + " ChildrenCount:" + horizontalPages.ChildrenCount); var layoutParameters = GetChildAt(0).LayoutParameters; layoutParameters.Width = mPageWidth; layoutParameters.Height = mHeight; GetChildAt(0).LayoutParameters = layoutParameters; GetChildAt(0).SetX(TCBJ - mPageWidth); GetChildAt(0).SetY(JMBJ); var layoutParameters2 = GetChildAt(1).LayoutParameters; layoutParameters2.Width = mPageWidth; layoutParameters2.Height = mHeight; GetChildAt(1).LayoutParameters = layoutParameters2; GetChildAt(1).SetY(JMBJ); GetChildAt(1).SetX((int)horizontalPages.GetChildren(horizontalPages.ChildrenCount - 1).AndroidView.GetX() + mPageWidth + JMBJ); } RefreshPageView(); } catch { } } /// /// /// /// bool CheckIfStartOrEnd() { return (deltaX > 0 && CurrentScreen == horizontalPages.ChildrenCount - 1) || (deltaX < 0 && CurrentScreen == 0); } /// /// 判断是否需要发送结束滑动事件 /// void SendEndScrollAction() { if (isStart) { isStart = false; horizontalPages.EndScrollAction?.Invoke(); } } //bool isNoMore; bool isStart; int moveX; void init() { scroller = new Scroller(Context); var configuration = ViewConfiguration.Get(Context); //最小的滑动距离 mTouchSlop = configuration.ScaledTouchSlop; mMaximumVelocity = configuration.ScaledMaximumFlingVelocity; ScrollChange += (s, e) => { moveX = ScrollX - beforeXScrollX; RefreshPageView(); //Shared.HDLUtils.WriteLine($"HorizontalPages Scrolled ScrollX:{ScrollX} WIDTH:{Width}"); if (mTouchSlop < Math.Abs(moveX)) { if (!isStart) { //当前索引为,不是第一个或者最后一个,滑动距离大于触发距离并且没执行过则 执行一次StartScrollAction if ((moveX > 0 && CurrentScreen < horizontalPages.ChildrenCount - 1) || (moveX < 0 && CurrentScreen > 0)) { isStart = true; horizontalPages.StartScrollAction?.Invoke(); } } } }; } /// /// 自动计算刷新所有View的高度,实现缩放效果 /// public void RefreshPageView() { //RefreshViewWithTransform3D(); RefreshViewWithChangeHeight(); } /// /// 刷新最大距离值 /// double MaxDistance; //public void RefreshMaxDistance() //{ // MaxDistance = Width - 2 * TCBJ - JMBJ; //} /// /// 计算滑动缩放高度的方法 /// public void RefreshViewWithChangeHeight() { if (horizontalPages.ChildrenCount <= 0) return; MaxDistance = LayoutParameters.Width - 2 * TCBJ - JMBJ; //方法1 for (int i = 0; i < horizontalPages.viewList.Count; i++) { double distance = Math.Abs(horizontalPages.viewList[i].X - ScrollX - TCBJ - JMBJ); var HH = 0; if (distance <= 0) { HH = 0; } else if (distance >= MaxDistance) { HH = JMBJ; } else { HH = (int)((distance / MaxDistance) * JMBJ); } horizontalPages.viewList[i].Height = LayoutParameters.Height - HH * 2; horizontalPages.viewList[i].Y = HH; RefreshSubviewHeight(horizontalPages.viewList[i], horizontalPages.viewList[i].Height); } } /// /// 同时刷新子view 和ImageView的高度,暂时解决底部圆角失效问题 /// /// /// void RefreshSubviewHeight(View mView, float height) { try { if (mView is ViewGroup) { var tempViewGroup = (ViewGroup)mView; for (int i = 0; i < tempViewGroup.ChildrenCount; i++) { if (tempViewGroup.GetChildren(i) is ViewGroup) { var tempViewGroup2 = (ViewGroup)tempViewGroup.GetChildren(i); for (int j = 0; j < tempViewGroup2.ChildrenCount; j++) { if (tempViewGroup2.GetChildren(j) is ImageView && tempViewGroup2.GetChildren(j).Tag.ToString() == "R") { tempViewGroup2.GetChildren(j).Height = (int)height; break; } } if (tempViewGroup2.Tag.ToString() == "R") { tempViewGroup2.Height = (int)height; break; } } } } } catch { } } public override void RequestDisallowInterceptTouchEvent(bool disallowIntercept) { base.RequestDisallowInterceptTouchEvent(disallowIntercept); disallowInterceptTouchEvent = disallowIntercept; } bool disallowInterceptTouchEvent; public override bool OnInterceptTouchEvent(MotionEvent ev) { if (disallowInterceptTouchEvent) { return false; } //Shared.HDLUtils.WriteLine($"HorizontalPages->OnInterceptTouchEvent:{Height} {ev.Action}"); //如果没有子控件或者不允许滑动就返回,不处理当前事件 if (horizontalPages.ChildrenCount == 0 || !ScrollEnabled) return false; mVelocityTracker.AddMovement(ev); var x = ev.GetX(); var y = ev.GetY(); switch (ev.Action) { //每次点击都执行这里 case MotionEventActions.Down: beforeXScrollX = ScrollX; if (!scroller.IsFinished) { //如果还在滑动,但是用户重新点击了,就让控件马上停止 scroller.AbortAnimation(); } //记录点击的最新X坐标 lastMotionX = x; lastMotionY = y; touchState = scroller.IsFinished ? touchStateRest : touchStateScrolling; //IfAction = true; //isNoMore = false; //isStart = false; ////开始滑动事件 //horizontalPages.StartScrollAction?.Invoke() ; break; //有子控件时执行这里 case MotionEventActions.Move: var deltaX = (int)(lastMotionX - x); var deltaY = (int)(lastMotionY - y); //当前滑动是否已经超出了设定值 var xMoved = Math.Abs(deltaX) > mTouchSlop; if (xMoved && Math.Abs(deltaY) < Math.Abs(deltaX)) { touchState = touchStateScrolling; //isNoMore = false; } if (touchState == touchStateScrolling) { //记录最新的坐标,因为接下来控件已经滑动了 lastMotionX = x; //当前事件自己处理了,子控制不需要处理当前这事件 return true; } break; case MotionEventActions.Cancel: touchState = touchStateRest; mVelocityTracker.Clear(); break; } return false; } int deltaX; int beforeXScrollX; public override bool OnTouchEvent(MotionEvent e) { if (disallowInterceptTouchEvent) { return false; } if (horizontalPages.ChildrenCount == 0 || !ScrollEnabled) return false; mVelocityTracker.AddMovement(e); var x = e.GetX(); var y = e.GetY(); switch (e.Action) { case MotionEventActions.Move: SetIsScrolling(true); deltaX = (int)(lastMotionX - x); var deltaY = (int)(lastMotionY - y); //当前滑动是否已经超出了设定值 var xMoved = Math.Abs(deltaX) > mTouchSlop; if (xMoved && Math.Abs(deltaY) < Math.Abs(deltaX)) { touchState = touchStateScrolling; } if (touchState == touchStateScrolling) { lastMotionX = x; int scrollX = ScrollX; //向右滑动 if (deltaX < 0) { //之前已经滑动的X轴 if (scrollX > 0) { //向右滑动时显示出左边的界面,最低的长度为之前已经滑动的距离 ScrollBy(Math.Max(-scrollX, deltaX), 0); } } //向左滑动 else if (deltaX > 0) { //最小的右边需要滑动的距离 int availableToScroll = (int)horizontalPages.GetChildren( horizontalPages.ChildrenCount - 1).AndroidView.GetX() + horizontalPages.GetChildren( horizontalPages.ChildrenCount - 1).AndroidView.LayoutParameters.Width + TCBJ + JMBJ - PaddingRight - HorizontalFadingEdgeLength - scrollX - Width; if (availableToScroll > 0) { ScrollBy(Math.Min(availableToScroll, deltaX), 0); } } return true; } break; case MotionEventActions.Up: case MotionEventActions.Cancel: //IfAction = true; if (touchState == touchStateScrolling) { mVelocityTracker.ComputeCurrentVelocity(1000, mMaximumVelocity); var velocityX = mVelocityTracker.XVelocity; var moveX = ScrollX - beforeXScrollX; if (mTouchSlop < Math.Abs(moveX) || velocityX > snapVelocity) { if (moveX < 0) { // 显示左边界面 snapToScreen(CurrentScreen - 1); } else if (moveX > 0) { // 显示右边界面 snapToScreen(CurrentScreen + 1); } } else { snapToScreen(CurrentScreen); } } else { snapToScreen(CurrentScreen); } //if (Math.Abs(deltaX) > mTouchSlop) //{ // if (!isNoMore && !bSendPageChange) // { // //当前索引为第一个或者最后一个,滑动距离大于触发距离、没执行过、PageIndex没变化、则执行一次NoMoreAction // if ((deltaX > 0 && CurrentScreen == horizontalPages.ChildrenCount - 1 && ScrollX >= Width) || (deltaX < 0 && CurrentScreen == 0 && ScrollX <= 0)) // { // isNoMore = true; // horizontalPages.NoMoreAction?.Invoke(); // } // } //} touchState = touchStateRest; mVelocityTracker.Clear(); break; } return true; } /// /// 滑动到指定的界面 /// public 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, horizontalPages.ChildrenCount - 1)); CurrentScreen = whichScreen; //还原这个控件之前的位置 int newX = 0; if (CurrentScreen != 0) { newX = (int)horizontalPages.GetChildren(CurrentScreen).AndroidView.GetX() - TCBJ - JMBJ; } SetIsScrolling(true); var delta = newX - ScrollX; scroller.StartScroll(ScrollX, 0, delta, 0, isDelay ? 400 : 0); Invalidate(); SendIfPageChange(isDelay ? 400 : 0); //Shared.HDLUtils.WriteLine("HorizontalPages StartScroll"); if (beforePosition != CurrentScreen) { RefreshPageView(); bSendPageChange = true; //DelayPageChange(); //horizontalPages.PageChange?.Invoke(horizontalPages, CurrentScreen); } } private void SetIsScrolling(bool bSctolling) { if (horizontalPages.isScrolling == bSctolling) return; //Shared.HDLUtils.WriteLine($"HorizontalPages StartScroll: {bSctolling}"); horizontalPages.isScrolling = bSctolling; } System.Threading.Thread sendThread; //string SendmarkStr; bool bSendPageChange = false; /// /// 判断是否要发送改变事件 /// private void SendIfPageChange(int DELAY = 400) { try { if (sendThread != null) sendThread.Abort(); sendThread = new System.Threading.Thread(async () => { System.Threading.Thread.Sleep(DELAY + 100); Application.RunOnMainThread(() => { SetIsScrolling(false); if (bSendPageChange) { bSendPageChange = false; horizontalPages.PageChange?.Invoke(horizontalPages, CurrentScreen); } SendEndScrollAction(); HDLUtils.WriteLine($"HorizontalPages SendIfPageChange______________-----------"); }); }) { IsBackground = true }; sendThread.Start(); } catch { } } //public void DelayPageChange() //{ // new System.Threading.Thread(async () => // { // System.Threading.Thread.Sleep(500); // Application.RunOnMainThread(() => // { // horizontalPages.PageChange?.Invoke(horizontalPages, CurrentScreen); // }); // }) // { IsBackground = true }.Start(); //} public override void ComputeScroll() { if (scroller.ComputeScrollOffset()) { ScrollTo(scroller.CurrX, scroller.CurrY); PostInvalidate(); } else if (CurrentScreen != invalidScreen) { //mCurrentScreen = Math.Max(0, Math.Min(mNextScreen, horizontalPages.ChildrenCount - 1)); //mNextScreen = invalidScreen; //postViewSwitched(mLastScrollDirection); } } public void SetSelection(int position) { if (position < 0) { return; } scroller.ForceFinished(true); position = Math.Min(position, horizontalPages.ChildrenCount - 1); if(position != CurrentScreen) { isStart = true; horizontalPages.StartScrollAction?.Invoke(); } snapToScreen(position, false); } public override bool DispatchTouchEvent(MotionEvent e) { //Shared.HDLUtils.WriteLine($"PageLayout->DispatchTouchEvent:{Height} {e.Action}"); if (e.Action == MotionEventActions.Down) { //还原中断 RequestDisallowInterceptTouchEvent(false); Parent.RequestDisallowInterceptTouchEvent(true); } else if (e.Action == MotionEventActions.Up || e.Action == MotionEventActions.Cancel) { Parent.RequestDisallowInterceptTouchEvent(false); } return base.DispatchTouchEvent(e); } } }