Xamarin Android: how to create a cancel panel

I want to know how to create a cancel panel for Xamarin Android something like this.

enter image description here

I used this code created by Dan Ardelean for Xamarin Android, which bases the code example Roman Nurik for android , but this does not work for me. I get this message:

Unable to activate instance of type SwipeDismissListView.MainActivity from native handle 0x200019 (key_handle 0xf9f4bcb). --->

System.MissingMethodException: No constructor found for SwipeDismissListView.MainActivity::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership) ---> Java.Interop.JavaLocationException: Exception of type 'Java.Interop.JavaLocationException' was thrown. --- End of inner exception stack trace --- at Java.Interop.TypeManager.CreateProxy (System.Type type, System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00054] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType) [0x00111] in <d855bac285f44dda8a0d8510b679b1e2>:0 --- End of inner exception stack trace --- at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType) [0x0017d] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object.GetObject (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type type) [0x000b9] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object._GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00017] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object.GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00000] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Android.Views.View.get_Context () [0x0001f] in <d855bac285f44dda8a0d8510b679b1e2>:0 at XpressCode.SwipeDismissListViewTouchListener..ctor (Android.Widget.ListView listView, XpressCode.IDismissCallbacks callbacks) [0x00021] in D:\...\Xamarin-SwipeToDismiss-master\SwipeDismissListView\SwipeDismissListViewTouchListener.cs:58 at SwipeDismissListView.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x00086]

I searched the Internet for sample code, but I did not find anything like it.

I tried to add a constructor comment block,

 public MainActivity() { } 

but I get the error:

 {System.InvalidCastException: Specified cast is not valid. at Android.Widget.AbsListView.SetOnScrollListener (Android.Widget.AbsListView+IOnScrollListener l) [0x0000c] in <d855bac285f44dda8a0d8510b679b1e2>:0 at SwipeDismissListView.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x000a0] in D:\...\Xamarin-SwipeToDismiss-master\SwipeDismissListView\MainActivity.cs:45 } 

Then I deleted the created constructor and added a try ... cacth block. The application is broken into this line:

 list.SetOnScrollListener(touchListener.MakeScrollListener()); 

THIS COMMUNICATION CODE:

MainActivity.cs

 using System; using Android.App; using Android.Content; using Android.Runtime; using Android.Views; using Android.Widget; using Android.OS; using System.Collections.Generic; using XpressCode; namespace SwipeDismissListView { [Activity (Label = "SwipeDismissListView", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity,IDismissCallbacks,IUndoListener { UndoBarController mUndoBarController; ArrayAdapter<String> mAdapter; ListView list; protected override void OnCreate (Bundle bundle) { try { base.OnCreate(bundle); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); String[] items = new String[20]; for (int i = 0; i < items.Length; i++) { items[i] = "Item " + (i + 1); } list = FindViewById<ListView>(Resource.Id.lstItems); mAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItem1, Android.Resource.Id.Text1, new List<String>(items)); list.Adapter = mAdapter; SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(list, this); list.SetOnTouchListener(touchListener); list.SetOnScrollListener(touchListener.MakeScrollListener()); mUndoBarController = new UndoBarController(FindViewById(Resource.Id.undobar), this); } catch (Exception ex) { Toast.MakeText(this,"error: " + ex,ToastLength.Short); } } protected override void OnSaveInstanceState (Bundle outState) { base.OnSaveInstanceState (outState); mUndoBarController.onSaveInstanceState(outState); } protected override void OnRestoreInstanceState (Bundle savedInstanceState) { base.OnRestoreInstanceState (savedInstanceState); mUndoBarController.onRestoreInstanceState(savedInstanceState); } public bool canDismiss (int position) { return true; } int _position; public void onDismiss (ListView listView, int[] reverseSortedPositions) { foreach (int position in reverseSortedPositions) { var item = mAdapter.GetItem (position); mAdapter.Remove(item); _position = position; mUndoBarController.ShowUndoBar(false,GetString(Resource.String.undobar_sample_message),item); } mAdapter.NotifyDataSetChanged(); } public void OnUndo(object token) { mAdapter.Insert ((string)token, _position); // Perform the undo } } } 

SwipeDismissListViewTouchListener.cs

 using System; using Android.Views; using Android.Widget; using System.Collections.Generic; using Android.Graphics; using Android.Animation; using Java.Util; using Android.OS; namespace XpressCode { public interface IDismissCallbacks { /** * Called to determine whether the given position can be dismissed. */ bool canDismiss (int position); /** * Called when the user has indicated they she would like to dismiss one or more list item * positions. * * @param listView The originating {@link ListView}. * @param reverseSortedPositions An array of positions to dismiss, sorted in descending * order for convenience. */ void onDismiss (ListView listView, int[] reverseSortedPositions); } public class SwipeDismissListViewTouchListener:Java.Lang.Object,Android.Views.View.IOnTouchListener { // Cached ViewConfiguration and system-wide constant values int mSlop; int mMinFlingVelocity; int mMaxFlingVelocity; long mAnimationTime; // Fixed properties internal ListView mListView; internal IDismissCallbacks mCallbacks; internal int mViewWidth = 1; // Transient properties internal List<PendingDismissData> mPendingDismisses = new List<PendingDismissData> (); internal int mDismissAnimationRefCount = 0; internal float mDownX; internal float mDownY; internal bool mSwiping; internal int mSwipingSlop; internal VelocityTracker mVelocityTracker; internal int mDownPosition; internal View mDownView; internal bool mPaused; public SwipeDismissListViewTouchListener (ListView listView, IDismissCallbacks callbacks) { ViewConfiguration vc = ViewConfiguration.Get (listView.Context); mSlop = vc.ScaledTouchSlop; mMinFlingVelocity = vc.ScaledMinimumFlingVelocity * 16; mMaxFlingVelocity = vc.ScaledMaximumFlingVelocity; mAnimationTime = listView.Context.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime); mListView = listView; mCallbacks = callbacks; } /** * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures. * * @param enabled Whether or not to watch for gestures. */ public void SetEnabled (bool enabled) { mPaused = !enabled; } /** * Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the {@link * ListView} using {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}. * If a scroll listener is already assigned, the caller should still pass scroll changes through * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is * paused during list view scrolling.</p> * * @see SwipeDismissListViewTouchListener */ public AbsListView.IOnScrollListener MakeScrollListener() { return new OnScrollListener (this); } class OnScrollListener:AbsListView.IOnScrollListener { SwipeDismissListViewTouchListener _listener; public OnScrollListener(SwipeDismissListViewTouchListener listener) { _listener = listener; } public void OnScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } public void OnScrollStateChanged (AbsListView view, ScrollState scrollState) { _listener.SetEnabled(scrollState != ScrollState.TouchScroll); } public void Dispose () { throw new NotImplementedException (); } public IntPtr Handle { get { return new IntPtr (); } } } public bool OnTouch(View view, MotionEvent motionEvent) { if (mViewWidth < 2) { mViewWidth = mListView.Width; } switch (motionEvent.ActionMasked) { case MotionEventActions.Down: if (mPaused) { return false; } // TODO: ensure this is a finger, and set a flag // Find the child view that was touched (perform a hit test) Rect rect = new Rect (); int childCount = mListView.ChildCount; int[] listViewCoords = new int[2]; mListView.GetLocationOnScreen (listViewCoords); int x = (int)motionEvent.RawX - listViewCoords [0]; int y = (int)motionEvent.RawY - listViewCoords [1]; View child; for (int i = 0; i < childCount; i++) { child = mListView.GetChildAt (i); child.GetHitRect (rect); if (rect.Contains (x, y)) { mDownView = child; break; } } if (mDownView != null) { mDownX = motionEvent.RawX; mDownY = motionEvent.RawY; mDownPosition = mListView.GetPositionForView (mDownView); if (mCallbacks.canDismiss (mDownPosition)) { mVelocityTracker = VelocityTracker.Obtain (); mVelocityTracker.AddMovement (motionEvent); } else { mDownView = null; } } return false; case MotionEventActions.Cancel: { if (mVelocityTracker == null) { break; } if (mDownView != null && mSwiping) { // cancel mDownView.Animate() .TranslationX(0) .Alpha(1) .SetDuration(mAnimationTime) .SetListener(null); } mVelocityTracker.Recycle(); mVelocityTracker = null; mDownX = 0; mDownY = 0; mDownView = null; mDownPosition = ListView.InvalidPosition; mSwiping = false; break; } case MotionEventActions.Up: { if (mVelocityTracker == null) { break; } float deltaX = motionEvent.RawX - mDownX; mVelocityTracker.AddMovement(motionEvent); mVelocityTracker.ComputeCurrentVelocity(1000); float velocityX = mVelocityTracker.XVelocity; float absVelocityX = Math.Abs(velocityX); float absVelocityY = Math.Abs(mVelocityTracker.YVelocity); bool dismiss = false; bool dismissRight = false; if (Math.Abs(deltaX) > mViewWidth / 2) { dismiss = true; dismissRight = deltaX > 0; } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity&& absVelocityY < absVelocityX&&mSwiping) { // dismiss only if flinging in the same direction as dragging dismiss = (velocityX < 0) == (deltaX < 0); dismissRight = mVelocityTracker.XVelocity > 0; } if (dismiss&& mDownPosition != ListView.InvalidPosition) { // dismiss View downView = mDownView; // mDownView gets null'd before animation ends int downPosition = mDownPosition; ++mDismissAnimationRefCount; var anim = mDownView.Animate () .TranslationX (dismissRight ? mViewWidth : -mViewWidth) .Alpha (0) .SetDuration (mAnimationTime) .SetListener (new DownAnimatorListenerAdapter(this,downView,downPosition)); } else { // cancel mDownView.Animate() .TranslationX(0) .Alpha(1) .SetDuration(mAnimationTime) .SetListener(null); } mVelocityTracker.Recycle(); mVelocityTracker = null; mDownX = 0; mDownY = 0; mDownView = null; mDownPosition = ListView.InvalidPosition; mSwiping = false; break; } case MotionEventActions.Move: { if (mVelocityTracker == null || mPaused) { break; } mVelocityTracker.AddMovement(motionEvent); float deltaX = motionEvent.RawX - mDownX; float deltaY = motionEvent.RawY - mDownY; if (Math.Abs(deltaX) > mSlop && Math.Abs(deltaY) < Math.Abs(deltaX) / 2) { mSwiping = true; mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); mListView.RequestDisallowInterceptTouchEvent(true); // Cancel ListView touch (un-highlighting the item) MotionEvent cancelEvent = MotionEvent.Obtain(motionEvent); cancelEvent.Action= (Android.Views.MotionEventActions) ((int) MotionEventActions.Cancel | ((int)motionEvent.ActionIndex <<(int) MotionEventActions.PointerIndexShift)); mListView.OnTouchEvent(cancelEvent); cancelEvent.Recycle(); } if (mSwiping) { mDownView.TranslationX=deltaX - mSwipingSlop; mDownView.Alpha=Math.Max(0f, Math.Min(1f,1f - 2f * Math.Abs(deltaX) / mViewWidth)); return true; } break; } } return false; } /** * Manually cause the item at the given position to be dismissed (trigger the dismiss * animation). */ public void PerformDismiss(View dismissView, int dismissPosition) { // Animate the dismissed list item to zero-height and fire the dismiss callback when // all dismissed list item animations have completed. This triggers layout on each animation // frame; in the future we may want to do something smarter and more performant. ViewGroup.LayoutParams lp = dismissView.LayoutParameters; int originalHeight = dismissView.Height; ValueAnimator animator = ValueAnimator.OfInt (originalHeight, 1); animator.SetDuration(mAnimationTime); animator.AddListener (new DismissAnimatorListenerAdapter (this,originalHeight)); animator.AddUpdateListener(new AnimatorUpdateListener(dismissView,lp)); mPendingDismisses.Add(new PendingDismissData(dismissPosition, dismissView)); animator.Start(); } public void DismissAnimationEnded(int originalHeight) { --mDismissAnimationRefCount; if (mDismissAnimationRefCount == 0) { // No active animations, process all pending dismisses. // Sort by descending position mPendingDismisses.Sort (); //Collections.Sort(mPendingDismisses); int[] dismissPositions = new int[mPendingDismisses.Count]; for (int i = mPendingDismisses.Count - 1; i >= 0; i--) { dismissPositions[i] = mPendingDismisses[i].position; } mCallbacks.onDismiss(mListView, dismissPositions); // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss // animation with a stale position mDownPosition = ListView.InvalidPosition; foreach (PendingDismissData pendingDismiss in mPendingDismisses) { // Reset view presentation pendingDismiss.view.Alpha=1f; pendingDismiss.view.TranslationX=0; var cc = pendingDismiss.view.LayoutParameters; cc.Height = originalHeight; pendingDismiss.view.LayoutParameters=cc; } // Send a cancel event long time = SystemClock.UptimeMillis(); MotionEvent cancelEvent = MotionEvent.Obtain(time, time,MotionEventActions.Cancel, 0, 0, 0); mListView.DispatchTouchEvent(cancelEvent); mPendingDismisses.Clear(); } } } class AnimatorUpdateListener : Java.Lang.Object, ValueAnimator.IAnimatorUpdateListener { View dismissView; ViewGroup.LayoutParams lp; public AnimatorUpdateListener (View dismissView, ViewGroup.LayoutParams lp) { this.dismissView = dismissView; this.lp = lp; } public void OnAnimationUpdate (ValueAnimator valueAnimator) { lp.Height = (int) valueAnimator.AnimatedValue; dismissView.LayoutParameters=lp; } } class DownAnimatorListenerAdapter:AnimatorListenerAdapter { View _downView; // mDownView gets null'd before animation ends int _downPosition ; SwipeDismissListViewTouchListener _parent; public DownAnimatorListenerAdapter(SwipeDismissListViewTouchListener parent,View downView,int downPosition) { _downView = downView; _parent = parent; _downPosition = downPosition; } public override void OnAnimationEnd (Animator animation) { base.OnAnimationEnd (animation); _parent.PerformDismiss (_downView, _downPosition); } } class DismissAnimatorListenerAdapter:AnimatorListenerAdapter { SwipeDismissListViewTouchListener _parent; int _originalHeight; public DismissAnimatorListenerAdapter(SwipeDismissListViewTouchListener parent,int originalHeight) { _parent = parent; _originalHeight = originalHeight; } public override void OnAnimationEnd (Animator animation) { base.OnAnimationEnd (animation); _parent.DismissAnimationEnded (_originalHeight); } } class PendingDismissData:IComparable<PendingDismissData> { public int position; public View view; public PendingDismissData(int position, View view) { this.position = position; this.view = view; } public int CompareTo(PendingDismissData other) { // Sort by descending position return other.position - position; } } } 

UndoBarController.cs

 using System; using Android.Views; using Android.Views; using Android.OS; using Android.Widget; using Android.Text; using Android.Runtime; using Android.Animation; using Java.Lang; using Newtonsoft.Json; using System.Collections.Generic; using SwipeDismissListView; namespace XpressCode { public interface IUndoListener { void OnUndo (object obj); } public class UndoBarController { View mBarView; TextView mMessageView; ViewPropertyAnimator mBarAnimator; Handler mHideHandler = new Handler (); internal IUndoListener mUndoListener; // State objects internal object mUndoToken; string mUndoMessage; public UndoBarController (View undoBarView, IUndoListener undoListener) { mBarView = undoBarView; mBarAnimator = mBarView.Animate (); mUndoListener = undoListener; mMessageView = (TextView)mBarView.FindViewById (Resource.Id.undobar_message); mBarView.FindViewById<Button> (Resource.Id.undobar_button).Click += (object sender, EventArgs e) => { HideUndoBar (false); mUndoListener.OnUndo (mUndoToken); }; HideUndoBar (true); } public void ShowUndoBar (bool immediate, string message, object undoToken) { mUndoToken = undoToken; mUndoMessage = message; mMessageView.Text = mUndoMessage; mBarView.Visibility = ViewStates.Visible; if (immediate) { mBarView.Alpha = 1; } else { mBarAnimator.Cancel (); mBarAnimator .Alpha (1) .SetDuration (mBarView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime)) .SetListener (null); } t = new System.Timers.Timer (mBarView.Resources.GetInteger (Resource.Integer.undobar_hide_delay)); t.Elapsed += HandleTimeoutElapsed; t.Start (); } void HandleTimeoutElapsed (object sender, System.Timers.ElapsedEventArgs e) { DisposeTimer (); HideUndoBar (false); } public void DisposeTimer() { lock(this) { if (t==null) return; t.Stop (); t.Elapsed -= HandleTimeoutElapsed; t.Dispose (); t = null; } } System.Timers.Timer t; public void HideUndoBar (bool immediate) { DisposeTimer (); if (immediate) { mBarView.Visibility = ViewStates.Gone; mBarView.Alpha = 0; mUndoMessage = null; mUndoToken = null; } else { mBarAnimator.Cancel (); var animator = mBarAnimator .Alpha (0) .SetDuration (mBarView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime)) .SetListener (new CloseAnimatorListenerAdapter (this)); } } public void ClosePopup () { mBarView.Visibility = ViewStates.Gone; mUndoMessage = null; mUndoToken = null; } public void onSaveInstanceState (Bundle outState) { outState.PutCharSequence ("undo_message", mUndoMessage); outState.PutString ("undo_token", JsonConvert.SerializeObject (mUndoToken)); } public void onRestoreInstanceState (Bundle savedInstanceState) { if (savedInstanceState != null) { mUndoMessage = savedInstanceState.GetCharSequence ("undo_message"); mUndoToken = JsonConvert.DeserializeObject<object> (savedInstanceState.GetString ("undo_token")); if (mUndoToken != null || !TextUtils.IsEmpty (mUndoMessage)) { ShowUndoBar (true, mUndoMessage, mUndoToken); } } } } class CloseAnimatorListenerAdapter:AnimatorListenerAdapter { UndoBarController controller; public CloseAnimatorListenerAdapter (UndoBarController controller) { this.controller = controller; } public override void OnAnimationEnd (Animator animation) { controller.HideUndoBar (false); } } } 

Main.axml

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:listSeparatorTextViewStyle" android:text="@string/heading_dismissable_list_view" /> <ListView android:id="@+id/lstItems" android:layout_weight="1" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout> <LinearLayout android:id="@+id/undobar" style="@style/UndoBar"> <TextView android:id="@+id/undobar_message" style="@style/UndoBarMessage" /> <Button android:id="@+id/undobar_button" style="@style/UndoBarButton" /> </LinearLayout> </FrameLayout> 

What am I doing wrong? Thank you for your help!

+5
source share

Source: https://habr.com/ru/post/1270085/


All Articles